From 8a787bf19bcff38501dddeef95ad9f8683b5976c Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Tue, 29 Oct 2024 13:01:09 +0100 Subject: [PATCH 01/34] IMN-810 - Aligning local dev AWS region with dev env (eu-south-1) (#1128) --- collections/bff/catalog/Import Eservice.bru | 2 +- collections/bff/catalog/Import Eservices.bru | 2 +- docker/docker-compose.yml | 4 ++-- packages/agreement-email-sender/.env | 2 +- packages/agreement-platformstate-writer/.env | 2 +- packages/agreement-process/aws.config.local | 2 +- packages/agreement-readmodel-writer/.env | 2 +- .../anac-certified-attributes-importer/aws.config.local | 2 +- packages/attribute-registry-readmodel-writer/.env | 2 +- packages/authorization-updater/.env | 2 +- packages/authorization-updater/aws.config.local | 2 +- packages/backend-for-frontend/aws.config.local | 2 +- packages/catalog-platformstate-writer/test/utils.test.ts | 2 +- packages/catalog-process/aws.config.local | 2 +- packages/catalog-readmodel-writer/.env | 2 +- packages/client-readmodel-writer/.env | 2 +- packages/commons-test/aws.config.local | 2 +- packages/commons-test/src/containerTestUtils.ts | 2 +- packages/commons-test/test/SES.test.ts | 2 +- packages/compute-agreements-consumer/.env | 2 +- packages/compute-agreements-consumer/aws.config.local | 2 +- packages/datalake-data-export/aws.config.local | 2 +- packages/dtd-catalog-exporter/.env | 2 +- packages/dtd-catalog-exporter/aws.config.local | 2 +- packages/eservice-descriptors-archiver/.env | 2 +- packages/eservice-descriptors-archiver/aws.config.local | 2 +- .../ivass-certified-attributes-importer/aws.config.local | 2 +- packages/kafka-iam-auth/README.md | 7 ++++--- packages/key-readmodel-writer/.env | 2 +- packages/notifier-seeder/.env | 2 +- packages/notifier-seeder/aws.config.local | 2 +- packages/one-trust-notices/aws.config.local | 2 +- packages/producer-key-events-writer/.env | 2 +- packages/producer-key-readmodel-writer/.env | 2 +- packages/producer-keychain-readmodel-writer/.env | 2 +- packages/purpose-process/aws.config.local | 2 +- packages/purpose-readmodel-writer/.env | 2 +- packages/selfcare-onboarding-consumer/.env | 2 +- packages/selfcare-onboarding-consumer/aws.config.local | 2 +- packages/tenant-readmodel-writer/.env | 2 +- 40 files changed, 44 insertions(+), 43 deletions(-) diff --git a/collections/bff/catalog/Import Eservice.bru b/collections/bff/catalog/Import Eservice.bru index 04d22b7d55..03b143fa97 100644 --- a/collections/bff/catalog/Import Eservice.bru +++ b/collections/bff/catalog/Import Eservice.bru @@ -21,6 +21,6 @@ headers { body:json { { "filename": "054d02f9-7beb-4443-994a-062c4157672e_1635b970-eb99-4c12-8a51-aac884dfd648.zip", - "url": "http://localhost:9000/interop-application-import-export-local/local/eservices-import/69e2865e-65ab-4e48-a638-2037a9ee2ee7/054d02f9-7beb-4443-994a-062c4157672e_1635b970-eb99-4c12-8a51-aac884dfd648.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=testawskey%2F20241001%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20241001T132035Z&X-Amz-Expires=5000&X-Amz-Signature=b24b9928969cd58351e7c5d6c25e509c7b6f1436fc5835ef54c0331972b94c40&X-Amz-SignedHeaders=host&x-id=PutObject" + "url": "http://localhost:9000/interop-application-import-export-local/local/eservices-import/69e2865e-65ab-4e48-a638-2037a9ee2ee7/054d02f9-7beb-4443-994a-062c4157672e_1635b970-eb99-4c12-8a51-aac884dfd648.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=testawskey%2F20241001%2Feu-south-1%2Fs3%2Faws4_request&X-Amz-Date=20241001T132035Z&X-Amz-Expires=5000&X-Amz-Signature=b24b9928969cd58351e7c5d6c25e509c7b6f1436fc5835ef54c0331972b94c40&X-Amz-SignedHeaders=host&x-id=PutObject" } } diff --git a/collections/bff/catalog/Import Eservices.bru b/collections/bff/catalog/Import Eservices.bru index c113d42f95..a455a08d6a 100644 --- a/collections/bff/catalog/Import Eservices.bru +++ b/collections/bff/catalog/Import Eservices.bru @@ -18,5 +18,5 @@ headers { body:multipart-form { filename: f07b1c49-38fc-445e-a103-c2c156ae85ca_3bb5154d-eebc-42fd-9ea9-32b5be48e434.zip - url: http://localhost:9000/interop-application-import-export-local/local/eservices-import/69e2865e-65ab-4e48-a638-2037a9ee2ee7/f07b1c49-38fc-445e-a103-c2c156ae85ca_3bb5154d-eebc-42fd-9ea9-32b5be48e434.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=testawskey%2F20241001%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20241001T140847Z&X-Amz-Expires=5000&X-Amz-Signature=d57f527006fce0a819c381821c688aa9d211a38f250f8bc450ff661ca06923d2&X-Amz-SignedHeaders=host&x-id=PutObject + url: http://localhost:9000/interop-application-import-export-local/local/eservices-import/69e2865e-65ab-4e48-a638-2037a9ee2ee7/f07b1c49-38fc-445e-a103-c2c156ae85ca_3bb5154d-eebc-42fd-9ea9-32b5be48e434.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=testawskey%2F20241001%2Feu-south-1%2Fs3%2Faws4_request&X-Amz-Date=20241001T140847Z&X-Amz-Expires=5000&X-Amz-Signature=d57f527006fce0a819c381821c688aa9d211a38f250f8bc450ff661ca06923d2&X-Amz-SignedHeaders=host&x-id=PutObject } diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 2e73299cec..8a84f69746 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -163,7 +163,7 @@ services: environment: MINIO_ROOT_USER: testawskey # use this as AWS S3 access key ID MINIO_ROOT_PASSWORD: testawssecret # use this as AWS S3 secret access key - MINIO_SITE_REGION: eu-central-1 + MINIO_SITE_REGION: eu-south-1 volumes: - ./minio-data:/data restart: always @@ -208,7 +208,7 @@ services: - 4566:4566 environment: PORT: 4566 - KMS_REGION: eu-central-1 + KMS_REGION: eu-south-1 volumes: - ./local-kms-seed/seed.yaml:/init/seed.yaml diff --git a/packages/agreement-email-sender/.env b/packages/agreement-email-sender/.env index 73d796d577..cc67c376d8 100644 --- a/packages/agreement-email-sender/.env +++ b/packages/agreement-email-sender/.env @@ -5,7 +5,7 @@ KAFKA_GROUP_ID="agreement-email-sender--group-local" KAFKA_BROKERS="localhost:9092" KAFKA_DISABLE_AWS_IAM_AUTH=true AGREEMENT_TOPIC="event-store.agreement.events" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" READMODEL_DB_HOST="localhost" READMODEL_DB_NAME="readmodel" diff --git a/packages/agreement-platformstate-writer/.env b/packages/agreement-platformstate-writer/.env index 4d789cf37c..e585e9bbe0 100644 --- a/packages/agreement-platformstate-writer/.env +++ b/packages/agreement-platformstate-writer/.env @@ -9,4 +9,4 @@ AWS_CONFIG_FILE=aws.config.local TOKEN_GENERATION_READMODEL_TABLE_NAME_PLATFORM="platform-states" TOKEN_GENERATION_READMODEL_TABLE_NAME_TOKEN_GENERATION="token-generation-states" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/agreement-process/aws.config.local b/packages/agreement-process/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/agreement-process/aws.config.local +++ b/packages/agreement-process/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/agreement-readmodel-writer/.env b/packages/agreement-readmodel-writer/.env index 59843f8ed3..01f629a874 100644 --- a/packages/agreement-readmodel-writer/.env +++ b/packages/agreement-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/anac-certified-attributes-importer/aws.config.local b/packages/anac-certified-attributes-importer/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/anac-certified-attributes-importer/aws.config.local +++ b/packages/anac-certified-attributes-importer/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/attribute-registry-readmodel-writer/.env b/packages/attribute-registry-readmodel-writer/.env index fd326b0244..d1eee2fad3 100644 --- a/packages/attribute-registry-readmodel-writer/.env +++ b/packages/attribute-registry-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/authorization-updater/.env b/packages/authorization-updater/.env index 9c1b2b0940..0b401234c2 100644 --- a/packages/authorization-updater/.env +++ b/packages/authorization-updater/.env @@ -8,7 +8,7 @@ CATALOG_TOPIC="event-store.catalog.events" AGREEMENT_TOPIC="event-store.agreement.events" PURPOSE_TOPIC="event-store.purpose.events" AUTHORIZATION_TOPIC="event-store.authorization.events" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" READMODEL_DB_HOST="localhost" READMODEL_DB_NAME="readmodel" diff --git a/packages/authorization-updater/aws.config.local b/packages/authorization-updater/aws.config.local index 9c57dc948e..8dfc0e7f55 100644 --- a/packages/authorization-updater/aws.config.local +++ b/packages/authorization-updater/aws.config.local @@ -1,7 +1,7 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 services=local [services local] diff --git a/packages/backend-for-frontend/aws.config.local b/packages/backend-for-frontend/aws.config.local index 4a2fef9936..5d3103957c 100644 --- a/packages/backend-for-frontend/aws.config.local +++ b/packages/backend-for-frontend/aws.config.local @@ -1,7 +1,7 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 services=local [services local] diff --git a/packages/catalog-platformstate-writer/test/utils.test.ts b/packages/catalog-platformstate-writer/test/utils.test.ts index 46444e0dc0..ed13803846 100644 --- a/packages/catalog-platformstate-writer/test/utils.test.ts +++ b/packages/catalog-platformstate-writer/test/utils.test.ts @@ -49,7 +49,7 @@ describe("utils tests", async () => { } const dynamoDBClient = new DynamoDBClient({ credentials: { accessKeyId: "key", secretAccessKey: "secret" }, - region: "eu-central-1", + region: "eu-south-1", endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, }); beforeEach(async () => { diff --git a/packages/catalog-process/aws.config.local b/packages/catalog-process/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/catalog-process/aws.config.local +++ b/packages/catalog-process/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/catalog-readmodel-writer/.env b/packages/catalog-readmodel-writer/.env index 837d78ede5..06d51acd5b 100644 --- a/packages/catalog-readmodel-writer/.env +++ b/packages/catalog-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/client-readmodel-writer/.env b/packages/client-readmodel-writer/.env index 06c31f2515..5423be0ba1 100644 --- a/packages/client-readmodel-writer/.env +++ b/packages/client-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/commons-test/aws.config.local b/packages/commons-test/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/commons-test/aws.config.local +++ b/packages/commons-test/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/commons-test/src/containerTestUtils.ts b/packages/commons-test/src/containerTestUtils.ts index d74b08d248..3f76738c80 100644 --- a/packages/commons-test/src/containerTestUtils.ts +++ b/packages/commons-test/src/containerTestUtils.ts @@ -85,7 +85,7 @@ export const minioContainer = (config: S3Config): GenericContainer => .withEnvironment({ MINIO_ROOT_USER: "testawskey", MINIO_ROOT_PASSWORD: "testawssecret", - MINIO_SITE_REGION: "eu-central-1", + MINIO_SITE_REGION: "eu-south-1", }) .withEntrypoint(["sh", "-c"]) .withCommand([ diff --git a/packages/commons-test/test/SES.test.ts b/packages/commons-test/test/SES.test.ts index 72a1a2a0e4..608e1acc1a 100644 --- a/packages/commons-test/test/SES.test.ts +++ b/packages/commons-test/test/SES.test.ts @@ -15,7 +15,7 @@ describe("initSesMailManager", () => { sesMock.on(SendEmailCommand).resolves({}); const awsSesConfig: AWSSesConfig = { - awsRegion: "eu-central-1", + awsRegion: "eu-south-1", awsSesEndpoint: undefined, }; const emailManager = initSesMailManager(awsSesConfig); diff --git a/packages/compute-agreements-consumer/.env b/packages/compute-agreements-consumer/.env index 785ea53132..9fcc7fca87 100644 --- a/packages/compute-agreements-consumer/.env +++ b/packages/compute-agreements-consumer/.env @@ -5,7 +5,7 @@ KAFKA_GROUP_ID="compute-agreements-consumer-group-local" KAFKA_BROKERS="localhost:9092" KAFKA_DISABLE_AWS_IAM_AUTH="true" TENANT_TOPIC="event-store.tenant.events" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" AGREEMENT_PROCESS_URL="http://0.0.0.0:3000" diff --git a/packages/compute-agreements-consumer/aws.config.local b/packages/compute-agreements-consumer/aws.config.local index 9c57dc948e..8dfc0e7f55 100644 --- a/packages/compute-agreements-consumer/aws.config.local +++ b/packages/compute-agreements-consumer/aws.config.local @@ -1,7 +1,7 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 services=local [services local] diff --git a/packages/datalake-data-export/aws.config.local b/packages/datalake-data-export/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/datalake-data-export/aws.config.local +++ b/packages/datalake-data-export/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/dtd-catalog-exporter/.env b/packages/dtd-catalog-exporter/.env index d2a817544f..6eccaff37c 100644 --- a/packages/dtd-catalog-exporter/.env +++ b/packages/dtd-catalog-exporter/.env @@ -15,5 +15,5 @@ DTD_CATALOG_STORAGE_PATH="dtd-catalog" DTD_CATALOG_FILENAME="dtd-catalog.json" AWS_CONFIG_FILE=aws.config.local -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/dtd-catalog-exporter/aws.config.local b/packages/dtd-catalog-exporter/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/dtd-catalog-exporter/aws.config.local +++ b/packages/dtd-catalog-exporter/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/eservice-descriptors-archiver/.env b/packages/eservice-descriptors-archiver/.env index 39f9cd10f5..97ebfdbd82 100644 --- a/packages/eservice-descriptors-archiver/.env +++ b/packages/eservice-descriptors-archiver/.env @@ -5,7 +5,7 @@ KAFKA_GROUP_ID="eservice-descriptors-archiver-group-local" KAFKA_BROKERS="localhost:9092" KAFKA_DISABLE_AWS_IAM_AUTH="true" AGREEMENT_TOPIC="event-store.agreement.events" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" READMODEL_DB_HOST="localhost" READMODEL_DB_NAME="readmodel" diff --git a/packages/eservice-descriptors-archiver/aws.config.local b/packages/eservice-descriptors-archiver/aws.config.local index 9c57dc948e..8dfc0e7f55 100644 --- a/packages/eservice-descriptors-archiver/aws.config.local +++ b/packages/eservice-descriptors-archiver/aws.config.local @@ -1,7 +1,7 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 services=local [services local] diff --git a/packages/ivass-certified-attributes-importer/aws.config.local b/packages/ivass-certified-attributes-importer/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/ivass-certified-attributes-importer/aws.config.local +++ b/packages/ivass-certified-attributes-importer/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/kafka-iam-auth/README.md b/packages/kafka-iam-auth/README.md index 55e0b7b5e2..eb950abe4d 100644 --- a/packages/kafka-iam-auth/README.md +++ b/packages/kafka-iam-auth/README.md @@ -30,21 +30,22 @@ await runConsumer(config, topicName, processMessageHandler); ``` ## Local Testing + To simulate Kafaka topic consuming and executing a real SALS authentication with AWS, you need to connect your consumer to specific topic presents in dev environment. You must put the following variables in your consumer .env file to simulate the same credential provisioning executed by service in Kubernates pod: ```bash AWS_WEB_IDENTITY_TOKEN_FILE="{TOKE_FILE_PATH}" AWS_ROLE_ARN="arn:aws:iam::{ID}:role/interop-be-{SERVICE}-consumer-refactor-dev" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" AWS_STS_REGIONAL_ENDPOINTS="regional" -AWS_DEFAULT_REGION="eu-central-1" +AWS_DEFAULT_REGION="eu-south-1" ``` Replace all placeholders {...} with desired configurations. Token file should contains a valid token retrieved from AWS, by the way all of those variables can be found inspecting pod in dev cluster. - ## Credits + This project uses code from the [Original Repository](https://github.com/jmaver-plume/kafkajs-msk-iam-authentication-mechanism), which is licensed under the MIT License. We are grateful to the original authors and contributors for their work. diff --git a/packages/key-readmodel-writer/.env b/packages/key-readmodel-writer/.env index 297b233277..5282be8508 100644 --- a/packages/key-readmodel-writer/.env +++ b/packages/key-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/notifier-seeder/.env b/packages/notifier-seeder/.env index 526efb796e..b64a315f86 100644 --- a/packages/notifier-seeder/.env +++ b/packages/notifier-seeder/.env @@ -8,5 +8,5 @@ CATALOG_TOPIC="event-store.catalog.events" PURPOSE_TOPIC="event-store.purpose.events" AWS_CONFIG_FILE=aws.config.local -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" NOTIFICATION_QUEUE_URL="http://localhost:9324/000000000000/sqsLocalQueue.fifo" diff --git a/packages/notifier-seeder/aws.config.local b/packages/notifier-seeder/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/notifier-seeder/aws.config.local +++ b/packages/notifier-seeder/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/one-trust-notices/aws.config.local b/packages/one-trust-notices/aws.config.local index 4a3a746c5a..06a4c18070 100644 --- a/packages/one-trust-notices/aws.config.local +++ b/packages/one-trust-notices/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawskey -region=eu-central-1 \ No newline at end of file +region=eu-south-1 diff --git a/packages/producer-key-events-writer/.env b/packages/producer-key-events-writer/.env index a27c11516a..0a791d9188 100644 --- a/packages/producer-key-events-writer/.env +++ b/packages/producer-key-events-writer/.env @@ -13,4 +13,4 @@ KAFKA_GROUP_ID="producer-key-events-group" KAFKA_BROKERS="localhost:9092" KAFKA_DISABLE_AWS_IAM_AUTH="true" AUTHORIZATION_TOPIC="event-store.authorization.events" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/producer-key-readmodel-writer/.env b/packages/producer-key-readmodel-writer/.env index 1ce4ba2520..c7249b87bf 100644 --- a/packages/producer-key-readmodel-writer/.env +++ b/packages/producer-key-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/producer-keychain-readmodel-writer/.env b/packages/producer-keychain-readmodel-writer/.env index 37dfb3febf..08f3bc87cb 100644 --- a/packages/producer-keychain-readmodel-writer/.env +++ b/packages/producer-keychain-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/purpose-process/aws.config.local b/packages/purpose-process/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/purpose-process/aws.config.local +++ b/packages/purpose-process/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/purpose-readmodel-writer/.env b/packages/purpose-readmodel-writer/.env index de8cdcc8bc..9006d7735e 100644 --- a/packages/purpose-readmodel-writer/.env +++ b/packages/purpose-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/selfcare-onboarding-consumer/.env b/packages/selfcare-onboarding-consumer/.env index ed0fe628e1..06bc52ac9d 100644 --- a/packages/selfcare-onboarding-consumer/.env +++ b/packages/selfcare-onboarding-consumer/.env @@ -7,7 +7,7 @@ KAFKA_BROKERS="localhost:9092" KAFKA_BROKER_CONNECTION_STRING="placeholder" SELFCARE_TOPIC="placeholder.selfcare.topic" TOPIC_STARTING_OFFSET="earliest" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" RESET_CONSUMER_OFFSETS="false" diff --git a/packages/selfcare-onboarding-consumer/aws.config.local b/packages/selfcare-onboarding-consumer/aws.config.local index 9c57dc948e..8dfc0e7f55 100644 --- a/packages/selfcare-onboarding-consumer/aws.config.local +++ b/packages/selfcare-onboarding-consumer/aws.config.local @@ -1,7 +1,7 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 services=local [services local] diff --git a/packages/tenant-readmodel-writer/.env b/packages/tenant-readmodel-writer/.env index 1ed07e0ba1..3b9d5ab94a 100644 --- a/packages/tenant-readmodel-writer/.env +++ b/packages/tenant-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" From b3db1fea45fe20d39e1cb21f4da2794c2b046a9a Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Wed, 30 Oct 2024 14:10:43 +0100 Subject: [PATCH 02/34] IMN-842 - Removing uuid and using crypto (#1127) --- packages/agreement-email-sender/package.json | 2 - .../agreement-outbound-writer/package.json | 2 - .../package.json | 2 - packages/agreement-process/package.json | 2 - .../agreement-readmodel-writer/package.json | 2 - .../package.json | 2 - .../attribute-registry-process/package.json | 2 - ...tributeRegistryService.integration.test.ts | 10 +- packages/authorization-process/package.json | 1 - packages/authorization-updater/package.json | 2 - packages/backend-for-frontend/package.json | 2 - packages/catalog-outbound-writer/package.json | 2 - .../catalog-platformstate-writer/package.json | 2 - packages/catalog-process/package.json | 2 - .../catalog-readmodel-writer/package.json | 2 - packages/client-readmodel-writer/package.json | 2 - packages/commons-test/package.json | 2 - packages/commons/package.json | 2 - packages/commons/src/context/context.ts | 4 +- .../compute-agreements-consumer/package.json | 2 - packages/datalake-data-export/package.json | 2 - .../package.json | 2 - .../package.json | 4 - packages/key-readmodel-writer/package.json | 2 - packages/models/package.json | 2 - packages/models/src/brandedIds.ts | 4 +- packages/notifier-seeder/package.json | 2 - .../agreementEventNotificationMessage.ts | 4 +- .../authorizationEventNotificationMessage.ts | 4 +- .../catalogItemEventNotificationMessage.ts | 6 +- .../purposeEventNotificationMessage.ts | 4 +- .../notificationMessage.integration.test.ts | 11 +- .../test/queueManager.integration.test.ts | 6 +- packages/pn-consumers/package.json | 2 - .../test/readModelQueriesService.test.ts | 10 +- .../producer-key-events-writer/package.json | 2 - .../package.json | 2 - .../package.json | 2 - packages/purpose-outbound-writer/package.json | 2 - .../purpose-platformstate-writer/package.json | 2 - .../purpose-readmodel-writer/package.json | 2 - .../selfcare-onboarding-consumer/package.json | 2 - packages/tenant-outbound-writer/package.json | 2 - packages/tenant-process/package.json | 2 - pnpm-lock.yaml | 208 ------------------ 45 files changed, 31 insertions(+), 309 deletions(-) diff --git a/packages/agreement-email-sender/package.json b/packages/agreement-email-sender/package.json index 7d5ffffa8b..bfb9359b03 100644 --- a/packages/agreement-email-sender/package.json +++ b/packages/agreement-email-sender/package.json @@ -25,7 +25,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "cpx2": "7.0.1", "axios": "1.7.4", "pagopa-interop-commons-test": "workspace:*", @@ -33,7 +32,6 @@ "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index 55023519f9..29903c6541 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -20,13 +20,11 @@ "@anatine/zod-mock": "3.13.4", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/agreement-platformstate-writer/package.json b/packages/agreement-platformstate-writer/package.json index 11f0fee562..4774847616 100644 --- a/packages/agreement-platformstate-writer/package.json +++ b/packages/agreement-platformstate-writer/package.json @@ -22,13 +22,11 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/agreement-process/package.json b/packages/agreement-process/package.json index 96db2dc22a..980c7b3721 100644 --- a/packages/agreement-process/package.json +++ b/packages/agreement-process/package.json @@ -23,7 +23,6 @@ "@pagopa/eslint-config": "3.0.0", "@types/express": "4.17.21", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "cpx2": "7.0.1", "openapi-zod-client": "1.18.1", "pagopa-interop-commons-test": "workspace:*", @@ -47,7 +46,6 @@ "pagopa-interop-api-clients": "workspace:*", "pagopa-interop-agreement-lifecycle": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/agreement-readmodel-writer/package.json b/packages/agreement-readmodel-writer/package.json index 1f5be6d7ea..9fb6785dcc 100644 --- a/packages/agreement-readmodel-writer/package.json +++ b/packages/agreement-readmodel-writer/package.json @@ -22,13 +22,11 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0", "@anatine/zod-mock": "3.13.4" }, diff --git a/packages/anac-certified-attributes-importer/package.json b/packages/anac-certified-attributes-importer/package.json index f318036f2e..a78f5ad664 100644 --- a/packages/anac-certified-attributes-importer/package.json +++ b/packages/anac-certified-attributes-importer/package.json @@ -24,7 +24,6 @@ "@types/ssh2-sftp-client": "9.0.4", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "@types/uuid": "9.0.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", @@ -33,7 +32,6 @@ "dependencies": { "csv": "^6.3.2", "dotenv-flow": "4.1.0", - "uuid": "10.0.0", "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "pagopa-interop-api-clients": "workspace:*", diff --git a/packages/attribute-registry-process/package.json b/packages/attribute-registry-process/package.json index d7387f2bd6..9725a194c8 100644 --- a/packages/attribute-registry-process/package.json +++ b/packages/attribute-registry-process/package.json @@ -24,7 +24,6 @@ "@protobuf-ts/runtime": "2.9.4", "@types/express": "4.17.21", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -46,7 +45,6 @@ "pagopa-interop-api-clients": "workspace:*", "pg-promise": "11.8.0", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts b/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts index 3291374623..1064f5ddd2 100644 --- a/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts +++ b/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-floating-promises */ +import { randomUUID } from "crypto"; import { describe, expect, it } from "vitest"; import { decodeProtobufPayload, @@ -7,7 +8,6 @@ import { getMockAuthData, } from "pagopa-interop-commons-test"; import { genericLogger } from "pagopa-interop-commons"; -import { v4 as uuidv4 } from "uuid"; import { Attribute, AttributeAddedV1, @@ -214,7 +214,7 @@ describe("database test", () => { features: [ { type: "PersistentCertifier", - certifierId: uuidv4(), + certifierId: randomUUID(), }, ], }; @@ -274,7 +274,7 @@ describe("database test", () => { features: [ { type: "PersistentCertifier", - certifierId: uuidv4(), + certifierId: randomUUID(), }, ], }; @@ -342,7 +342,7 @@ describe("database test", () => { features: [ { type: "PersistentCertifier", - certifierId: uuidv4(), + certifierId: randomUUID(), }, ], }; @@ -402,7 +402,7 @@ describe("database test", () => { features: [ { type: "PersistentCertifier", - certifierId: uuidv4(), + certifierId: randomUUID(), }, ], }; diff --git a/packages/authorization-process/package.json b/packages/authorization-process/package.json index a71f8e48bf..27f0127472 100644 --- a/packages/authorization-process/package.json +++ b/packages/authorization-process/package.json @@ -22,7 +22,6 @@ "@protobuf-ts/runtime": "2.9.4", "@types/express": "4.17.21", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "ts-node": "10.9.2", diff --git a/packages/authorization-updater/package.json b/packages/authorization-updater/package.json index 2338c136e4..d703d3b783 100644 --- a/packages/authorization-updater/package.json +++ b/packages/authorization-updater/package.json @@ -25,7 +25,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "openapi-zod-client": "1.18.1", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", @@ -45,7 +44,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/backend-for-frontend/package.json b/packages/backend-for-frontend/package.json index 02f8500104..7044893c23 100644 --- a/packages/backend-for-frontend/package.json +++ b/packages/backend-for-frontend/package.json @@ -24,7 +24,6 @@ "@types/multer": "1.4.11", "@types/node": "20.14.6", "@types/qs": "6.9.15", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -51,7 +50,6 @@ "pagopa-interop-models": "workspace:*", "qs": "6.12.3", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "yaml": "2.4.5", "zod": "3.23.8" } diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index a5a8e5cbe6..8e19ced827 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -20,13 +20,11 @@ "@anatine/zod-mock": "3.13.4", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/catalog-platformstate-writer/package.json b/packages/catalog-platformstate-writer/package.json index a1f3084e60..4a720b21c3 100644 --- a/packages/catalog-platformstate-writer/package.json +++ b/packages/catalog-platformstate-writer/package.json @@ -22,13 +22,11 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/catalog-process/package.json b/packages/catalog-process/package.json index 6c28f0d06a..769b1246b8 100644 --- a/packages/catalog-process/package.json +++ b/packages/catalog-process/package.json @@ -25,7 +25,6 @@ "@protobuf-ts/runtime": "2.9.4", "@types/express": "4.17.21", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "pg-promise": "11.8.0", @@ -47,7 +46,6 @@ "pagopa-interop-models": "workspace:*", "pagopa-interop-api-clients": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/catalog-readmodel-writer/package.json b/packages/catalog-readmodel-writer/package.json index d4fbdc3efc..8a6e112fd4 100644 --- a/packages/catalog-readmodel-writer/package.json +++ b/packages/catalog-readmodel-writer/package.json @@ -22,14 +22,12 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/client-readmodel-writer/package.json b/packages/client-readmodel-writer/package.json index 2e2aa6b8c8..6b5a59464d 100644 --- a/packages/client-readmodel-writer/package.json +++ b/packages/client-readmodel-writer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -38,7 +37,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/commons-test/package.json b/packages/commons-test/package.json index dde6a915e0..c8acc06d33 100644 --- a/packages/commons-test/package.json +++ b/packages/commons-test/package.json @@ -26,7 +26,6 @@ "@protobuf-ts/runtime": "2.9.4", "@testcontainers/postgresql": "10.9.0", "@types/jsonwebtoken": "9.0.6", - "@types/uuid": "9.0.8", "@zodios/core": "10.9.6", "aws-sdk-client-mock": "4.0.1", "axios": "1.7.4", @@ -38,7 +37,6 @@ "testcontainers": "10.9.0", "ts-pattern": "5.2.0", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0", "zod": "3.23.8" } diff --git a/packages/commons/package.json b/packages/commons/package.json index 237c7ee534..a2d1cf32a8 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -42,7 +42,6 @@ "rate-limiter-flexible": "5.0.3", "redis": "4.6.15", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "winston": "3.13.0", "zod": "3.23.8", "zod-validation-error": "3.3.0" @@ -52,7 +51,6 @@ "@types/jsonwebtoken": "9.0.6", "@types/node": "20.14.6", "@types/nodemailer": "6.4.15", - "@types/uuid": "9.0.8", "cpx2": "7.0.1", "eslint": "8.57.0", "prettier": "2.8.8", diff --git a/packages/commons/src/context/context.ts b/packages/commons/src/context/context.ts index 847f9d385c..e37a83d84b 100644 --- a/packages/commons/src/context/context.ts +++ b/packages/commons/src/context/context.ts @@ -1,9 +1,9 @@ import { constants } from "http2"; +import { randomUUID } from "crypto"; import { ZodiosRouterContextRequestHandler, zodiosContext, } from "@zodios/express"; -import { v4 as uuidv4 } from "uuid"; import { z } from "zod"; import { CorrelationId, @@ -63,7 +63,7 @@ export const contextMiddleware = setCtx(correlationIdHeader); } else { - setCtx(uuidv4()); + setCtx(randomUUID()); } return next(); diff --git a/packages/compute-agreements-consumer/package.json b/packages/compute-agreements-consumer/package.json index a8c15c79d7..b2b6d22bb8 100644 --- a/packages/compute-agreements-consumer/package.json +++ b/packages/compute-agreements-consumer/package.json @@ -20,7 +20,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "prettier": "2.8.8", "ts-node": "10.9.2", "typescript": "5.4.5" @@ -33,7 +32,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/datalake-data-export/package.json b/packages/datalake-data-export/package.json index 6ecf307f90..d9ab0456b6 100644 --- a/packages/datalake-data-export/package.json +++ b/packages/datalake-data-export/package.json @@ -19,7 +19,6 @@ "license": "Apache-2.0", "devDependencies": { "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "@pagopa/eslint-config": "3.0.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", @@ -33,7 +32,6 @@ "dotenv-flow": "4.1.0", "date-fns": "3.6.0", "mongodb": "6.7.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/eservice-descriptors-archiver/package.json b/packages/eservice-descriptors-archiver/package.json index 94ca71141d..c81de847eb 100644 --- a/packages/eservice-descriptors-archiver/package.json +++ b/packages/eservice-descriptors-archiver/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "openapi-zod-client": "1.18.1", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -40,7 +39,6 @@ "pagopa-interop-models": "workspace:*", "pagopa-interop-api-clients": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/ivass-certified-attributes-importer/package.json b/packages/ivass-certified-attributes-importer/package.json index c60e0205ad..a4866577fa 100644 --- a/packages/ivass-certified-attributes-importer/package.json +++ b/packages/ivass-certified-attributes-importer/package.json @@ -23,9 +23,7 @@ "@types/node": "20.14.6", "@types/ssh2-sftp-client": "9.0.4", "pagopa-interop-commons-test": "workspace:*", - "@types/uuid": "9.0.8", "@types/adm-zip": "0.5.5", - "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", @@ -33,8 +31,6 @@ "vitest": "1.6.0" }, "dependencies": { - "ts-pattern": "5.2.0", - "uuid": "10.0.0", "@aws-sdk/client-s3": "3.387.0", "adm-zip": "0.5.15", "axios": "1.7.4", diff --git a/packages/key-readmodel-writer/package.json b/packages/key-readmodel-writer/package.json index 761b9c59da..af4f12b037 100644 --- a/packages/key-readmodel-writer/package.json +++ b/packages/key-readmodel-writer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -38,7 +37,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/models/package.json b/packages/models/package.json index d3ce8ffb63..f3ab5d994d 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -26,9 +26,7 @@ "@protobuf-ts/protoc": "2.9.4", "@protobuf-ts/runtime": "2.9.4", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8", "zod-validation-error": "3.3.0" }, diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index a9a306ff65..d2e9470fb0 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -1,5 +1,5 @@ +import { randomUUID } from "crypto"; import { z } from "zod"; -import { v4 as uuidv4 } from "uuid"; export const CorrelationId = z.string().brand("CorrelationId"); export type CorrelationId = z.infer; @@ -159,7 +159,7 @@ type IDS = // it infers the type of the ID based on how is used the result // the 'as' is used to cast the uuid string to the inferred type export function generateId(): T { - return uuidv4() as T; + return randomUUID() as T; } // This function is used to get a branded ID from a string diff --git a/packages/notifier-seeder/package.json b/packages/notifier-seeder/package.json index f06224c2b9..ac3c5213a5 100644 --- a/packages/notifier-seeder/package.json +++ b/packages/notifier-seeder/package.json @@ -29,7 +29,6 @@ "ts-node": "10.9.2", "typescript": "5.4.5", "kafkajs": "2.2.4", - "@types/uuid": "9.0.8", "mkdirp": "3.0.1", "pagopa-interop-commons-test": "workspace:*", "testcontainers": "10.9.0", @@ -45,7 +44,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/notifier-seeder/src/models/agreement/agreementEventNotificationMessage.ts b/packages/notifier-seeder/src/models/agreement/agreementEventNotificationMessage.ts index 97f1ad2b34..5e36a96556 100644 --- a/packages/notifier-seeder/src/models/agreement/agreementEventNotificationMessage.ts +++ b/packages/notifier-seeder/src/models/agreement/agreementEventNotificationMessage.ts @@ -1,6 +1,6 @@ +import { randomUUID } from "crypto"; import { AgreementEventEnvelopeV2 } from "pagopa-interop-models"; import { match } from "ts-pattern"; -import { v4 as uuidv4 } from "uuid"; import { QueueMessage } from "../../queue-manager/queueMessage.js"; import { AgreementEventNotification } from "./agreementEventNotification.js"; @@ -41,7 +41,7 @@ export const buildAgreementMessage = ( event: AgreementEventEnvelopeV2, agreementEvent: AgreementEventNotification ): QueueMessage => ({ - messageUUID: uuidv4(), + messageUUID: randomUUID(), eventJournalPersistenceId: event.stream_id, eventJournalSequenceNumber: event.version, eventTimestamp: Number(event.log_date), diff --git a/packages/notifier-seeder/src/models/authorization/authorizationEventNotificationMessage.ts b/packages/notifier-seeder/src/models/authorization/authorizationEventNotificationMessage.ts index eb8f23a3ad..2a915d3141 100644 --- a/packages/notifier-seeder/src/models/authorization/authorizationEventNotificationMessage.ts +++ b/packages/notifier-seeder/src/models/authorization/authorizationEventNotificationMessage.ts @@ -1,5 +1,5 @@ +import { randomUUID } from "crypto"; import { match } from "ts-pattern"; -import { v4 as uuidv4 } from "uuid"; import { AuthorizationEventEnvelopeV2 } from "pagopa-interop-models"; import { QueueMessage } from "../../queue-manager/queueMessage.js"; import { AuthorizationEventNotification } from "./authorizationEventNotification.js"; @@ -34,7 +34,7 @@ export const buildAuthorizationMessage = ( event: AuthorizationEventEnvelopeV2, authorizationEvent: AuthorizationEventNotification ): QueueMessage => ({ - messageUUID: uuidv4(), + messageUUID: randomUUID(), eventJournalPersistenceId: event.stream_id, eventJournalSequenceNumber: event.version, eventTimestamp: Number(event.log_date), diff --git a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts index 03a4a950da..cc05355565 100644 --- a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts +++ b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts @@ -1,6 +1,6 @@ +import { randomUUID } from "crypto"; import { EServiceEventEnvelopeV2 } from "pagopa-interop-models"; import { match } from "ts-pattern"; -import { v4 as uuidv4 } from "uuid"; import { QueueMessage } from "../../queue-manager/queueMessage.js"; import { CatalogItemEventNotification } from "./catalogItemEventNotification.js"; @@ -59,7 +59,7 @@ export const eventV2TypeMapper = ( ) .exhaustive(); -/* +/* This method is used to build a message for catalog events, that to be sent to the notify queue, it will be used to mantains compatibility with the old version of queue consumers. Related issue https://pagopa.atlassian.net/browse/IMN-67 @@ -68,7 +68,7 @@ export const buildCatalogMessage = ( event: EServiceEventEnvelopeV2, catalogItemEvent: CatalogItemEventNotification ): QueueMessage => ({ - messageUUID: uuidv4(), + messageUUID: randomUUID(), eventJournalPersistenceId: event.stream_id, eventJournalSequenceNumber: event.version, eventTimestamp: Number(event.log_date), diff --git a/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts index a426ec90ee..2b82fb7b20 100644 --- a/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts +++ b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts @@ -1,6 +1,6 @@ +import { randomUUID } from "crypto"; import { PurposeEventEnvelopeV2 } from "pagopa-interop-models"; import { match } from "ts-pattern"; -import { v4 as uuidv4 } from "uuid"; import { QueueMessage } from "../../queue-manager/queueMessage.js"; import { PurposeEventNotification } from "./purposeEventNotification.js"; @@ -46,7 +46,7 @@ export const buildPurposeMessage = ( event: PurposeEventEnvelopeV2, purposeEvent: PurposeEventNotification ): QueueMessage => ({ - messageUUID: uuidv4(), + messageUUID: randomUUID(), eventJournalPersistenceId: event.stream_id, eventJournalSequenceNumber: event.version, eventTimestamp: Number(event.log_date), diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 1814fd3f1e..1c82100303 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -1,5 +1,5 @@ +import { randomUUID } from "crypto"; import { describe, expect, it, vi } from "vitest"; - import { Agreement, AgreementAddedV2, @@ -24,7 +24,6 @@ import { unsafeBrandId, } from "pagopa-interop-models"; import { genericLogger } from "pagopa-interop-commons"; -import { v4 } from "uuid"; import { getMockAgreement, getMockClient, @@ -135,7 +134,7 @@ describe("Notification tests", async () => { sequence_num: 1, stream_id: "d27f668f-630b-4889-a97f-2b7e39b24188", version: 1, - correlation_id: v4(), + correlation_id: randomUUID(), log_date: new Date(), event_version: 2, type: "EServiceDescriptorSuspended", @@ -160,7 +159,7 @@ describe("Notification tests", async () => { sequence_num: 2, stream_id: mockPurpose.id, version: 1, - correlation_id: v4(), + correlation_id: randomUUID(), log_date: new Date(), event_version: 2, type: "PurposeAdded", @@ -194,7 +193,7 @@ describe("Notification tests", async () => { sequence_num: 2, stream_id: mockAgreement.id, version: 1, - correlation_id: v4(), + correlation_id: randomUUID(), log_date: new Date(), event_version: 2, type: "AgreementAdded", @@ -222,7 +221,7 @@ describe("Notification tests", async () => { sequence_num: 3, stream_id: mockClient.id, version: 1, - correlation_id: v4(), + correlation_id: randomUUID(), log_date: new Date(), event_version: 2, type: "ClientKeyAdded", diff --git a/packages/notifier-seeder/test/queueManager.integration.test.ts b/packages/notifier-seeder/test/queueManager.integration.test.ts index 2651d8534d..4b5c89c856 100644 --- a/packages/notifier-seeder/test/queueManager.integration.test.ts +++ b/packages/notifier-seeder/test/queueManager.integration.test.ts @@ -1,6 +1,6 @@ /* eslint-disable functional/immutable-data */ /* eslint-disable functional/no-let */ -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { describe, expect, it } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; import { @@ -18,7 +18,7 @@ describe("FileManager tests", async () => { it("should send a message to the queue and receive it back", async () => { await queueWriter.send( { - messageUUID: uuidv4(), + messageUUID: randomUUID(), kind: "TestMessageKind", eventJournalPersistenceId: "test-persistence-id", eventJournalSequenceNumber: 0, @@ -47,7 +47,7 @@ describe("FileManager tests", async () => { await expect( nonExistingQueueWriter.send( { - messageUUID: uuidv4(), + messageUUID: randomUUID(), kind: "TestMessageKind", eventJournalPersistenceId: "test-persistence-id", eventJournalSequenceNumber: 0, diff --git a/packages/pn-consumers/package.json b/packages/pn-consumers/package.json index c741dfba1a..1d016ccfe2 100644 --- a/packages/pn-consumers/package.json +++ b/packages/pn-consumers/package.json @@ -23,8 +23,6 @@ "@types/node": "20.14.6", "@types/nodemailer": "6.4.9", "pagopa-interop-commons-test": "workspace:*", - "@types/uuid": "9.0.8", - "uuid": "10.0.0", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", diff --git a/packages/pn-consumers/test/readModelQueriesService.test.ts b/packages/pn-consumers/test/readModelQueriesService.test.ts index 63bbea7c55..5b11bb3f9e 100644 --- a/packages/pn-consumers/test/readModelQueriesService.test.ts +++ b/packages/pn-consumers/test/readModelQueriesService.test.ts @@ -1,4 +1,5 @@ /* eslint-disable functional/no-let */ +import { randomUUID } from "crypto"; import { describe, expect, it, afterEach, inject } from "vitest"; import { getMockPurpose, @@ -7,15 +8,14 @@ import { setupTestContainersVitest, } from "pagopa-interop-commons-test/index.js"; import { AttributeId, TenantId, unsafeBrandId } from "pagopa-interop-models"; -import { v4 as uuidv4 } from "uuid"; import { ReadModelQueriesClient } from "../src/services/readModelQueriesService.js"; const PN_ESERVICE_ID_MOCK = "4747d063-0d9c-4a5d-b143-9f2fdc4d7f22"; const COMUNI_E_LORO_CONSORZI_E_ASSOCIAZIONI_ATTRIBUTE_ID_MOCK = "5ec5dd81-ff71-4af8-974b-4190eb8347bf"; -const TENANT_COMUNE_ID = uuidv4(); -const TENANT_NON_COMUNE_ID = uuidv4(); +const TENANT_COMUNE_ID = randomUUID(); +const TENANT_NON_COMUNE_ID = randomUUID(); export const { cleanup, readModelRepository, postgresDB, fileManager } = await setupTestContainersVitest( @@ -75,7 +75,7 @@ describe("MetricsManager", () => { consumerId: unsafeBrandId(TENANT_COMUNE_ID), versions: [ { - id: uuidv4(), + id: randomUUID(), state: "Active", dailyCalls: 1, createdAt: new Date(), @@ -90,7 +90,7 @@ describe("MetricsManager", () => { consumerId: unsafeBrandId(TENANT_NON_COMUNE_ID), versions: [ { - id: uuidv4(), + id: randomUUID(), state: "Active", dailyCalls: 1, createdAt: new Date(), diff --git a/packages/producer-key-events-writer/package.json b/packages/producer-key-events-writer/package.json index 5c9843fb69..8b1c51a2d5 100644 --- a/packages/producer-key-events-writer/package.json +++ b/packages/producer-key-events-writer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "ts-node": "10.9.2", @@ -37,7 +36,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/producer-key-readmodel-writer/package.json b/packages/producer-key-readmodel-writer/package.json index 00e45346b1..da8bfaa6d4 100644 --- a/packages/producer-key-readmodel-writer/package.json +++ b/packages/producer-key-readmodel-writer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -38,7 +37,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/producer-keychain-readmodel-writer/package.json b/packages/producer-keychain-readmodel-writer/package.json index 2e3926b675..d73caa1024 100644 --- a/packages/producer-keychain-readmodel-writer/package.json +++ b/packages/producer-keychain-readmodel-writer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -38,7 +37,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index e34f3f1141..5b2830f708 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -20,13 +20,11 @@ "@anatine/zod-mock": "3.13.4", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/purpose-platformstate-writer/package.json b/packages/purpose-platformstate-writer/package.json index ea2a130497..b17c00dde4 100644 --- a/packages/purpose-platformstate-writer/package.json +++ b/packages/purpose-platformstate-writer/package.json @@ -22,13 +22,11 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/purpose-readmodel-writer/package.json b/packages/purpose-readmodel-writer/package.json index 47ec8edf85..cbdabd96f1 100644 --- a/packages/purpose-readmodel-writer/package.json +++ b/packages/purpose-readmodel-writer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -38,7 +37,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/selfcare-onboarding-consumer/package.json b/packages/selfcare-onboarding-consumer/package.json index eaf9394332..e1eb6bf376 100644 --- a/packages/selfcare-onboarding-consumer/package.json +++ b/packages/selfcare-onboarding-consumer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "prettier": "2.8.8", "ts-node": "10.9.2", "typescript": "5.4.5", @@ -37,7 +36,6 @@ "pagopa-interop-models": "workspace:*", "pagopa-interop-api-clients": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 2dacaf93a7..190a113491 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -20,13 +20,11 @@ "@anatine/zod-mock": "3.13.4", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/tenant-process/package.json b/packages/tenant-process/package.json index afb5e3112c..1ec9be60ad 100644 --- a/packages/tenant-process/package.json +++ b/packages/tenant-process/package.json @@ -30,7 +30,6 @@ "vitest": "1.6.0" }, "dependencies": { - "@types/uuid": "9.0.8", "@zodios/core": "10.9.6", "@zodios/express": "10.6.1", "axios": "1.7.4", @@ -42,7 +41,6 @@ "pagopa-interop-models": "workspace:*", "pagopa-interop-api-clients": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d5ee42b992..9dd9ae6c53 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,9 +48,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 axios: specifier: 1.7.4 version: 1.7.4 @@ -72,9 +69,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -140,9 +134,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -158,9 +149,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -207,9 +195,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -225,9 +210,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -267,9 +249,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -286,9 +265,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 cpx2: specifier: 7.0.1 version: 7.0.1 @@ -359,9 +335,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -377,9 +350,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -404,9 +374,6 @@ importers: ssh2-sftp-client: specifier: ^9.1.0 version: 9.1.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -420,9 +387,6 @@ importers: '@types/ssh2-sftp-client': specifier: 9.0.4 version: 9.0.4 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -599,9 +563,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -624,9 +585,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -758,9 +716,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -812,9 +767,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -825,9 +777,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -903,9 +852,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 yaml: specifier: 2.4.5 version: 2.4.5 @@ -931,9 +877,6 @@ importers: '@types/qs': specifier: 6.9.15 version: 6.9.15 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -995,9 +938,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1013,9 +953,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -1062,9 +999,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -1080,9 +1014,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -1122,9 +1053,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1147,9 +1075,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -1211,9 +1136,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -1232,9 +1154,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -1305,9 +1224,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1318,9 +1234,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1408,9 +1321,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 winston: specifier: 3.13.0 version: 3.13.0 @@ -1433,9 +1343,6 @@ importers: '@types/nodemailer': specifier: 6.4.15 version: 6.4.15 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 cpx2: specifier: 7.0.1 version: 7.0.1 @@ -1475,9 +1382,6 @@ importers: '@types/jsonwebtoken': specifier: 9.0.6 version: 9.0.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 '@zodios/core': specifier: 10.9.6 version: 10.9.6(axios@1.7.4)(zod@3.23.8) @@ -1511,9 +1415,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -1544,9 +1445,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1557,9 +1455,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 prettier: specifier: 2.8.8 version: 2.8.8 @@ -1587,9 +1482,6 @@ importers: pagopa-interop-models: specifier: workspace:* version: link:../models - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1600,9 +1492,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1691,9 +1580,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1704,9 +1590,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -1816,9 +1699,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1835,9 +1715,6 @@ importers: '@types/ssh2-sftp-client': specifier: 9.0.4 version: 9.0.4 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1902,9 +1779,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1915,9 +1789,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1951,15 +1822,9 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2012,9 +1877,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2025,9 +1887,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 kafkajs: specifier: 2.2.4 version: 2.2.4 @@ -2132,9 +1991,6 @@ importers: '@types/nodemailer': specifier: 6.4.9 version: 6.4.9 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2150,9 +2006,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -2180,9 +2033,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2193,9 +2043,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2235,9 +2082,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2248,9 +2092,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2293,9 +2134,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2306,9 +2144,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2370,9 +2205,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2388,9 +2220,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -2437,9 +2266,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -2455,9 +2281,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -2564,9 +2387,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2577,9 +2397,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2625,9 +2442,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2638,9 +2452,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 prettier: specifier: 2.8.8 version: 2.8.8 @@ -2696,9 +2507,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2714,18 +2522,12 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) packages/tenant-process: dependencies: - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 '@zodios/core': specifier: 10.9.6 version: 10.9.6(axios@1.7.4)(zod@3.23.8) @@ -2759,9 +2561,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -8134,9 +7933,6 @@ packages: resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} dev: false - /@types/uuid@9.0.8: - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - /@types/webidl-conversions@7.0.3: resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} dev: false @@ -13089,10 +12885,6 @@ packages: engines: {node: '>= 0.4.0'} dev: false - /uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true - /uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true From b9ffd7ee63657002eb7af7daa9cba23acd789734 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 31 Oct 2024 15:09:20 +0100 Subject: [PATCH 03/34] IMN-792 Add events service v2 in agreement-platformstate-writer (#987) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../src/consumerServiceV2.ts | 293 +- .../src/utils.ts | 448 ++- .../consumerServiceV2.integration.test.ts | 3137 +++++++++++++++++ .../test/utils.test.ts | 650 ++++ .../test/utils.ts | 11 + .../test/utils.test.ts | 2 - packages/commons-test/src/testUtils.ts | 21 + .../src/tokenGenerationReadmodelUtils.ts | 272 +- 8 files changed, 4749 insertions(+), 85 deletions(-) create mode 100644 packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts create mode 100644 packages/agreement-platformstate-writer/test/utils.test.ts create mode 100644 packages/agreement-platformstate-writer/test/utils.ts diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts index 53cb09bd99..caa711eee0 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts @@ -1,27 +1,292 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { AgreementEventEnvelopeV2 } from "pagopa-interop-models"; +import { + Agreement, + AgreementEventEnvelopeV2, + AgreementV2, + fromAgreementV2, + genericInternalError, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makePlatformStatesAgreementPK, + makePlatformStatesEServiceDescriptorPK, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, +} from "pagopa-interop-models"; import { match } from "ts-pattern"; +import { + agreementStateToItemState, + deleteAgreementEntry, + readAgreementEntry, + readCatalogEntry, + updateAgreementStateInPlatformStatesEntry, + updateAgreementStateOnTokenStates, + updateAgreementStateAndDescriptorInfoOnTokenStates, + writeAgreementEntry, + isLatestAgreement, +} from "./utils.js"; export async function handleMessageV2( message: AgreementEventEnvelopeV2, - _dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient ): Promise { await match(message) + .with({ type: "AgreementActivated" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + + const existingAgreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if (existingAgreementEntry) { + if (existingAgreementEntry.version > msg.version) { + // Stops processing if the message is older than the agreement entry + return Promise.resolve(); + } else { + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + agreementStateToItemState(agreement.state), + msg.version + ); + } + } else { + if (!agreement.stamps.activation) { + throw genericInternalError( + "An activated agreement should have activation stamp" + ); + } + const agreementEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: agreementStateToItemState(agreement.state), + version: msg.version, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + agreement.stamps.activation.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + + await writeAgreementEntry(agreementEntry, dynamoDBClient); + } + + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry = await readCatalogEntry( + pkCatalogEntry, + dynamoDBClient + ); + + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + // token-generation-states + await updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + } + }) .with( - { type: "AgreementAdded" }, - { type: "AgreementDeleted" }, - { type: "DraftAgreementUpdated" }, - { type: "AgreementSubmitted" }, - { type: "AgreementActivated" }, { type: "AgreementUnsuspendedByProducer" }, { type: "AgreementUnsuspendedByConsumer" }, { type: "AgreementUnsuspendedByPlatform" }, - { type: "AgreementArchivedByConsumer" }, - { type: "AgreementArchivedByUpgrade" }, - { type: "AgreementUpgraded" }, { type: "AgreementSuspendedByProducer" }, { type: "AgreementSuspendedByConsumer" }, { type: "AgreementSuspendedByPlatform" }, + async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const agreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + + if (!agreementEntry || agreementEntry.version > msg.version) { + return Promise.resolve(); + } else { + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + agreementStateToItemState(agreement.state), + msg.version + ); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + // token-generation-states only if agreement is the latest + + await updateAgreementStateOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + }); + } + } + } + ) + .with({ type: "AgreementUpgraded" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const agreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if (agreementEntry) { + if (agreementEntry.version > msg.version) { + return Promise.resolve(); + } else { + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + agreementStateToItemState(agreement.state), + msg.version + ); + } + } else { + if (!agreement.stamps.activation) { + throw genericInternalError( + "An activated agreement should have activation stamp" + ); + } + const newAgreementEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: agreementStateToItemState(agreement.state), + version: msg.version, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + agreement.stamps.activation.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + + await writeAgreementEntry(newAgreementEntry, dynamoDBClient); + } + + const updateLatestAgreementOnTokenStates = async ( + catalogEntry: PlatformStatesCatalogEntry | undefined + ): Promise => { + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + // token-generation-states only if agreement is the latest + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId( + { + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + } + ); + + await updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + } + }; + + const catalogEntry = await readCatalogEntry( + pkCatalogEntry, + dynamoDBClient + ); + + await updateLatestAgreementOnTokenStates(catalogEntry); + + const updatedCatalogEntry = await readCatalogEntry( + pkCatalogEntry, + dynamoDBClient + ); + + if ( + updatedCatalogEntry && + (!catalogEntry || updatedCatalogEntry.state !== catalogEntry.state) + ) { + await updateLatestAgreementOnTokenStates(updatedCatalogEntry); + } + }) + .with({ type: "AgreementArchivedByUpgrade" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + const pk = makePlatformStatesAgreementPK(agreement.id); + await deleteAgreementEntry(pk, dynamoDBClient); + }) + .with({ type: "AgreementArchivedByConsumer" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + // token-generation-states only if agreement is the latest + + await updateAgreementStateOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + }); + } + + await deleteAgreementEntry(primaryKey, dynamoDBClient); + }) + .with( + { type: "AgreementAdded" }, + { type: "AgreementDeleted" }, + { type: "DraftAgreementUpdated" }, + { type: "AgreementSubmitted" }, { type: "AgreementRejected" }, { type: "AgreementConsumerDocumentAdded" }, { type: "AgreementConsumerDocumentRemoved" }, @@ -31,3 +296,11 @@ export async function handleMessageV2( ) .exhaustive(); } + +const parseAgreement = (agreementV2: AgreementV2 | undefined): Agreement => { + if (!agreementV2) { + throw genericInternalError(`Agreement not found in message data`); + } + + return fromAgreementV2(agreementV2); +}; diff --git a/packages/agreement-platformstate-writer/src/utils.ts b/packages/agreement-platformstate-writer/src/utils.ts index 678ef75f8a..b42a26d449 100644 --- a/packages/agreement-platformstate-writer/src/utils.ts +++ b/packages/agreement-platformstate-writer/src/utils.ts @@ -1,9 +1,20 @@ import { + AgreementId, + agreementState, + AgreementState, genericInternalError, + GSIPKConsumerIdEServiceId, + GSIPKEServiceIdDescriptorId, + itemState, + ItemState, PlatformStatesAgreementEntry, PlatformStatesAgreementPK, + PlatformStatesCatalogEntry, + PlatformStatesEServiceDescriptorPK, + TokenGenerationStatesClientPurposeEntry, } from "pagopa-interop-models"; import { + AttributeValue, DeleteItemCommand, DeleteItemInput, DynamoDBClient, @@ -12,8 +23,14 @@ import { GetItemInput, PutItemCommand, PutItemInput, + QueryCommand, + QueryCommandOutput, + QueryInput, + UpdateItemCommand, + UpdateItemInput, } from "@aws-sdk/client-dynamodb"; import { unmarshall } from "@aws-sdk/util-dynamodb"; +import { z } from "zod"; import { config } from "./config/config.js"; export const writeAgreementEntry = async ( @@ -21,6 +38,7 @@ export const writeAgreementEntry = async ( dynamoDBClient: DynamoDBClient ): Promise => { const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", Item: { PK: { S: agreementEntry.PK, @@ -37,7 +55,7 @@ export const writeAgreementEntry = async ( GSIPK_consumerId_eserviceId: { S: agreementEntry.GSIPK_consumerId_eserviceId, }, - GSI_agreementTimestamp: { + GSISK_agreementTimestamp: { S: agreementEntry.GSISK_agreementTimestamp, }, agreementDescriptorId: { @@ -93,3 +111,431 @@ export const deleteAgreementEntry = async ( const command = new DeleteItemCommand(input); await dynamoDBClient.send(command); }; + +export const updateAgreementStateInPlatformStatesEntry = async ( + dynamoDBClient: DynamoDBClient, + primaryKey: PlatformStatesAgreementPK, + state: ItemState, + version: number +): Promise => { + const input: UpdateItemInput = { + ConditionExpression: "attribute_exists(PK)", + Key: { + PK: { + S: primaryKey, + }, + }, + ExpressionAttributeValues: { + ":newState": { + S: state, + }, + ":newVersion": { + N: version.toString(), + }, + ":newUpdateAt": { + S: new Date().toISOString(), + }, + }, + ExpressionAttributeNames: { + "#state": "state", + }, + UpdateExpression: + "SET #state = :newState, version = :newVersion, updatedAt = :newUpdateAt", + TableName: config.tokenGenerationReadModelTableNamePlatform, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const agreementStateToItemState = (state: AgreementState): ItemState => + state === agreementState.active ? itemState.active : itemState.inactive; + +export const updateAgreementStateOnTokenStatesEntries = async ({ + entriesToUpdate, + agreementState, + dynamoDBClient, +}: { + entriesToUpdate: TokenGenerationStatesClientPurposeEntry[]; + agreementState: AgreementState; + dynamoDBClient: DynamoDBClient; +}): Promise => { + for (const entry of entriesToUpdate) { + const input: UpdateItemInput = { + // ConditionExpression to avoid upsert + ConditionExpression: "attribute_exists(GSIPK_eserviceId_descriptorId)", + Key: { + PK: { + S: entry.PK, + }, + }, + ExpressionAttributeValues: { + ":newState": { + S: agreementStateToItemState(agreementState), + }, + ":newUpdateAt": { + S: new Date().toISOString(), + }, + }, + UpdateExpression: + "SET agreementState = :newState, updatedAt = :newUpdateAt", + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); + } +}; + +export const updateAgreementStateAndDescriptorInfoOnTokenStatesEntries = + async ({ + entriesToUpdate, + agreementState, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }: { + entriesToUpdate: TokenGenerationStatesClientPurposeEntry[]; + agreementState: AgreementState; + dynamoDBClient: DynamoDBClient; + GSIPK_eserviceId_descriptorId: GSIPKEServiceIdDescriptorId; + catalogEntry: PlatformStatesCatalogEntry | undefined; + }): Promise => { + for (const entry of entriesToUpdate) { + const additionalDescriptorInfo = + catalogEntry && + (!entry.GSIPK_eserviceId_descriptorId || !entry.descriptorState); + + const additionalAttributesToSet: Record = + additionalDescriptorInfo + ? { + ":descriptorState": { + S: catalogEntry.state, + }, + ":descriptorAudience": { + L: catalogEntry.descriptorAudience.map((item) => ({ + S: item, + })), + }, + ":descriptorVoucherLifespan": { + N: catalogEntry.descriptorVoucherLifespan.toString(), + }, + ":gsi": { + S: GSIPK_eserviceId_descriptorId, + }, + } + : {}; + const input: UpdateItemInput = { + // ConditionExpression to avoid upsert + ConditionExpression: "attribute_exists(PK)", + Key: { + PK: { + S: entry.PK, + }, + }, + ExpressionAttributeValues: { + ":newState": { + S: agreementStateToItemState(agreementState), + }, + ":newUpdateAt": { + S: new Date().toISOString(), + }, + ...additionalAttributesToSet, + }, + UpdateExpression: + "SET agreementState = :newState, updatedAt = :newUpdateAt".concat( + additionalDescriptorInfo + ? ", GSI_eservice_id_descriptor_id = :gsi, descriptorState = :descriptorState, descriptorAudience = :descriptorAudience, descriptorVoucherLifespan = :descriptorVoucherLifespan" + : "" + ), + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); + } + }; + +export const readPlatformStateAgreementEntriesByConsumerIdEserviceId = async ( + consumerId_eserviceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( + consumerId_eserviceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNamePlatform, + IndexName: "Agreement", + KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: consumerId_eserviceId }, + }, + ExclusiveStartKey: exclusiveStartKey, + ScanIndexForward: false, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read platform state agreement entries: result ${JSON.stringify( + data + )} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const agreementEntries = z + .array(PlatformStatesAgreementEntry) + .safeParse(unmarshalledItems); + + if (!agreementEntries.success) { + throw genericInternalError( + `Unable to parse platform state entry items: result ${JSON.stringify( + agreementEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + if (!data.LastEvaluatedKey) { + return agreementEntries.data; + } else { + return [ + ...agreementEntries.data, + ...(await runPaginatedQuery( + consumerId_eserviceId, + dynamoDBClient, + data.LastEvaluatedKey + )), + ]; + } + } + }; + + return await runPaginatedQuery( + consumerId_eserviceId, + dynamoDBClient, + undefined + ); +}; + +export const updateAgreementStateAndDescriptorInfoOnTokenStates = async ({ + GSIPK_consumerId_eserviceId, + agreementState, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, +}: { + GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId; + agreementState: AgreementState; + dynamoDBClient: DynamoDBClient; + GSIPK_eserviceId_descriptorId: GSIPKEServiceIdDescriptorId; + catalogEntry: PlatformStatesCatalogEntry | undefined; +}): Promise => { + const runPaginatedQuery = async ( + consumerId_eserviceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "Agreement", + KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: consumerId_eserviceId }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesClientPurposeEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + await updateAgreementStateAndDescriptorInfoOnTokenStatesEntries({ + entriesToUpdate: tokenStateEntries.data, + agreementState, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + + if (!data.LastEvaluatedKey) { + return tokenStateEntries.data; + } else { + return [ + ...tokenStateEntries.data, + ...(await runPaginatedQuery( + consumerId_eserviceId, + dynamoDBClient, + data.LastEvaluatedKey + )), + ]; + } + } + }; + + return await runPaginatedQuery( + GSIPK_consumerId_eserviceId, + dynamoDBClient, + undefined + ); +}; + +export const extractAgreementIdFromAgreementPK = ( + pk: PlatformStatesAgreementPK +): AgreementId => { + const substrings = pk.split("#"); + const agreementId = substrings[1]; + const result = AgreementId.safeParse(agreementId); + + if (!result.success) { + throw genericInternalError( + `Unable to parse agreement PK: result ${JSON.stringify( + result + )} - data ${JSON.stringify(agreementId)} ` + ); + } + return result.data; +}; + +export const updateAgreementStateOnTokenStates = async ({ + GSIPK_consumerId_eserviceId, + agreementState, + dynamoDBClient, +}: { + GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId; + agreementState: AgreementState; + dynamoDBClient: DynamoDBClient; +}): Promise => { + const runPaginatedQuery = async ( + consumerId_eserviceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "Agreement", + KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: consumerId_eserviceId }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesClientPurposeEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + await updateAgreementStateOnTokenStatesEntries({ + entriesToUpdate: tokenStateEntries.data, + agreementState, + dynamoDBClient, + }); + + if (!data.LastEvaluatedKey) { + return tokenStateEntries.data; + } else { + return [ + ...tokenStateEntries.data, + ...(await runPaginatedQuery( + consumerId_eserviceId, + dynamoDBClient, + data.LastEvaluatedKey + )), + ]; + } + } + }; + + return await runPaginatedQuery( + GSIPK_consumerId_eserviceId, + dynamoDBClient, + undefined + ); +}; + +export const readCatalogEntry = async ( + primaryKey: PlatformStatesEServiceDescriptorPK, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: GetItemInput = { + Key: { + PK: { S: primaryKey }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new GetItemCommand(input); + const data: GetItemCommandOutput = await dynamoDBClient.send(command); + + if (!data.Item) { + return undefined; + } else { + const unmarshalled = unmarshall(data.Item); + const catalogEntry = PlatformStatesCatalogEntry.safeParse(unmarshalled); + + if (!catalogEntry.success) { + throw genericInternalError( + `Unable to parse catalog entry item: result ${JSON.stringify( + catalogEntry + )} - data ${JSON.stringify(data)} ` + ); + } + return catalogEntry.data; + } +}; + +export const isLatestAgreement = async ( + GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId, + agreementId: AgreementId, + dynamoDBClient: DynamoDBClient +): Promise => { + const agreementEntries = + await readPlatformStateAgreementEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + if (agreementEntries.length === 0) { + return true; + } + const agreementIdFromEntry = extractAgreementIdFromAgreementPK( + agreementEntries[0].PK + ); + return agreementIdFromEntry === agreementId; +}; diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts new file mode 100644 index 0000000000..f1cdca56f4 --- /dev/null +++ b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -0,0 +1,3137 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { fail } from "assert"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { + Agreement, + AgreementActivatedV2, + AgreementArchivedByConsumerV2, + AgreementArchivedByUpgradeV2, + AgreementEventEnvelope, + AgreementSuspendedByConsumerV2, + AgreementSuspendedByPlatformV2, + AgreementSuspendedByProducerV2, + AgreementUnsuspendedByConsumerV2, + AgreementUnsuspendedByProducerV2, + AgreementUpgradedV2, + EServiceId, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, + TenantId, + TokenGenerationStatesClientPurposeEntry, + agreementState, + generateId, + itemState, + makeGSIPKConsumerIdEServiceId, + makePlatformStatesAgreementPK, + makePlatformStatesEServiceDescriptorPK, + makeTokenGenerationStatesClientKidPurposePK, + toAgreementV2, +} from "pagopa-interop-models"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + getMockTokenStatesClientPurposeEntry, + getMockAgreement, + buildDynamoDBTables, + deleteDynamoDBTables, + readTokenStateEntriesByConsumerIdEserviceId, + writeTokenStateEntry, + getMockAgreementEntry, + writeCatalogEntry, +} from "pagopa-interop-commons-test"; +import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; +import { handleMessageV2 } from "../src/consumerServiceV2.js"; +import { config, sleep } from "./utils.js"; + +describe("integration tests V2 events", async () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + const mockDate = new Date(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + describe("AgreementActivated", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementActivated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + version: 2, + }; + await writeAgreementEntry(previousStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toEqual(previousStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than existing table entry", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 3, + type: "AgreementActivated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + version: 2, + }; + await writeAgreementEntry(previousStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...previousStateEntry, + state: itemState.active, + version: 3, + updatedAt: new Date().toISOString(), + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should add the entry if it doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementActivated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: agreement.stamps.activation!.when.toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should add the entry if it doesn't exist - and add descriptor info to token-generation-states entry if missing", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementActivated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: agreement.stamps.activation!.when.toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + + it("should add the entry if it doesn't exist (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + + const payload: AgreementActivatedV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementActivated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: previousAgreement.eserviceId, + descriptorId: previousAgreement.descriptorId, + }); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: previousAgreement.consumerId, + eserviceId: previousAgreement.eserviceId, + }), + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: previousAgreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + }); + describe("AgreementSuspendedByProducer", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByProducerV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementSuspendedByProducer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByProducerV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementSuspendedByProducer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByProducerV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementSuspendedByProducer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + describe("AgreementSuspendedByConsumer", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByConsumerV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementSuspendedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByConsumerV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementSuspendedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByConsumerV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementSuspendedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + describe("AgreementSuspendedByPlatform", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByPlatformV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementSuspendedByPlatform", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByPlatformV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementSuspendedByPlatform", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByPlatformV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementSuspendedByPlatform", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + describe("AgreementUnsuspendedByProducer", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUnsuspendedByProducerV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementUnsuspendedByProducer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUnsuspendedByProducerV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementUnsuspendedByProducer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.active, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUnsuspendedByProducerV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementUnsuspendedByProducer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.active, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + + describe("AgreementUnsuspendedByConsumer", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUnsuspendedByConsumerV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementUnsuspendedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUnsuspendedByConsumerV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementUnsuspendedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.active, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUnsuspendedByConsumerV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementUnsuspendedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.active, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + + describe("AgreementUpgraded", async () => { + it("should do no operation if the table entry is more recent than incoming version", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpgradedV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 2, + type: "AgreementUpgraded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + version: 3, + }; + await writeAgreementEntry(previousStateEntry, dynamoDBClient); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedAgreementEntry).toEqual(previousStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than the table entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpgradedV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 2, + type: "AgreementUpgraded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: latestAgreement.eserviceId, + descriptorId: latestAgreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.active, + version: 2, + updatedAt: new Date().toISOString(), + }; + expect(retrievedEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than the table entry (agreement is not the latest -> no operation in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpgradedV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 2, + type: "AgreementUpgraded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: previousAgreement.eserviceId, + descriptorId: previousAgreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.active, + version: 2, + updatedAt: new Date().toISOString(), + }; + expect(retrievedEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + it("add the entry if it doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpgradedV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementUpgraded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: agreement.stamps.activation!.when.toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + }); + + describe("AgreementArchivedByUpgrade", () => { + it("should delete the entry (no update in token-generation-states)", async () => { + const consumerId = generateId(); + const eserviceId = generateId(); + + const agreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + archiving: { + when: new Date(), + who: generateId(), + }, + }, + }; + + const payload: AgreementArchivedByUpgradeV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementArchivedByUpgrade", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const agreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + }; + + await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: agreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: agreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + }); + + describe("AgreementArchivedByConsumer", () => { + it("should delete the entry if it exists (agreement is the latest -> includes operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementArchivedByConsumerV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementArchivedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should delete the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementArchivedByConsumerV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementArchivedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + }); +}); diff --git a/packages/agreement-platformstate-writer/test/utils.test.ts b/packages/agreement-platformstate-writer/test/utils.test.ts new file mode 100644 index 0000000000..67b950fc6d --- /dev/null +++ b/packages/agreement-platformstate-writer/test/utils.test.ts @@ -0,0 +1,650 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { fail } from "assert"; +import crypto from "crypto"; +import { + ConditionalCheckFailedException, + DynamoDBClient, +} from "@aws-sdk/client-dynamodb"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockAgreementEntry, + getMockTokenStatesClientPurposeEntry, + readAllTokenStateItems, + readTokenStateEntriesByConsumerIdEserviceId, + writeTokenStateEntry, +} from "pagopa-interop-commons-test"; +import { + makePlatformStatesAgreementPK, + generateId, + AgreementId, + itemState, + PlatformStatesAgreementEntry, + makeGSIPKConsumerIdEServiceId, + DescriptorId, + makePlatformStatesEServiceDescriptorPK, + agreementState, + makeTokenGenerationStatesClientKidPurposePK, + TokenGenerationStatesClientPurposeEntry, + makeGSIPKEServiceIdDescriptorId, + EServiceId, + PlatformStatesCatalogEntry, + TenantId, +} from "pagopa-interop-models"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { + updateAgreementStateInPlatformStatesEntry, + readAgreementEntry, + writeAgreementEntry, + deleteAgreementEntry, + agreementStateToItemState, + updateAgreementStateOnTokenStates, + updateAgreementStateAndDescriptorInfoOnTokenStates, + isLatestAgreement, +} from "../src/utils.js"; +import { config } from "./utils.js"; + +describe("utils", async () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + describe("updateAgreementStateInPlatformStatesEntry", async () => { + it("should throw error if previous entry doesn't exist", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + expect( + updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + itemState.active, + 1 + ) + ).rejects.toThrowError(ConditionalCheckFailedException); + const agreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + expect(agreementEntry).toBeUndefined(); + }); + + it("should update state if previous entry exists", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + const previousAgreementStateEntry = getMockAgreementEntry(primaryKey); + expect( + await readAgreementEntry(primaryKey, dynamoDBClient) + ).toBeUndefined(); + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + itemState.active, + 2 + ); + + const result = await readAgreementEntry(primaryKey, dynamoDBClient); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.active, + version: 2, + updatedAt: new Date().toISOString(), + }; + + expect(result).toEqual(expectedAgreementEntry); + }); + }); + + describe("writeAgreementEntry", async () => { + it("should throw error if previous entry exists", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + const agreementStateEntry = getMockAgreementEntry(primaryKey); + await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + expect( + writeAgreementEntry(agreementStateEntry, dynamoDBClient) + ).rejects.toThrowError(ConditionalCheckFailedException); + }); + + it("should write if previous entry doesn't exist", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const agreementStateEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: itemState.inactive, + version: 1, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: generateId(), + }; + + await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toEqual(agreementStateEntry); + }); + }); + + describe("readAgreementEntry", async () => { + it("should return undefined if entry doesn't exist", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + const agreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + expect(agreementEntry).toBeUndefined(); + }); + + it("should return entry if it exists", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const agreementStateEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: itemState.inactive, + version: 1, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: generateId(), + }; + await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + const retrievedEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + + expect(retrievedEntry).toEqual(agreementStateEntry); + }); + }); + + describe("deleteAgreementEntry", async () => { + it("should not throw error if previous entry doesn't exist", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + expect( + deleteAgreementEntry(primaryKey, dynamoDBClient) + ).resolves.not.toThrowError(); + }); + + it("should delete the entry if it exists", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const agreementStateEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: itemState.inactive, + version: 1, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: generateId(), + }; + await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + await deleteAgreementEntry(primaryKey, dynamoDBClient); + const retrievedAgreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + expect(retrievedAgreementEntry).toBeUndefined(); + }); + }); + + describe("agreementStateToItemState", async () => { + it.each([agreementState.active])( + "should convert %s state to active", + async (s) => { + expect(agreementStateToItemState(s)).toBe(itemState.active); + } + ); + + it.each([agreementState.archived, agreementState.suspended])( + "should convert %s state to inactive", + async (s) => { + expect(agreementStateToItemState(s)).toBe(itemState.inactive); + } + ); + }); + + describe("readTokenStateEntriesByConsumerIdEserviceId", async () => { + it("should return empty array if entries do not exist", async () => { + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const result = await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + expect(result).toEqual([]); + }); + + it("should return entries if they exist (no need for pagination)", async () => { + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const tokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(tokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(tokenStateEntry2, dynamoDBClient); + + const retrievedTokenEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenStateEntry1, tokenStateEntry2]) + ); + }); + + it("should return entries if they exist (with pagination)", async () => { + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + + const tokenEntriesLength = 10; + + const writtenEntries = []; + // eslint-disable-next-line functional/no-let + for (let i = 0; i < tokenEntriesLength; i++) { + const tokenStateEntryPK = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenStateEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_consumerId_eserviceId, + publicKey: crypto.randomBytes(100000).toString("hex"), + }; + await writeTokenStateEntry(tokenStateEntry, dynamoDBClient); + // eslint-disable-next-line functional/immutable-data + writtenEntries.push(tokenStateEntry); + } + vi.spyOn(dynamoDBClient, "send"); + const tokenEntries = await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(dynamoDBClient.send).toHaveBeenCalledTimes(2); + expect(tokenEntries).toHaveLength(tokenEntriesLength); + expect(tokenEntries).toEqual(expect.arrayContaining(writtenEntries)); + }); + }); + + describe("updateAgreementStateOnTokenStates", async () => { + it("should do nothing if previous entry doesn't exist", async () => { + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const tokenStateEntries = await readAllTokenStateItems(dynamoDBClient); + expect(tokenStateEntries).toEqual([]); + expect( + updateAgreementStateOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreementState.archived, + dynamoDBClient, + }) + ).resolves.not.toThrowError(); + const tokenStateEntriesAfterUpdate = await readAllTokenStateItems( + dynamoDBClient + ); + expect(tokenStateEntriesAfterUpdate).toEqual([]); + }); + + it("should update state if previous entries exist", async () => { + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await updateAgreementStateOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreementState.active, + dynamoDBClient, + }); + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + }); + + describe("updateAgreementStateAndDescriptorInfoOnTokenStates", async () => { + it("should do nothing if previous entry doesn't exist", async () => { + const eserviceId = generateId(); + const descriptorId = generateId(); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId, + }); + + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId: generateId(), + }); + + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId, + descriptorId, + }); + + const catalogEntry: PlatformStatesCatalogEntry = { + PK: pkCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + descriptorVoucherLifespan: 60, + version: 3, + updatedAt: new Date().toISOString(), + }; + + const tokenStateEntries = await readAllTokenStateItems(dynamoDBClient); + expect(tokenStateEntries).toEqual([]); + expect( + updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreementState.archived, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }) + ).resolves.not.toThrowError(); + const tokenStateEntriesAfterUpdate = await readAllTokenStateItems( + dynamoDBClient + ); + expect(tokenStateEntriesAfterUpdate).toEqual([]); + }); + + it("should update state if previous entries exist", async () => { + const eserviceId = generateId(); + const descriptorId = generateId(); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId, + }); + + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId: generateId(), + }); + + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId, + descriptorId, + }); + + const catalogEntry: PlatformStatesCatalogEntry = { + PK: pkCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + descriptorVoucherLifespan: 60, + version: 3, + updatedAt: new Date().toISOString(), + }; + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + descriptorState: undefined, + descriptorAudience: [], + descriptorVoucherLifespan: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + descriptorState: undefined, + descriptorAudience: [], + descriptorVoucherLifespan: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreementState.active, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + + describe("isLatestAgreement", () => { + it("should return true if the agreement is the latest", async () => { + const eserviceId = generateId(); + const consumerId = generateId(); + const agreementId1 = generateId(); + const agreementId2 = generateId(); + const now = new Date(); + const threeHoursAgo = new Date(); + threeHoursAgo.setHours(threeHoursAgo.getHours() - 3); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const agreementPK1 = makePlatformStatesAgreementPK(agreementId1); + const agreementPK2 = makePlatformStatesAgreementPK(agreementId2); + + const agreementEntry1: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementPK1, GSIPK_consumerId_eserviceId), + GSISK_agreementTimestamp: now.toISOString(), + state: itemState.active, + }; + + const agreementEntry2: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementPK2, GSIPK_consumerId_eserviceId), + GSISK_agreementTimestamp: threeHoursAgo.toISOString(), + state: itemState.inactive, + }; + + await writeAgreementEntry(agreementEntry1, dynamoDBClient); + await writeAgreementEntry(agreementEntry2, dynamoDBClient); + + expect( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreementId1, + dynamoDBClient + ) + ).toEqual(true); + + expect( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreementId2, + dynamoDBClient + ) + ).toEqual(false); + }); + + it("should return true if there are no other agreements", async () => { + const eserviceId = generateId(); + const consumerId = generateId(); + const agreementId1 = generateId(); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + expect( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreementId1, + dynamoDBClient + ) + ).toEqual(true); + }); + }); +}); diff --git a/packages/agreement-platformstate-writer/test/utils.ts b/packages/agreement-platformstate-writer/test/utils.ts new file mode 100644 index 0000000000..fdc2e3c2c5 --- /dev/null +++ b/packages/agreement-platformstate-writer/test/utils.ts @@ -0,0 +1,11 @@ +import { inject, vi } from "vitest"; + +export const config = inject("tokenGenerationReadModelConfig"); + +export const sleep = (ms: number, mockDate = new Date()): Promise => + new Promise((resolve) => { + vi.useRealTimers(); + setTimeout(resolve, ms); + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); diff --git a/packages/catalog-platformstate-writer/test/utils.test.ts b/packages/catalog-platformstate-writer/test/utils.test.ts index ed13803846..2caf1da813 100644 --- a/packages/catalog-platformstate-writer/test/utils.test.ts +++ b/packages/catalog-platformstate-writer/test/utils.test.ts @@ -48,8 +48,6 @@ describe("utils tests", async () => { fail(); } const dynamoDBClient = new DynamoDBClient({ - credentials: { accessKeyId: "key", secretAccessKey: "secret" }, - region: "eu-south-1", endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, }); beforeEach(async () => { diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 63d4569838..15a439945e 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -48,6 +48,9 @@ import { PurposeVersionId, ProducerKeychain, DescriptorState, + GSIPKConsumerIdEServiceId, + PlatformStatesAgreementEntry, + PlatformStatesAgreementPK, } from "pagopa-interop-models"; import { AuthData } from "pagopa-interop-commons"; import { z } from "zod"; @@ -368,3 +371,21 @@ export const getMockTokenStatesClientPurposeEntry = ( }), }; }; + +export const getMockAgreementEntry = ( + primaryKey: PlatformStatesAgreementPK, + GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId( + { + consumerId: generateId(), + eserviceId: generateId(), + } + ) +): PlatformStatesAgreementEntry => ({ + PK: primaryKey, + state: itemState.inactive, + version: 1, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: generateId(), +}); diff --git a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts index 4535e4b974..36a78b5c87 100644 --- a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts +++ b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts @@ -14,7 +14,9 @@ import { } from "@aws-sdk/client-dynamodb"; import { genericInternalError, + GSIPKConsumerIdEServiceId, GSIPKEServiceIdDescriptorId, + PlatformStatesCatalogEntry, TokenGenerationStatesClientPurposeEntry, } from "pagopa-interop-models"; import { unmarshall } from "@aws-sdk/util-dynamodb"; @@ -24,80 +26,110 @@ export const writeTokenStateEntry = async ( tokenStateEntry: TokenGenerationStatesClientPurposeEntry, dynamoDBClient: DynamoDBClient ): Promise => { - const agreementItems: Record = - tokenStateEntry.GSIPK_consumerId_eserviceId - ? { - agreementId: { - S: tokenStateEntry.agreementId!, - }, - agreementState: { - S: tokenStateEntry.agreementState!, - }, - GSIPK_consumerId_eserviceId: { - S: tokenStateEntry.GSIPK_consumerId_eserviceId, - }, - } - : {}; - const descriptorItems: Record = - tokenStateEntry.GSIPK_eserviceId_descriptorId - ? { - descriptorState: { - S: tokenStateEntry.descriptorState!, - }, - descriptorAudience: { - L: tokenStateEntry.descriptorAudience!.map((item) => ({ - S: item, - })), - }, - descriptorVoucherLifespan: { - N: tokenStateEntry.descriptorVoucherLifespan!.toString(), - }, - GSIPK_eserviceId_descriptorId: { - S: tokenStateEntry.GSIPK_eserviceId_descriptorId, - }, - } - : {}; - const items: Record = { - ...agreementItems, - ...descriptorItems, - PK: { - S: tokenStateEntry.PK, - }, - updatedAt: { - S: tokenStateEntry.updatedAt, - }, - consumerId: { - S: tokenStateEntry.consumerId, - }, - purposeVersionId: { - S: tokenStateEntry.purposeVersionId!, - }, - clientKind: { - S: tokenStateEntry.clientKind, - }, - publicKey: { - S: tokenStateEntry.publicKey, - }, - GSIPK_clientId: { - S: tokenStateEntry.GSIPK_clientId, - }, - GSIPK_kid: { - S: tokenStateEntry.GSIPK_kid, - }, - GSIPK_clientId_purposeId: { - S: tokenStateEntry.GSIPK_clientId_purposeId!, - }, - GSIPK_purposeId: { - S: tokenStateEntry.GSIPK_purposeId!, - }, - purposeState: { - S: tokenStateEntry.purposeState!, - }, - }; - const input: PutItemInput = { ConditionExpression: "attribute_not_exists(PK)", - Item: items, + Item: { + PK: { + S: tokenStateEntry.PK, + }, + ...(tokenStateEntry.descriptorState + ? { + descriptorState: { + S: tokenStateEntry.descriptorState, + }, + } + : {}), + ...(tokenStateEntry.descriptorAudience + ? { + descriptorAudience: { + L: tokenStateEntry.descriptorAudience.map((item) => ({ + S: item, + })), + }, + } + : {}), + ...(tokenStateEntry.descriptorVoucherLifespan + ? { + descriptorVoucherLifespan: { + N: tokenStateEntry.descriptorVoucherLifespan.toString(), + }, + } + : {}), + updatedAt: { + S: tokenStateEntry.updatedAt, + }, + consumerId: { + S: tokenStateEntry.consumerId, + }, + ...(tokenStateEntry.agreementId + ? { + agreementId: { + S: tokenStateEntry.agreementId, + }, + } + : {}), + ...(tokenStateEntry.purposeVersionId + ? { + purposeVersionId: { + S: tokenStateEntry.purposeVersionId, + }, + } + : {}), + ...(tokenStateEntry.GSIPK_consumerId_eserviceId + ? { + GSIPK_consumerId_eserviceId: { + S: tokenStateEntry.GSIPK_consumerId_eserviceId, + }, + } + : {}), + clientKind: { + S: tokenStateEntry.clientKind, + }, + publicKey: { + S: tokenStateEntry.publicKey, + }, + GSIPK_clientId: { + S: tokenStateEntry.GSIPK_clientId, + }, + GSIPK_kid: { + S: tokenStateEntry.GSIPK_kid, + }, + ...(tokenStateEntry.GSIPK_clientId_purposeId + ? { + GSIPK_clientId_purposeId: { + S: tokenStateEntry.GSIPK_clientId_purposeId, + }, + } + : {}), + ...(tokenStateEntry.agreementState + ? { + agreementState: { + S: tokenStateEntry.agreementState, + }, + } + : {}), + ...(tokenStateEntry.GSIPK_eserviceId_descriptorId + ? { + GSIPK_eserviceId_descriptorId: { + S: tokenStateEntry.GSIPK_eserviceId_descriptorId, + }, + } + : {}), + ...(tokenStateEntry.GSIPK_purposeId + ? { + GSIPK_purposeId: { + S: tokenStateEntry.GSIPK_purposeId, + }, + } + : {}), + ...(tokenStateEntry.purposeState + ? { + purposeState: { + S: tokenStateEntry.purposeState, + }, + } + : {}), + }, TableName: "token-generation-states", }; const command = new PutItemCommand(input); @@ -196,3 +228,99 @@ export const readTokenStateEntriesByEserviceIdAndDescriptorId = async ( undefined ); }; + +export const readTokenStateEntriesByConsumerIdEserviceId = async ( + consumerId_eserviceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( + consumerId_eserviceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: "token-generation-states", + IndexName: "Agreement", + KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: consumerId_eserviceId }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesClientPurposeEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + if (!data.LastEvaluatedKey) { + return tokenStateEntries.data; + } else { + return [ + ...tokenStateEntries.data, + ...(await runPaginatedQuery( + consumerId_eserviceId, + dynamoDBClient, + data.LastEvaluatedKey + )), + ]; + } + } + }; + + return await runPaginatedQuery( + consumerId_eserviceId, + dynamoDBClient, + undefined + ); +}; + +export const writeCatalogEntry = async ( + catalogEntry: PlatformStatesCatalogEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: catalogEntry.PK, + }, + state: { + S: catalogEntry.state, + }, + descriptorAudience: { + L: catalogEntry.descriptorAudience.map((item) => ({ + S: item, + })), + }, + descriptorVoucherLifespan: { + N: catalogEntry.descriptorVoucherLifespan.toString(), + }, + version: { + N: catalogEntry.version.toString(), + }, + updatedAt: { + S: catalogEntry.updatedAt, + }, + }, + TableName: "platform-states", + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; From 5bd798977b05b1242bfad19a315c5cda89c99854 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 31 Oct 2024 15:28:21 +0100 Subject: [PATCH 04/34] IMN-791 Add events service v1 in agreement-platformstate-writer (#1014) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../src/consumerServiceV1.ts | 347 ++- ...entPlatformstateWriter.integration.test.ts | 7 - .../consumerServiceV1.integration.test.ts | 2084 +++++++++++++++++ 3 files changed, 2425 insertions(+), 13 deletions(-) delete mode 100644 packages/agreement-platformstate-writer/test/agreementPlatformstateWriter.integration.test.ts create mode 100644 packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts index 8160ce4de1..3e3254bc4b 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts @@ -1,20 +1,99 @@ import { match } from "ts-pattern"; -import { AgreementEventEnvelopeV1 } from "pagopa-interop-models"; +import { + Agreement, + AgreementEventEnvelopeV1, + AgreementV1, + genericInternalError, + fromAgreementV1, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makePlatformStatesAgreementPK, + makePlatformStatesEServiceDescriptorPK, + PlatformStatesAgreementEntry, + agreementState, + PlatformStatesCatalogEntry, +} from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + readAgreementEntry, + updateAgreementStateInPlatformStatesEntry, + agreementStateToItemState, + updateAgreementStateOnTokenStates, + writeAgreementEntry, + readCatalogEntry, + updateAgreementStateAndDescriptorInfoOnTokenStates, + deleteAgreementEntry, + isLatestAgreement, +} from "./utils.js"; export async function handleMessageV1( message: AgreementEventEnvelopeV1, - _dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient ): Promise { await match(message) + .with({ type: "AgreementActivated" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + await handleFirstActivation(agreement, dynamoDBClient, msg.version); + }) + .with({ type: "AgreementSuspended" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + await handleActivationOrSuspension( + agreement, + dynamoDBClient, + msg.version + ); + }) + .with({ type: "AgreementUpdated" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + + await match(agreement.state) + // eslint-disable-next-line sonarjs/no-identical-functions + .with(agreementState.active, agreementState.suspended, async () => { + const agreement = parseAgreement(msg.data.agreement); + await handleActivationOrSuspension( + agreement, + dynamoDBClient, + msg.version + ); + }) + .with(agreementState.archived, async () => { + const agreement = parseAgreement(msg.data.agreement); + await handleArchiving(agreement, dynamoDBClient); + }) + .with( + agreementState.draft, + agreementState.missingCertifiedAttributes, + agreementState.pending, + agreementState.rejected, + () => Promise.resolve() + ) + .exhaustive(); + }) + .with({ type: "AgreementAdded" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + + await match(agreement.state) + // eslint-disable-next-line sonarjs/no-identical-functions + .with(agreementState.active, async () => { + // this case is for agreement upgraded + const agreement = parseAgreement(msg.data.agreement); + await handleUpgrade(agreement, dynamoDBClient, msg.version); + }) + .with( + agreementState.draft, + agreementState.archived, + agreementState.missingCertifiedAttributes, + agreementState.pending, + agreementState.rejected, + agreementState.suspended, + () => Promise.resolve() + ) + .exhaustive(); + }) .with( - { type: "AgreementAdded" }, - { type: "AgreementActivated" }, - { type: "AgreementSuspended" }, { type: "AgreementDeactivated" }, { type: "AgreementDeleted" }, { type: "VerifiedAttributeUpdated" }, - { type: "AgreementUpdated" }, { type: "AgreementConsumerDocumentAdded" }, { type: "AgreementConsumerDocumentRemoved" }, { type: "AgreementContractAdded" }, @@ -22,3 +101,259 @@ export async function handleMessageV1( ) .exhaustive(); } + +const parseAgreement = (agreementV1: AgreementV1 | undefined): Agreement => { + if (!agreementV1) { + throw genericInternalError(`Agreement not found in message data`); + } + + return fromAgreementV1(agreementV1); +}; + +const handleFirstActivation = async ( + agreement: Agreement, + dynamoDBClient: DynamoDBClient, + incomingVersion: number +): Promise => { + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + + const existingAgreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if (existingAgreementEntry) { + if (existingAgreementEntry.version > incomingVersion) { + // Stops processing if the message is older than the agreement entry + return Promise.resolve(); + } else { + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + agreementStateToItemState(agreement.state), + incomingVersion + ); + } + } else { + const agreementEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: agreementStateToItemState(agreement.state), + version: incomingVersion, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + + await writeAgreementEntry(agreementEntry, dynamoDBClient); + } + + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + const catalogEntry = await readCatalogEntry(pkCatalogEntry, dynamoDBClient); + + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + // token-generation-states + await updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + } +}; + +const handleActivationOrSuspension = async ( + agreement: Agreement, + dynamoDBClient: DynamoDBClient, + incomingVersion: number +): Promise => { + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + + const existingAgreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if (existingAgreementEntry) { + if (existingAgreementEntry.version > incomingVersion) { + // Stops processing if the message is older than the agreement entry + return Promise.resolve(); + } else { + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + agreementStateToItemState(agreement.state), + incomingVersion + ); + } + } + + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry = await readCatalogEntry(pkCatalogEntry, dynamoDBClient); + + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + // token-generation-states + await updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + } +}; + +const handleArchiving = async ( + agreement: Agreement, + dynamoDBClient: DynamoDBClient +): Promise => { + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + // token-generation-states only if agreement is the latest + + await updateAgreementStateOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + }); + } + + await deleteAgreementEntry(primaryKey, dynamoDBClient); +}; + +const handleUpgrade = async ( + agreement: Agreement, + dynamoDBClient: DynamoDBClient, + msgVersion: number +): Promise => { + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const agreementEntry = await readAgreementEntry(primaryKey, dynamoDBClient); + + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry = await readCatalogEntry(pkCatalogEntry, dynamoDBClient); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if (agreementEntry) { + if (agreementEntry.version > msgVersion) { + return Promise.resolve(); + } else { + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + agreementStateToItemState(agreement.state), + msgVersion + ); + } + } else { + const newAgreementEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: agreementStateToItemState(agreement.state), + version: msgVersion, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + + await writeAgreementEntry(newAgreementEntry, dynamoDBClient); + } + + const updateLatestAgreementOnTokenStates = async ( + catalogEntry: PlatformStatesCatalogEntry | undefined + ): Promise => { + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + // token-generation-states only if agreement is the latest + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + await updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + } + }; + + await updateLatestAgreementOnTokenStates(catalogEntry); + + const secondRetrievalCatalogEntry = await readCatalogEntry( + pkCatalogEntry, + dynamoDBClient + ); + if ( + secondRetrievalCatalogEntry && + (!catalogEntry || secondRetrievalCatalogEntry.state !== catalogEntry.state) + ) { + await updateLatestAgreementOnTokenStates(secondRetrievalCatalogEntry); + } +}; diff --git a/packages/agreement-platformstate-writer/test/agreementPlatformstateWriter.integration.test.ts b/packages/agreement-platformstate-writer/test/agreementPlatformstateWriter.integration.test.ts deleted file mode 100644 index e110c51648..0000000000 --- a/packages/agreement-platformstate-writer/test/agreementPlatformstateWriter.integration.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { describe, expect, it } from "vitest"; - -describe("sample", () => { - it("test", () => { - expect(1).toBe(1); - }); -}); diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts new file mode 100644 index 0000000000..339672bc55 --- /dev/null +++ b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -0,0 +1,2084 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { fail } from "assert"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { + Agreement, + AgreementActivatedV1, + AgreementAddedV1, + AgreementEventEnvelope, + AgreementUpdatedV1, + EServiceId, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, + TenantId, + TokenGenerationStatesClientPurposeEntry, + agreementState, + generateId, + itemState, + makeGSIPKConsumerIdEServiceId, + makePlatformStatesAgreementPK, + makePlatformStatesEServiceDescriptorPK, + makeTokenGenerationStatesClientKidPurposePK, +} from "pagopa-interop-models"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + getMockTokenStatesClientPurposeEntry, + getMockAgreement, + buildDynamoDBTables, + deleteDynamoDBTables, + toAgreementV1, + readTokenStateEntriesByConsumerIdEserviceId, + getMockAgreementEntry, + writeCatalogEntry, + writeTokenStateEntry, +} from "pagopa-interop-commons-test"; +import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; +import { handleMessageV1 } from "../src/consumerServiceV1.js"; +import { config, sleep } from "./utils.js"; + +describe("integration tests V1 events", async () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + const mockDate = new Date(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + describe("AgreementActivated", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementActivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + version: 2, + }; + await writeAgreementEntry(previousStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toEqual(previousStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than existing table entry", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 3, + type: "AgreementActivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + version: 2, + }; + await writeAgreementEntry(previousStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...previousStateEntry, + state: itemState.active, + version: 3, + updatedAt: new Date().toISOString(), + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should add the entry if it doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementActivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: agreement.stamps.activation!.when.toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should add the entry if it doesn't exist - and add descriptor info to token-generation-states entry if missing", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementActivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: agreement.stamps.activation!.when.toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should add the entry if it doesn't exist (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + + const payload: AgreementActivatedV1 = { + agreement: toAgreementV1(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementActivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: previousAgreement.eserviceId, + descriptorId: previousAgreement.descriptorId, + }); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: previousAgreement.consumerId, + eserviceId: previousAgreement.eserviceId, + }), + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: previousAgreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + }); + describe("AgreementAdded (upgrade)", async () => { + it("should do no operation if the table entry is more recent than incoming version", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementAddedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 2, + type: "AgreementAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + version: 3, + }; + await writeAgreementEntry(previousStateEntry, dynamoDBClient); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedAgreementEntry).toEqual(previousStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than the table entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementAddedV1 = { + agreement: toAgreementV1(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 2, + type: "AgreementAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: latestAgreement.eserviceId, + descriptorId: latestAgreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.active, + version: 2, + updatedAt: new Date().toISOString(), + }; + expect(retrievedEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than the table entry (agreement is not the latest -> no operation in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementAddedV1 = { + agreement: toAgreementV1(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 2, + type: "AgreementAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: previousAgreement.eserviceId, + descriptorId: previousAgreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.active, + version: 2, + updatedAt: new Date().toISOString(), + }; + expect(retrievedEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + it("add the entry if it doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementAddedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: agreement.stamps.activation!.when.toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + }); + + describe("AgreementUpdated (suspended by producer)", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + + describe("AgreementUpdated (unsuspended by producer)", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.active, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.active, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + + describe("Agreement Updated (archived by consumer or by upgrade)", () => { + it("agreement is the latest (includes operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + archiving: { + when: new Date(), + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("agreement is not the latest (no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + }); +}); From 59a572d7c7ba1c16c2ec2dc9bc1700236e0449d4 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Tue, 5 Nov 2024 14:38:31 +0100 Subject: [PATCH 05/34] IMN-784 - Fixing attribute duplicate errors (#1136) --- .../src/model/domain/errors.ts | 15 +++++- .../src/services/attributeRegistryService.ts | 17 ++++-- ...tributeRegistryService.integration.test.ts | 53 +++++++++++++------ 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/packages/attribute-registry-process/src/model/domain/errors.ts b/packages/attribute-registry-process/src/model/domain/errors.ts index 7972d7f8ed..16eb6e43cd 100644 --- a/packages/attribute-registry-process/src/model/domain/errors.ts +++ b/packages/attribute-registry-process/src/model/domain/errors.ts @@ -24,16 +24,27 @@ export function attributeNotFound(identifier: string): ApiError { }); } -export function attributeDuplicate( +export function attributeDuplicateByName( attributeName: string ): ApiError { return new ApiError({ - detail: `ApiError during Attribute creation with name ${attributeName}`, + detail: `An attribute with name ${attributeName} already exists`, code: "attributeDuplicate", title: "Duplicated attribute name", }); } +export function attributeDuplicateByNameAndCode( + attributeName: string, + attributeCode: string +): ApiError { + return new ApiError({ + detail: `An attribute with name ${attributeName} and code ${attributeCode} already exists`, + code: "attributeDuplicate", + title: "Duplicated attribute name and code", + }); +} + export function originNotCompliant(origin: string): ApiError { return new ApiError({ detail: `Requester origin ${origin} is not allowed`, diff --git a/packages/attribute-registry-process/src/services/attributeRegistryService.ts b/packages/attribute-registry-process/src/services/attributeRegistryService.ts index 57f20ab31d..4c4b9a37f0 100644 --- a/packages/attribute-registry-process/src/services/attributeRegistryService.ts +++ b/packages/attribute-registry-process/src/services/attributeRegistryService.ts @@ -20,7 +20,8 @@ import { attributeRegistryApi } from "pagopa-interop-api-clients"; import { toCreateEventAttributeAdded } from "../model/domain/toEvent.js"; import { OrganizationIsNotACertifier, - attributeDuplicate, + attributeDuplicateByName, + attributeDuplicateByNameAndCode, attributeNotFound, originNotCompliant, tenantNotFound, @@ -141,7 +142,7 @@ export function attributeRegistryServiceBuilder( apiDeclaredAttributeSeed.name ); if (attributeWithSameName) { - throw attributeDuplicate(apiDeclaredAttributeSeed.name); + throw attributeDuplicateByName(apiDeclaredAttributeSeed.name); } const newDeclaredAttribute: Attribute = { @@ -182,7 +183,7 @@ export function attributeRegistryServiceBuilder( apiVerifiedAttributeSeed.name ); if (attributeWithSameName) { - throw attributeDuplicate(apiVerifiedAttributeSeed.name); + throw attributeDuplicateByName(apiVerifiedAttributeSeed.name); } const newVerifiedAttribute: Attribute = { @@ -230,7 +231,10 @@ export function attributeRegistryServiceBuilder( ]); if (attributeWithSameName) { - throw attributeDuplicate(apiCertifiedAttributeSeed.name); + throw attributeDuplicateByNameAndCode( + apiCertifiedAttributeSeed.name, + apiCertifiedAttributeSeed.code + ); } const newCertifiedAttribute: Attribute = { @@ -270,7 +274,10 @@ export function attributeRegistryServiceBuilder( apiInternalCertifiedAttributeSeed.name ); if (attributeWithSameNameAndCode) { - throw attributeDuplicate(apiInternalCertifiedAttributeSeed.name); + throw attributeDuplicateByNameAndCode( + apiInternalCertifiedAttributeSeed.name, + apiInternalCertifiedAttributeSeed.code + ); } const newInternalCertifiedAttribute: Attribute = { diff --git a/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts b/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts index 1064f5ddd2..b33fee3535 100644 --- a/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts +++ b/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts @@ -18,7 +18,8 @@ import { } from "pagopa-interop-models"; import { OrganizationIsNotACertifier, - attributeDuplicate, + attributeDuplicateByName, + attributeDuplicateByNameAndCode, originNotCompliant, tenantNotFound, } from "../src/model/domain/errors.js"; @@ -99,16 +100,17 @@ describe("database test", () => { ) ).rejects.toThrowError(originNotCompliant("not-allowed-origin")); }); - it("should throw attributeDuplicate if an attribute with the same name already exists", async () => { + it("should throw attributeDuplicate if an attribute with the same name already exists, case insensitive", async () => { const attribute = { ...mockAttribute, + name: mockAttribute.name.toUpperCase(), kind: attributeKind.declared, }; await addOneAttribute(attribute); expect( attributeRegistryService.createDeclaredAttribute( { - name: attribute.name, + name: attribute.name.toLowerCase(), description: attribute.description, }, { @@ -118,7 +120,9 @@ describe("database test", () => { serviceName: "", } ) - ).rejects.toThrowError(attributeDuplicate(attribute.name)); + ).rejects.toThrowError( + attributeDuplicateByName(attribute.name.toLowerCase()) + ); }); }); describe("verified attribute creation", () => { @@ -185,16 +189,17 @@ describe("database test", () => { ) ).rejects.toThrowError(originNotCompliant("not-allowed-origin")); }); - it("should throw attributeDuplicate if an attribute with the same name already exists", async () => { + it("should throw attributeDuplicate if an attribute with the same name already exists, case insensitive", async () => { const attribute = { ...mockAttribute, + name: mockAttribute.name.toUpperCase(), kind: attributeKind.verified, }; await addOneAttribute(attribute); expect( attributeRegistryService.createVerifiedAttribute( { - name: attribute.name, + name: attribute.name.toLowerCase(), description: attribute.description, }, { @@ -204,7 +209,9 @@ describe("database test", () => { serviceName: "", } ) - ).rejects.toThrowError(attributeDuplicate(attribute.name)); + ).rejects.toThrowError( + attributeDuplicateByName(attribute.name.toLowerCase()) + ); }); }); describe("certified attribute creation", () => { @@ -263,10 +270,11 @@ describe("database test", () => { ); expect(writtenPayload.attribute).toEqual(toAttributeV1(attribute)); }); - it("should throw attributeDuplicate if an attribute with the same name and code already exists", async () => { + it("should throw attributeDuplicate if an attribute with the same name and code already exists, case insensitive", async () => { const attribute = { ...mockAttribute, - code: "123456", + name: mockAttribute.name.toUpperCase(), + code: "123456AB", }; const tenant: Tenant = { @@ -284,8 +292,8 @@ describe("database test", () => { expect( attributeRegistryService.createCertifiedAttribute( { - name: attribute.name, - code: attribute.code, + name: attribute.name.toLowerCase(), + code: attribute.code.toLowerCase(), description: attribute.description, }, { @@ -295,7 +303,12 @@ describe("database test", () => { serviceName: "", } ) - ).rejects.toThrowError(attributeDuplicate(attribute.name)); + ).rejects.toThrowError( + attributeDuplicateByNameAndCode( + attribute.name.toLowerCase(), + attribute.code.toLowerCase() + ) + ); }); it("should throw OrganizationIsNotACertifier if the organization is not a certifier", async () => { await addOneTenant(mockTenant); @@ -391,10 +404,11 @@ describe("database test", () => { ); expect(writtenPayload.attribute).toEqual(toAttributeV1(attribute)); }); - it("should throw attributeDuplicate if an attribute with the same name and code already exists", async () => { + it("should throw attributeDuplicate if an attribute with the same name and code already exists, case insensitive", async () => { const attribute = { ...mockAttribute, - code: "123456", + name: mockAttribute.name.toUpperCase(), + code: "123456AB", }; const tenant: Tenant = { @@ -412,8 +426,8 @@ describe("database test", () => { expect( attributeRegistryService.createInternalCertifiedAttribute( { - name: attribute.name, - code: attribute.code, + name: attribute.name.toLowerCase(), + code: attribute.code.toLowerCase(), origin: tenant.features[0].certifierId, description: attribute.description, }, @@ -424,7 +438,12 @@ describe("database test", () => { serviceName: "", } ) - ).rejects.toThrowError(attributeDuplicate(attribute.name)); + ).rejects.toThrowError( + attributeDuplicateByNameAndCode( + attribute.name.toLowerCase(), + attribute.code.toLowerCase() + ) + ); }); }); }); From ab69f335ad73580cabbb426a8535704e12a2db4c Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Tue, 5 Nov 2024 15:12:40 +0100 Subject: [PATCH 06/34] IMN-639 - Creating JWKS clients only once at services startup (#1131) --- .../bff/authorization/get Session Token.bru | 2 +- collections/environments/PagoPA local.bru | 4 ++-- packages/agreement-process/src/app.ts | 5 ++++- packages/api-gateway/src/app.ts | 5 ++++- packages/attribute-registry-process/src/app.ts | 5 ++++- packages/authorization-process/src/app.ts | 5 ++++- packages/backend-for-frontend/src/app.ts | 15 ++++++++++++--- .../src/routers/authorizationRouter.ts | 7 +++++-- .../src/routers/supportRouter.ts | 7 +++++-- .../src/services/authorizationService.ts | 7 +++---- packages/catalog-process/src/app.ts | 5 ++++- .../commons/src/auth/authenticationMiddleware.ts | 9 ++++----- packages/commons/src/auth/jwk.ts | 6 +++--- packages/purpose-process/src/app.ts | 5 ++++- packages/tenant-process/src/app.ts | 5 ++++- 15 files changed, 63 insertions(+), 29 deletions(-) diff --git a/collections/bff/authorization/get Session Token.bru b/collections/bff/authorization/get Session Token.bru index 80a82bb2e4..7a2e334f80 100644 --- a/collections/bff/authorization/get Session Token.bru +++ b/collections/bff/authorization/get Session Token.bru @@ -17,7 +17,7 @@ headers { body:json { { - "identity_token": {{JWT}} + "identity_token": "{{process.env.JWT}}" } } diff --git a/collections/environments/PagoPA local.bru b/collections/environments/PagoPA local.bru index 2a084277c7..db3d9de5bb 100644 --- a/collections/environments/PagoPA local.bru +++ b/collections/environments/PagoPA local.bru @@ -1,6 +1,6 @@ vars { host-agreement: http://localhost:3100 - JWT: {{process.env.JWT}} + JWT: Bearer {{process.env.JWT}} host-catalog: http://localhost:3000 correlation-id: 79eb4963-3866-422f-8ef0-87871e5f9a71 JWT-Invalid: @@ -10,5 +10,5 @@ vars { host-purpose: http://localhost:3400 host-authorization: http://localhost:3300 host-api-gw: http://localhost:3700/api-gateway/0.0 - JWT-M2M: {{process.env.JWT-M2M}} + JWT-M2M: Bearer {{process.env.JWT-M2M}} } diff --git a/packages/agreement-process/src/app.ts b/packages/agreement-process/src/app.ts index d56584d362..7b22271360 100644 --- a/packages/agreement-process/src/app.ts +++ b/packages/agreement-process/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, loggerMiddleware, zodiosCtx, @@ -12,13 +13,15 @@ const serviceName = "agreement-process"; const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + // Disable the "X-Powered-By: Express" HTTP header for security reasons. // See https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#recommendation_16 app.disable("x-powered-by"); app.use(healthRouter); app.use(contextMiddleware(serviceName)); -app.use(authenticationMiddleware(config)); +app.use(authenticationMiddleware(config, jwksClients)); app.use(loggerMiddleware(serviceName)); app.use(agreementRouter(zodiosCtx)); diff --git a/packages/api-gateway/src/app.ts b/packages/api-gateway/src/app.ts index a1e1f1c974..b9c16f7157 100644 --- a/packages/api-gateway/src/app.ts +++ b/packages/api-gateway/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, initRedisRateLimiter, loggerMiddleware, @@ -17,6 +18,8 @@ const clients = getInteropBeClients(); const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + const redisRateLimiter = await initRedisRateLimiter({ limiterGroup: "API_GW", maxRequests: config.rateLimiterMaxRequests, @@ -37,7 +40,7 @@ app.use( `/api-gateway/${config.apiGatewayInterfaceVersion}`, healthRouter, contextMiddleware(serviceName, false), - authenticationMiddleware(config), + authenticationMiddleware(config, jwksClients), // Authenticated routes - rate limiter relies on auth data to work rateLimiterMiddleware(redisRateLimiter), apiGatewayRouter(zodiosCtx, clients) diff --git a/packages/attribute-registry-process/src/app.ts b/packages/attribute-registry-process/src/app.ts index 0f5886ed61..4ae8948925 100644 --- a/packages/attribute-registry-process/src/app.ts +++ b/packages/attribute-registry-process/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, loggerMiddleware, zodiosCtx, @@ -12,13 +13,15 @@ const serviceName = "attribute-registry-process"; const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + // Disable the "X-Powered-By: Express" HTTP header for security reasons. // See https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#recommendation_16 app.disable("x-powered-by"); app.use(healthRouter); app.use(contextMiddleware(serviceName)); -app.use(authenticationMiddleware(config)); +app.use(authenticationMiddleware(config, jwksClients)); app.use(loggerMiddleware(serviceName)); app.use(attributeRouter(zodiosCtx)); diff --git a/packages/authorization-process/src/app.ts b/packages/authorization-process/src/app.ts index d7cb5b9671..8ef53acabb 100644 --- a/packages/authorization-process/src/app.ts +++ b/packages/authorization-process/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, zodiosCtx, } from "pagopa-interop-commons"; @@ -11,13 +12,15 @@ const serviceName = "authorization-process"; const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + // Disable the "X-Powered-By: Express" HTTP header for security reasons. // See https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#recommendation_16 app.disable("x-powered-by"); app.use(healthRouter); app.use(contextMiddleware(serviceName)); -app.use(authenticationMiddleware(config)); +app.use(authenticationMiddleware(config, jwksClients)); app.use(authorizationRouter(zodiosCtx)); export default app; diff --git a/packages/backend-for-frontend/src/app.ts b/packages/backend-for-frontend/src/app.ts index 7bb8946839..c0413917ee 100644 --- a/packages/backend-for-frontend/src/app.ts +++ b/packages/backend-for-frontend/src/app.ts @@ -6,6 +6,7 @@ import { zodiosCtx, initRedisRateLimiter, rateLimiterMiddleware, + buildJwksClients, } from "pagopa-interop-commons"; import express from "express"; import { config } from "./config/config.js"; @@ -37,6 +38,8 @@ const clients = getInteropBeClients(); const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + const redisRateLimiter = await initRedisRateLimiter({ limiterGroup: "BFF", maxRequests: config.rateLimiterMaxRequests, @@ -66,8 +69,14 @@ app.use( `/backend-for-frontend/${config.backendForFrontendInterfaceVersion}`, healthRouter, contextMiddleware(serviceName, false), - authorizationRouter(zodiosCtx, clients, allowList, redisRateLimiter), - authenticationMiddleware(config), + authorizationRouter( + zodiosCtx, + clients, + allowList, + redisRateLimiter, + jwksClients + ), + authenticationMiddleware(config, jwksClients), // Authenticated routes - rate limiter relies on auth data to work rateLimiterMiddleware(redisRateLimiter), catalogRouter(zodiosCtx, clients, fileManager), @@ -75,7 +84,7 @@ app.use( purposeRouter(zodiosCtx, clients), agreementRouter(zodiosCtx, clients, fileManager), selfcareRouter(clients, zodiosCtx), - supportRouter(zodiosCtx, clients, redisRateLimiter), + supportRouter(zodiosCtx, clients, redisRateLimiter, jwksClients), toolRouter(zodiosCtx, clients), tenantRouter(zodiosCtx, clients), clientRouter(zodiosCtx, clients), diff --git a/packages/backend-for-frontend/src/routers/authorizationRouter.ts b/packages/backend-for-frontend/src/routers/authorizationRouter.ts index ecbb4baa0f..98dcadf818 100644 --- a/packages/backend-for-frontend/src/routers/authorizationRouter.ts +++ b/packages/backend-for-frontend/src/routers/authorizationRouter.ts @@ -8,6 +8,7 @@ import { zodiosValidationErrorToApiProblem, RateLimiter, rateLimiterHeadersFromStatus, + buildJwksClients, } from "pagopa-interop-commons"; import { tooManyRequestsError } from "pagopa-interop-models"; import { makeApiProblem } from "../model/errors.js"; @@ -21,7 +22,8 @@ const authorizationRouter = ( ctx: ZodiosContext, { tenantProcessClient }: PagoPAInteropBeClients, allowList: string[], - rateLimiter: RateLimiter + rateLimiter: RateLimiter, + jwksClients: ReturnType ): ZodiosRouter => { const authorizationRouter = ctx.router(bffApi.authorizationApi.api, { validationErrorHandler: zodiosValidationErrorToApiProblem, @@ -32,7 +34,8 @@ const authorizationRouter = ( interopTokenGenerator, tenantProcessClient, allowList, - rateLimiter + rateLimiter, + jwksClients ); authorizationRouter diff --git a/packages/backend-for-frontend/src/routers/supportRouter.ts b/packages/backend-for-frontend/src/routers/supportRouter.ts index 847024ea0f..e4dd117675 100644 --- a/packages/backend-for-frontend/src/routers/supportRouter.ts +++ b/packages/backend-for-frontend/src/routers/supportRouter.ts @@ -1,6 +1,7 @@ import { ZodiosEndpointDefinitions } from "@zodios/core"; import { ZodiosRouter } from "@zodios/express"; import { + buildJwksClients, ExpressContext, InteropTokenGenerator, RateLimiter, @@ -19,7 +20,8 @@ import { fromBffAppContext } from "../utilities/context.js"; const supportRouter = ( ctx: ZodiosContext, { tenantProcessClient }: PagoPAInteropBeClients, - rateLimiter: RateLimiter + rateLimiter: RateLimiter, + jwksClients: ReturnType ): ZodiosRouter => { const supportRouter = ctx.router(bffApi.supportApi.api, { validationErrorHandler: zodiosValidationErrorToApiProblem, @@ -30,7 +32,8 @@ const supportRouter = ( interopTokenGenerator, tenantProcessClient, config.tenantAllowedOrigins, - rateLimiter + rateLimiter, + jwksClients ); supportRouter.post("/session/saml2/tokens", async (req, res) => { diff --git a/packages/backend-for-frontend/src/services/authorizationService.ts b/packages/backend-for-frontend/src/services/authorizationService.ts index a5ae88b26c..4c317539fa 100644 --- a/packages/backend-for-frontend/src/services/authorizationService.ts +++ b/packages/backend-for-frontend/src/services/authorizationService.ts @@ -16,7 +16,7 @@ import { USER_ROLES, WithLogger, decodeJwtToken, - getJwksClients, + buildJwksClients, userRoles, verifyJwtToken, } from "pagopa-interop-commons"; @@ -52,10 +52,9 @@ export function authorizationServiceBuilder( interopTokenGenerator: InteropTokenGenerator, tenantProcessClient: PagoPAInteropBeClients["tenantProcessClient"], allowList: string[], - rateLimiter: RateLimiter + rateLimiter: RateLimiter, + jwksClients: ReturnType ) { - const jwksClients = getJwksClients(config); - const readJwt = async ( identityToken: string, logger: Logger diff --git a/packages/catalog-process/src/app.ts b/packages/catalog-process/src/app.ts index 830ec7acdb..7966b61e53 100644 --- a/packages/catalog-process/src/app.ts +++ b/packages/catalog-process/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, loggerMiddleware, zodiosCtx, @@ -12,13 +13,15 @@ const serviceName = "catalog-process"; const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + // Disable the "X-Powered-By: Express" HTTP header for security reasons. // See https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#recommendation_16 app.disable("x-powered-by"); app.use(healthRouter); app.use(contextMiddleware(serviceName)); -app.use(authenticationMiddleware(config)); +app.use(authenticationMiddleware(config, jwksClients)); app.use(loggerMiddleware(serviceName)); app.use(eservicesRouter(zodiosCtx)); diff --git a/packages/commons/src/auth/authenticationMiddleware.ts b/packages/commons/src/auth/authenticationMiddleware.ts index aa75eda7c9..dcc081a86e 100644 --- a/packages/commons/src/auth/authenticationMiddleware.ts +++ b/packages/commons/src/auth/authenticationMiddleware.ts @@ -5,10 +5,10 @@ import { unauthorizedError, } from "pagopa-interop-models"; import { match } from "ts-pattern"; +import { JwksClient } from "jwks-rsa"; import { ExpressContext, fromAppContext, - getJwksClients, JWTConfig, jwtFromAuthHeader, } from "../index.js"; @@ -18,17 +18,16 @@ import { readAuthDataFromJwtToken, verifyJwtToken } from "./jwt.js"; const makeApiProblem = makeApiProblemBuilder({}); export const authenticationMiddleware: ( - config: JWTConfig + config: JWTConfig, + jwksClients: JwksClient[] ) => ZodiosRouterContextRequestHandler = - (config: JWTConfig) => + (config: JWTConfig, jwksClients: JwksClient[]) => async (req, res, next): Promise => { // We assume that: // - contextMiddleware already set ctx.serviceName and ctx.correlationId const ctx = fromAppContext(req.ctx); try { - const jwksClients = getJwksClients(config); - const jwtToken = jwtFromAuthHeader(req, ctx.logger); const valid = await verifyJwtToken( jwtToken, diff --git a/packages/commons/src/auth/jwk.ts b/packages/commons/src/auth/jwk.ts index a38ef5ff67..4ac6dd3567 100644 --- a/packages/commons/src/auth/jwk.ts +++ b/packages/commons/src/auth/jwk.ts @@ -67,14 +67,14 @@ export function sortJWK(jwk: JsonWebKey): JsonWebKey { ); } -export function getJwksClients(config: JWTConfig): JwksClient[] { +export function buildJwksClients(config: JWTConfig): JwksClient[] { return config.wellKnownUrls.map((url) => jwksClient({ cache: true, rateLimit: true, jwksUri: url, - /* If JWKS_CACHE_MAX_AGE_MILLIS not provided using 10 minute like default value: - https://github.com/auth0/node-jwks-rsa/blob/master/EXAMPLES.md#configuration + /* If JWKS_CACHE_MAX_AGE_MILLIS not provided using 10 minutes as default value: + https://github.com/auth0/node-jwks-rsa/blob/master/EXAMPLES.md#configuration */ cacheMaxAge: config.jwksCacheMaxAge ?? 600000, }) diff --git a/packages/purpose-process/src/app.ts b/packages/purpose-process/src/app.ts index 6ca56e43f2..b9ace41e7c 100644 --- a/packages/purpose-process/src/app.ts +++ b/packages/purpose-process/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, loggerMiddleware, zodiosCtx, @@ -12,13 +13,15 @@ const serviceName = "purpose-process"; const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + // Disable the "X-Powered-By: Express" HTTP header for security reasons. // See https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#recommendation_16 app.disable("x-powered-by"); app.use(healthRouter); app.use(contextMiddleware(serviceName)); -app.use(authenticationMiddleware(config)); +app.use(authenticationMiddleware(config, jwksClients)); app.use(loggerMiddleware(serviceName)); app.use(purposeRouter(zodiosCtx)); diff --git a/packages/tenant-process/src/app.ts b/packages/tenant-process/src/app.ts index 218b8d6fc7..3a506b300a 100644 --- a/packages/tenant-process/src/app.ts +++ b/packages/tenant-process/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, loggerMiddleware, zodiosCtx, @@ -12,13 +13,15 @@ const serviceName = "tenant-process"; const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + // Disable the "X-Powered-By: Express" HTTP header for security reasons. // See https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#recommendation_16 app.disable("x-powered-by"); app.use(healthRouter); app.use(contextMiddleware(serviceName)); -app.use(authenticationMiddleware(config)); +app.use(authenticationMiddleware(config, jwksClients)); app.use(loggerMiddleware(serviceName)); app.use(tenantRouter(zodiosCtx)); From 49ba3aa1f055a1e51d074eede659abce96fb10e8 Mon Sep 17 00:00:00 2001 From: Simone Camito <32327779+MalpenZibo@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:27:02 +0100 Subject: [PATCH 07/34] Add strict to tsconfig (#1124) --- package.json | 1 + packages/agreement-email-sender/package.json | 4 +- packages/agreement-lifecycle/package.json | 2 +- .../agreement-outbound-writer/package.json | 4 +- packages/agreement-process/package.json | 4 +- .../agreement-readmodel-writer/package.json | 4 +- .../package.json | 4 +- packages/api-clients/package.json | 6 +- packages/api-gateway/package.json | 4 +- .../attribute-registry-process/package.json | 4 +- .../package.json | 4 +- packages/authorization-process/package.json | 4 +- packages/authorization-updater/package.json | 4 +- packages/backend-for-frontend/package.json | 4 +- packages/catalog-outbound-writer/package.json | 4 +- .../catalog-platformstate-writer/package.json | 5 +- packages/catalog-process/package.json | 4 +- .../catalog-readmodel-writer/package.json | 4 +- packages/client-readmodel-writer/package.json | 4 +- packages/commons/package.json | 2 +- .../compute-agreements-consumer/package.json | 4 +- packages/datalake-data-export/package.json | 4 +- .../package.json | 4 +- packages/event-migration/package.json | 6 +- .../package.json | 4 +- packages/key-readmodel-writer/package.json | 4 +- packages/models/package.json | 2 +- packages/notifier-seeder/package.json | 4 +- packages/one-trust-notices/package.json | 2 +- packages/pn-consumers/package.json | 4 +- .../producer-key-events-writer/package.json | 4 +- .../package.json | 4 +- .../package.json | 4 +- packages/purpose-outbound-writer/package.json | 4 +- .../purpose-platformstate-writer/package.json | 4 +- packages/purpose-process/package.json | 4 +- .../purpose-readmodel-writer/package.json | 4 +- .../selfcare-onboarding-consumer/package.json | 4 +- packages/tenant-outbound-writer/package.json | 4 +- packages/tenant-process/package.json | 4 +- packages/tenant-readmodel-writer/package.json | 4 +- pnpm-lock.yaml | 532 +++++++++++++----- tsconfig.json | 14 +- 43 files changed, 479 insertions(+), 225 deletions(-) diff --git a/package.json b/package.json index bbc23cfd29..28b56b7155 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "prune-modules": "find . -name 'node_modules' -type d -exec rm -rf {} +" }, "devDependencies": { + "@tsconfig/strictest": "2.0.5", "@tsconfig/node-lts": "20.1.3", "turbo": "2.0.4" }, diff --git a/packages/agreement-email-sender/package.json b/packages/agreement-email-sender/package.json index bfb9359b03..ea90b18e14 100644 --- a/packages/agreement-email-sender/package.json +++ b/packages/agreement-email-sender/package.json @@ -15,7 +15,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc && pnpm cpx './src/resources/**/*' './dist/resources'", "check": "tsc --project tsconfig.check.json" }, @@ -30,7 +30,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/agreement-lifecycle/package.json b/packages/agreement-lifecycle/package.json index 7cf5fa3890..c359fe25f0 100644 --- a/packages/agreement-lifecycle/package.json +++ b/packages/agreement-lifecycle/package.json @@ -13,7 +13,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm --watch ./src/index.ts", + "start": "tsx --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index 29903c6541..8879504385 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -9,7 +9,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/agreement-process/package.json b/packages/agreement-process/package.json index 980c7b3721..c405df5ad4 100644 --- a/packages/agreement-process/package.json +++ b/packages/agreement-process/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc && pnpm cpx './src/resources/**/*' './dist/resources'", "check": "tsc --project tsconfig.check.json" }, @@ -30,7 +30,7 @@ "prettier": "2.8.8", "puppeteer": "22.11.2", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/agreement-readmodel-writer/package.json b/packages/agreement-readmodel-writer/package.json index 9fb6785dcc..42c5db8fab 100644 --- a/packages/agreement-readmodel-writer/package.json +++ b/packages/agreement-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0", "@anatine/zod-mock": "3.13.4" diff --git a/packages/anac-certified-attributes-importer/package.json b/packages/anac-certified-attributes-importer/package.json index a78f5ad664..3a58c19dfd 100644 --- a/packages/anac-certified-attributes-importer/package.json +++ b/packages/anac-certified-attributes-importer/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/api-clients/package.json b/packages/api-clients/package.json index 20145f6472..71a102498f 100644 --- a/packages/api-clients/package.json +++ b/packages/api-clients/package.json @@ -13,10 +13,10 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json", - "generate-model": "node --loader ts-node/esm ./generate.ts", + "generate-model": "tsx ./generate.ts", "clean-generated": "pnpm exec rm ./src/generated/*.ts" }, "keywords": [], @@ -31,7 +31,7 @@ "openapi-zod-client": "1.18.1", "openapi3-ts": "3.1.0", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5" }, "dependencies": { diff --git a/packages/api-gateway/package.json b/packages/api-gateway/package.json index 5de2d0ef53..4eb73fdf98 100644 --- a/packages/api-gateway/package.json +++ b/packages/api-gateway/package.json @@ -10,7 +10,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/attribute-registry-process/package.json b/packages/attribute-registry-process/package.json index 9725a194c8..2d12271055 100644 --- a/packages/attribute-registry-process/package.json +++ b/packages/attribute-registry-process/package.json @@ -10,7 +10,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -27,7 +27,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/attribute-registry-readmodel-writer/package.json b/packages/attribute-registry-readmodel-writer/package.json index 319208b94e..fa5ffab59a 100644 --- a/packages/attribute-registry-readmodel-writer/package.json +++ b/packages/attribute-registry-readmodel-writer/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -24,7 +24,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/authorization-process/package.json b/packages/authorization-process/package.json index 27f0127472..d39fcc4589 100644 --- a/packages/authorization-process/package.json +++ b/packages/authorization-process/package.json @@ -10,7 +10,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -24,7 +24,7 @@ "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/authorization-updater/package.json b/packages/authorization-updater/package.json index d703d3b783..f585c6c187 100644 --- a/packages/authorization-updater/package.json +++ b/packages/authorization-updater/package.json @@ -13,7 +13,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json", "test": "vitest", @@ -28,7 +28,7 @@ "openapi-zod-client": "1.18.1", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/backend-for-frontend/package.json b/packages/backend-for-frontend/package.json index 7044893c23..17a7cde33c 100644 --- a/packages/backend-for-frontend/package.json +++ b/packages/backend-for-frontend/package.json @@ -10,7 +10,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -27,7 +27,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index 8e19ced827..edd098d26b 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -9,7 +9,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/catalog-platformstate-writer/package.json b/packages/catalog-platformstate-writer/package.json index 4a720b21c3..4e7eb78540 100644 --- a/packages/catalog-platformstate-writer/package.json +++ b/packages/catalog-platformstate-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,8 @@ "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "testcontainers": "10.9.0", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/catalog-process/package.json b/packages/catalog-process/package.json index 769b1246b8..bc2046ee62 100644 --- a/packages/catalog-process/package.json +++ b/packages/catalog-process/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -30,7 +30,7 @@ "pg-promise": "11.8.0", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/catalog-readmodel-writer/package.json b/packages/catalog-readmodel-writer/package.json index 8a6e112fd4..1274d02a7f 100644 --- a/packages/catalog-readmodel-writer/package.json +++ b/packages/catalog-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -26,7 +26,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/client-readmodel-writer/package.json b/packages/client-readmodel-writer/package.json index 6b5a59464d..e00bed02d8 100644 --- a/packages/client-readmodel-writer/package.json +++ b/packages/client-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/commons/package.json b/packages/commons/package.json index a2d1cf32a8..1c9c3bb1fe 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -13,7 +13,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm --watch ./src/index.ts", + "start": "tsx --watch ./src/index.ts", "build": "tsc && pnpm run copy-sql-files && cpx './src/pdf-generator/paged.polyfill.js' './dist/pdf-generator'", "check": "tsc --project tsconfig.check.json", "copy-sql-files": "cp ./src/repositories/sql/*.sql dist/repositories/sql" diff --git a/packages/compute-agreements-consumer/package.json b/packages/compute-agreements-consumer/package.json index b2b6d22bb8..cb86bd49c7 100644 --- a/packages/compute-agreements-consumer/package.json +++ b/packages/compute-agreements-consumer/package.json @@ -10,7 +10,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -21,7 +21,7 @@ "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5" }, "dependencies": { diff --git a/packages/datalake-data-export/package.json b/packages/datalake-data-export/package.json index d9ab0456b6..171b7c87ac 100644 --- a/packages/datalake-data-export/package.json +++ b/packages/datalake-data-export/package.json @@ -10,7 +10,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -22,7 +22,7 @@ "@pagopa/eslint-config": "3.0.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/eservice-descriptors-archiver/package.json b/packages/eservice-descriptors-archiver/package.json index c81de847eb..79c46dda91 100644 --- a/packages/eservice-descriptors-archiver/package.json +++ b/packages/eservice-descriptors-archiver/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "openapi-zod-client": "1.18.1", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/event-migration/package.json b/packages/event-migration/package.json index 5397a2de49..4fbc6c7904 100644 --- a/packages/event-migration/package.json +++ b/packages/event-migration/package.json @@ -10,8 +10,8 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start:migrate": "node --loader ts-node/esm -r 'dotenv-flow/config' ./src/index.ts", - "start:verify": "node --loader ts-node/esm -r 'dotenv-flow/config' ./src/read-models-migration-check.ts", + "start:migrate": "tsx -r 'dotenv-flow/config' ./src/index.ts", + "start:verify": "tsx -r 'dotenv-flow/config' ./src/read-models-migration-check.ts", "build": "tsc" }, "keywords": [], @@ -36,7 +36,7 @@ "@types/lodash.isequal": "4.5.8", "eslint": "8.57.0", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" } diff --git a/packages/ivass-certified-attributes-importer/package.json b/packages/ivass-certified-attributes-importer/package.json index a4866577fa..871a82cfab 100644 --- a/packages/ivass-certified-attributes-importer/package.json +++ b/packages/ivass-certified-attributes-importer/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -26,7 +26,7 @@ "@types/adm-zip": "0.5.5", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/key-readmodel-writer/package.json b/packages/key-readmodel-writer/package.json index af4f12b037..6325fa4784 100644 --- a/packages/key-readmodel-writer/package.json +++ b/packages/key-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/models/package.json b/packages/models/package.json index f3ab5d994d..48deee10fe 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -13,7 +13,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm --watch ./src/index.ts", + "start": "tsx --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json", "generate-protobuf": "mkdirp ./src/gen && npx protoc --ts_opt=eslint_disable --ts_out ./src/gen --proto_path ./proto ./proto/**/**/*.proto && tsc-esm-fix --src='src/gen/' --ext='.js'" diff --git a/packages/notifier-seeder/package.json b/packages/notifier-seeder/package.json index ac3c5213a5..0df35f6735 100644 --- a/packages/notifier-seeder/package.json +++ b/packages/notifier-seeder/package.json @@ -15,7 +15,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -26,7 +26,7 @@ "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "kafkajs": "2.2.4", "mkdirp": "3.0.1", diff --git a/packages/one-trust-notices/package.json b/packages/one-trust-notices/package.json index 3b68f5f4ed..73df7d6445 100644 --- a/packages/one-trust-notices/package.json +++ b/packages/one-trust-notices/package.json @@ -24,7 +24,7 @@ "@types/lodash": "4.14.196", "@types/node": "20.4.9", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5" }, "dependencies": { diff --git a/packages/pn-consumers/package.json b/packages/pn-consumers/package.json index 1d016ccfe2..bea8e6aa6d 100644 --- a/packages/pn-consumers/package.json +++ b/packages/pn-consumers/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/producer-key-events-writer/package.json b/packages/producer-key-events-writer/package.json index 8b1c51a2d5..1bdefcae1c 100644 --- a/packages/producer-key-events-writer/package.json +++ b/packages/producer-key-events-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -24,7 +24,7 @@ "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/producer-key-readmodel-writer/package.json b/packages/producer-key-readmodel-writer/package.json index da8bfaa6d4..a5df018a6f 100644 --- a/packages/producer-key-readmodel-writer/package.json +++ b/packages/producer-key-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/producer-keychain-readmodel-writer/package.json b/packages/producer-keychain-readmodel-writer/package.json index d73caa1024..f9119fae85 100644 --- a/packages/producer-keychain-readmodel-writer/package.json +++ b/packages/producer-keychain-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index 5b2830f708..244b9faa3f 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -9,7 +9,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/purpose-platformstate-writer/package.json b/packages/purpose-platformstate-writer/package.json index b17c00dde4..0b441191da 100644 --- a/packages/purpose-platformstate-writer/package.json +++ b/packages/purpose-platformstate-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/purpose-process/package.json b/packages/purpose-process/package.json index 9c2eaec3c4..9c6af53d9a 100644 --- a/packages/purpose-process/package.json +++ b/packages/purpose-process/package.json @@ -10,7 +10,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc && pnpm cpx './src/resources/**/*' './dist/resources'", "check": "tsc --project tsconfig.check.json" }, @@ -26,7 +26,7 @@ "prettier": "2.8.8", "puppeteer": "22.11.2", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/purpose-readmodel-writer/package.json b/packages/purpose-readmodel-writer/package.json index cbdabd96f1..328de3bf3d 100644 --- a/packages/purpose-readmodel-writer/package.json +++ b/packages/purpose-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/selfcare-onboarding-consumer/package.json b/packages/selfcare-onboarding-consumer/package.json index e1eb6bf376..cc5dad5ac9 100644 --- a/packages/selfcare-onboarding-consumer/package.json +++ b/packages/selfcare-onboarding-consumer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 190a113491..668b914f54 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -9,7 +9,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/tenant-process/package.json b/packages/tenant-process/package.json index 1ec9be60ad..858d3d4f2e 100644 --- a/packages/tenant-process/package.json +++ b/packages/tenant-process/package.json @@ -10,7 +10,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pg-promise": "11.8.0", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/tenant-readmodel-writer/package.json b/packages/tenant-readmodel-writer/package.json index 0764fccd60..ecab1ba030 100644 --- a/packages/tenant-readmodel-writer/package.json +++ b/packages/tenant-readmodel-writer/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9dd9ae6c53..cd0b0f8a82 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@tsconfig/node-lts': specifier: 20.1.3 version: 20.1.3 + '@tsconfig/strictest': + specifier: 2.0.5 + version: 2.0.5 turbo: specifier: 2.0.4 version: 2.0.4 @@ -63,9 +66,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -143,9 +146,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -286,9 +289,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -344,9 +347,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -396,9 +399,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -457,9 +460,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -512,9 +515,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -594,9 +597,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -649,9 +652,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -722,9 +725,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -786,9 +789,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -886,9 +889,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -947,9 +950,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1008,9 +1011,12 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + testcontainers: + specifier: 10.9.0 + version: 10.9.0 + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1090,9 +1096,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1148,9 +1154,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1243,9 +1249,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1458,9 +1464,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1498,9 +1504,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1599,9 +1605,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1660,9 +1666,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1724,9 +1730,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1798,9 +1804,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1902,9 +1908,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1951,9 +1957,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.4.9)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2000,9 +2006,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2049,9 +2055,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2101,9 +2107,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2153,9 +2159,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2214,9 +2220,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2275,9 +2281,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2354,9 +2360,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2406,9 +2412,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2455,9 +2461,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2516,9 +2522,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2586,9 +2592,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2638,9 +2644,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -6018,6 +6024,15 @@ packages: dev: true optional: true + /@esbuild/aix-ppc64@0.23.1: + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm64@0.21.5: resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} @@ -6027,6 +6042,15 @@ packages: dev: true optional: true + /@esbuild/android-arm64@0.23.1: + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm@0.21.5: resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} @@ -6036,6 +6060,15 @@ packages: dev: true optional: true + /@esbuild/android-arm@0.23.1: + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-x64@0.21.5: resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} @@ -6045,6 +6078,15 @@ packages: dev: true optional: true + /@esbuild/android-x64@0.23.1: + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-arm64@0.21.5: resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} @@ -6054,6 +6096,15 @@ packages: dev: true optional: true + /@esbuild/darwin-arm64@0.23.1: + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-x64@0.21.5: resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} @@ -6063,6 +6114,15 @@ packages: dev: true optional: true + /@esbuild/darwin-x64@0.23.1: + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-arm64@0.21.5: resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} @@ -6072,6 +6132,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-arm64@0.23.1: + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-x64@0.21.5: resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} @@ -6081,6 +6150,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-x64@0.23.1: + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm64@0.21.5: resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} @@ -6090,6 +6168,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm64@0.23.1: + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm@0.21.5: resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} @@ -6099,6 +6186,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm@0.23.1: + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ia32@0.21.5: resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} @@ -6108,6 +6204,15 @@ packages: dev: true optional: true + /@esbuild/linux-ia32@0.23.1: + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64@0.21.5: resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} @@ -6117,6 +6222,15 @@ packages: dev: true optional: true + /@esbuild/linux-loong64@0.23.1: + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-mips64el@0.21.5: resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} @@ -6126,6 +6240,15 @@ packages: dev: true optional: true + /@esbuild/linux-mips64el@0.23.1: + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ppc64@0.21.5: resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} @@ -6135,6 +6258,15 @@ packages: dev: true optional: true + /@esbuild/linux-ppc64@0.23.1: + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-riscv64@0.21.5: resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} @@ -6144,6 +6276,15 @@ packages: dev: true optional: true + /@esbuild/linux-riscv64@0.23.1: + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-s390x@0.21.5: resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} @@ -6153,6 +6294,15 @@ packages: dev: true optional: true + /@esbuild/linux-s390x@0.23.1: + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-x64@0.21.5: resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} @@ -6162,6 +6312,15 @@ packages: dev: true optional: true + /@esbuild/linux-x64@0.23.1: + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/netbsd-x64@0.21.5: resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} @@ -6171,6 +6330,24 @@ packages: dev: true optional: true + /@esbuild/netbsd-x64@0.23.1: + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-arm64@0.23.1: + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/openbsd-x64@0.21.5: resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} @@ -6180,6 +6357,15 @@ packages: dev: true optional: true + /@esbuild/openbsd-x64@0.23.1: + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/sunos-x64@0.21.5: resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} @@ -6189,6 +6375,15 @@ packages: dev: true optional: true + /@esbuild/sunos-x64@0.23.1: + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-arm64@0.21.5: resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} @@ -6198,6 +6393,15 @@ packages: dev: true optional: true + /@esbuild/win32-arm64@0.23.1: + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-ia32@0.21.5: resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} @@ -6207,6 +6411,15 @@ packages: dev: true optional: true + /@esbuild/win32-ia32@0.23.1: + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-x64@0.21.5: resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} @@ -6216,6 +6429,15 @@ packages: dev: true optional: true + /@esbuild/win32-x64@0.23.1: + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7734,6 +7956,10 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true + /@tsconfig/strictest@2.0.5: + resolution: {integrity: sha512-ec4tjL2Rr0pkZ5hww65c+EEPYwxOi4Ryv+0MtjeaSQRJyq322Q27eOQiFbuNgw2hpL4hB1/W/HBGk3VKS43osg==} + dev: true + /@types/adm-zip@0.5.5: resolution: {integrity: sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==} dependencies: @@ -9419,6 +9645,38 @@ packages: '@esbuild/win32-x64': 0.21.5 dev: true + /esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + dev: true + /escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} @@ -10118,6 +10376,12 @@ packages: get-intrinsic: 1.2.4 dev: true + /get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + /get-uri@6.0.3: resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} engines: {node: '>= 14'} @@ -11884,6 +12148,10 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + /resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -12585,37 +12853,6 @@ packages: yn: 3.1.1 dev: true - /ts-node@10.9.2(@types/node@20.4.9)(typescript@5.4.5): - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - 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 - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.4.9 - acorn: 8.12.1 - acorn-walk: 8.3.3 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.4.5 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - dev: true - /ts-pattern@5.2.0: resolution: {integrity: sha512-aGaSpOlDcns7ZoeG/OMftWyQG1KqPVhgplhJxNCvyIXqWrumM5uIoOSarw/hmmi/T1PnuQ/uD8NaFHvLpHicDg==} @@ -12660,6 +12897,17 @@ packages: typescript: 5.4.5 dev: true + /tsx@4.19.1: + resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} + engines: {node: '>=18.0.0'} + hasBin: true + dependencies: + esbuild: 0.23.1 + get-tsconfig: 4.8.1 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /turbo-darwin-64@2.0.4: resolution: {integrity: sha512-x9mvmh4wudBstML8Z8IOmokLWglIhSfhQwnh2gBCSqabgVBKYvzl8Y+i+UCNPxheCGTgtsPepTcIaKBIyFIcvw==} cpu: [x64] diff --git a/tsconfig.json b/tsconfig.json index 9f28fb71a1..b5d495ded3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,13 @@ { - "extends": "@tsconfig/node-lts", + "extends": [ + "@tsconfig/node-lts/tsconfig.json", + "@tsconfig/strictest/tsconfig.json" + ], "compilerOptions": { - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true + "noUncheckedIndexedAccess": false, + "exactOptionalPropertyTypes": false, + "noImplicitOverride": false, + "checkJs": false, + "noPropertyAccessFromIndexSignature": false } } From fa71e251af46988823581afc62d4dddcbceb9175 Mon Sep 17 00:00:00 2001 From: Simone Camito <32327779+MalpenZibo@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:07:37 +0100 Subject: [PATCH 08/34] IMN 504 enable stricter peer deps 2 (#1150) Co-authored-by: Eric Camellini --- .npmrc | 2 + package.json | 2 +- packages/agreement-email-sender/Dockerfile | 1 + packages/agreement-outbound-writer/Dockerfile | 1 + .../agreement-outbound-writer/package.json | 1 + .../agreement-platformstate-writer/Dockerfile | 1 + packages/agreement-process/Dockerfile | 1 + packages/agreement-process/package.json | 1 + .../agreement-readmodel-writer/Dockerfile | 1 + .../agreement-readmodel-writer/package.json | 2 + .../Dockerfile | 1 + packages/api-clients/package.json | 1 + packages/api-gateway/Dockerfile | 1 + .../attribute-registry-process/Dockerfile | 1 + .../Dockerfile | 1 + packages/authorization-process/Dockerfile | 1 + packages/authorization-updater/Dockerfile | 3 +- packages/backend-for-frontend/Dockerfile | 1 + packages/catalog-outbound-writer/Dockerfile | 1 + packages/catalog-outbound-writer/package.json | 1 + .../catalog-platformstate-writer/Dockerfile | 1 + packages/catalog-process/Dockerfile | 1 + packages/catalog-readmodel-writer/Dockerfile | 1 + packages/client-readmodel-writer/Dockerfile | 1 + packages/commons-test/package.json | 1 + packages/commons/package.json | 1 + .../compute-agreements-consumer/Dockerfile | 1 + packages/datalake-data-export/Dockerfile | 1 + packages/dtd-catalog-exporter/Dockerfile | 3 +- .../eservice-descriptors-archiver/Dockerfile | 1 + .../package.json | 1 + .../Dockerfile | 1 + packages/kafka-iam-auth/package.json | 2 + packages/key-readmodel-writer/Dockerfile | 1 + packages/notifier-seeder/Dockerfile | 3 +- packages/one-trust-notices/Dockerfile | 3 +- packages/pn-consumers/Dockerfile | 1 + .../producer-key-events-writer/Dockerfile | 1 + .../producer-key-readmodel-writer/Dockerfile | 1 + .../Dockerfile | 1 + packages/purpose-outbound-writer/Dockerfile | 1 + packages/purpose-outbound-writer/package.json | 1 + .../purpose-platformstate-writer/Dockerfile | 1 + packages/purpose-process/Dockerfile | 1 + packages/purpose-readmodel-writer/Dockerfile | 1 + .../selfcare-onboarding-consumer/Dockerfile | 1 + packages/tenant-outbound-writer/Dockerfile | 1 + packages/tenant-outbound-writer/package.json | 1 + packages/tenant-process/Dockerfile | 1 + packages/tenant-readmodel-writer/Dockerfile | 1 + pnpm-lock.yaml | 11564 +++++++++------- 51 files changed, 6512 insertions(+), 5114 deletions(-) diff --git a/.npmrc b/.npmrc index cadcdac3a1..e0473968ca 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,3 @@ save-prefix= +auto-install-peers = false +strict-peer-dependencies = true diff --git a/package.json b/package.json index 28b56b7155..785be17b2b 100644 --- a/package.json +++ b/package.json @@ -56,5 +56,5 @@ "config": { "protocVersion": "26.1" }, - "packageManager": "pnpm@8.15.8" + "packageManager": "pnpm@9.12.3" } diff --git a/packages/agreement-email-sender/Dockerfile b/packages/agreement-email-sender/Dockerfile index bd0bca6b96..5711c74b4e 100644 --- a/packages/agreement-email-sender/Dockerfile +++ b/packages/agreement-email-sender/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/agreement-email-sender/package.json /app/packages/agreement-email-sender/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/agreement-outbound-writer/Dockerfile b/packages/agreement-outbound-writer/Dockerfile index a86919c53a..f60133f5d9 100644 --- a/packages/agreement-outbound-writer/Dockerfile +++ b/packages/agreement-outbound-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/agreement-outbound-writer/package.json /app/packages/agreement-outbound-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index 8879504385..29c67eb697 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -18,6 +18,7 @@ "license": "Apache-2.0", "devDependencies": { "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", diff --git a/packages/agreement-platformstate-writer/Dockerfile b/packages/agreement-platformstate-writer/Dockerfile index cf634c5466..658a95d277 100644 --- a/packages/agreement-platformstate-writer/Dockerfile +++ b/packages/agreement-platformstate-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/agreement-platformstate-writer/package.json /app/packages/agreement-platformstate-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/agreement-process/Dockerfile b/packages/agreement-process/Dockerfile index eaeef21e16..a82a5121dd 100644 --- a/packages/agreement-process/Dockerfile +++ b/packages/agreement-process/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/agreement-process/package.json /app/packages/agreement-process/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/agreement-process/package.json b/packages/agreement-process/package.json index c405df5ad4..4638e17925 100644 --- a/packages/agreement-process/package.json +++ b/packages/agreement-process/package.json @@ -20,6 +20,7 @@ "license": "Apache-2.0", "devDependencies": { "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", "@types/express": "4.17.21", "@types/node": "20.14.6", diff --git a/packages/agreement-readmodel-writer/Dockerfile b/packages/agreement-readmodel-writer/Dockerfile index cbf15af562..46d3ebc355 100644 --- a/packages/agreement-readmodel-writer/Dockerfile +++ b/packages/agreement-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/agreement-readmodel-writer/package.json /app/packages/agreement-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/agreement-readmodel-writer/package.json b/packages/agreement-readmodel-writer/package.json index 42c5db8fab..9f5817d3ac 100644 --- a/packages/agreement-readmodel-writer/package.json +++ b/packages/agreement-readmodel-writer/package.json @@ -20,6 +20,8 @@ "author": "", "license": "Apache-2.0", "devDependencies": { + "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", diff --git a/packages/anac-certified-attributes-importer/Dockerfile b/packages/anac-certified-attributes-importer/Dockerfile index d0bda046bc..ef615e0032 100644 --- a/packages/anac-certified-attributes-importer/Dockerfile +++ b/packages/anac-certified-attributes-importer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/anac-certified-attributes-importer/package.json /app/packages/anac-certified-attributes-importer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/api-clients/package.json b/packages/api-clients/package.json index 71a102498f..da7a2203d2 100644 --- a/packages/api-clients/package.json +++ b/packages/api-clients/package.json @@ -27,6 +27,7 @@ "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "@types/qs": "6.9.15", + "openapi-types": "12.1.3", "handlebars": "4.7.7", "openapi-zod-client": "1.18.1", "openapi3-ts": "3.1.0", diff --git a/packages/api-gateway/Dockerfile b/packages/api-gateway/Dockerfile index 03a589e9f7..937ba688df 100644 --- a/packages/api-gateway/Dockerfile +++ b/packages/api-gateway/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/api-gateway/package.json /app/packages/api-gateway/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/attribute-registry-process/Dockerfile b/packages/attribute-registry-process/Dockerfile index 4a6e86250a..411c711ac1 100644 --- a/packages/attribute-registry-process/Dockerfile +++ b/packages/attribute-registry-process/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/attribute-registry-process/package.json /app/packages/attribute-registry-process/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/attribute-registry-readmodel-writer/Dockerfile b/packages/attribute-registry-readmodel-writer/Dockerfile index 98c8532726..c040bf5a84 100644 --- a/packages/attribute-registry-readmodel-writer/Dockerfile +++ b/packages/attribute-registry-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/attribute-registry-readmodel-writer/package.json /app/packages/attribute-registry-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/authorization-process/Dockerfile b/packages/authorization-process/Dockerfile index 3c510caf3d..7547ac5e65 100644 --- a/packages/authorization-process/Dockerfile +++ b/packages/authorization-process/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/authorization-process/package.json /app/packages/authorization-process/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/authorization-updater/Dockerfile b/packages/authorization-updater/Dockerfile index e4d2b25875..ae4739d51e 100644 --- a/packages/authorization-updater/Dockerfile +++ b/packages/authorization-updater/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/authorization-updater/package.json /app/packages/authorization-updater/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json @@ -45,4 +46,4 @@ COPY --from=build /out /app WORKDIR /app/packages/authorization-updater EXPOSE 3000 -CMD ["node", "."] \ No newline at end of file +CMD ["node", "."] diff --git a/packages/backend-for-frontend/Dockerfile b/packages/backend-for-frontend/Dockerfile index 807bfd6854..8b179f6d24 100644 --- a/packages/backend-for-frontend/Dockerfile +++ b/packages/backend-for-frontend/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/backend-for-frontend/package.json /app/packages/backend-for-frontend/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/catalog-outbound-writer/Dockerfile b/packages/catalog-outbound-writer/Dockerfile index 693cc0c61b..e9e0bd6435 100644 --- a/packages/catalog-outbound-writer/Dockerfile +++ b/packages/catalog-outbound-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/catalog-outbound-writer/package.json /app/packages/catalog-outbound-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index edd098d26b..a9909a6a41 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -18,6 +18,7 @@ "license": "Apache-2.0", "devDependencies": { "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", diff --git a/packages/catalog-platformstate-writer/Dockerfile b/packages/catalog-platformstate-writer/Dockerfile index 071bcfce1d..fc7761b66b 100644 --- a/packages/catalog-platformstate-writer/Dockerfile +++ b/packages/catalog-platformstate-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/catalog-platformstate-writer/package.json /app/packages/catalog-platformstate-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/catalog-process/Dockerfile b/packages/catalog-process/Dockerfile index ff84d16c24..55cd103bd7 100644 --- a/packages/catalog-process/Dockerfile +++ b/packages/catalog-process/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/catalog-process/package.json /app/packages/catalog-process/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/catalog-readmodel-writer/Dockerfile b/packages/catalog-readmodel-writer/Dockerfile index 4cc4aabbe4..5a8ed0c001 100644 --- a/packages/catalog-readmodel-writer/Dockerfile +++ b/packages/catalog-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/catalog-readmodel-writer/package.json /app/packages/catalog-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/client-readmodel-writer/Dockerfile b/packages/client-readmodel-writer/Dockerfile index 2ccaf6387d..93d9aff7c4 100644 --- a/packages/client-readmodel-writer/Dockerfile +++ b/packages/client-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/client-readmodel-writer/package.json /app/packages/client-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/commons-test/package.json b/packages/commons-test/package.json index c8acc06d33..55e91be360 100644 --- a/packages/commons-test/package.json +++ b/packages/commons-test/package.json @@ -19,6 +19,7 @@ "license": "Apache-2.0", "devDependencies": { "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@aws-sdk/client-dynamodb": "3.637.0", "@aws-sdk/client-sesv2": "3.620.1", "@aws-sdk/util-dynamodb": "3.658.1", diff --git a/packages/commons/package.json b/packages/commons/package.json index 1c9c3bb1fe..ddc6b2af54 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -30,6 +30,7 @@ "connection-string": "4.4.0", "date-fns": "3.6.0", "date-fns-tz": "3.1.3", + "express": "4.20.0", "handlebars": "4.7.8", "jsonwebtoken": "9.0.2", "jwks-rsa": "3.1.0", diff --git a/packages/compute-agreements-consumer/Dockerfile b/packages/compute-agreements-consumer/Dockerfile index 6c3029c745..d84930276d 100644 --- a/packages/compute-agreements-consumer/Dockerfile +++ b/packages/compute-agreements-consumer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/compute-agreements-consumer/package.json /app/packages/compute-agreements-consumer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/datalake-data-export/Dockerfile b/packages/datalake-data-export/Dockerfile index 8d82627c69..60a2edcb6d 100644 --- a/packages/datalake-data-export/Dockerfile +++ b/packages/datalake-data-export/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/commons/package.json /app/packages/commons/package.json COPY ./packages/models/package.json /app/packages/models/package.json diff --git a/packages/dtd-catalog-exporter/Dockerfile b/packages/dtd-catalog-exporter/Dockerfile index 9908b28da3..488e526305 100644 --- a/packages/dtd-catalog-exporter/Dockerfile +++ b/packages/dtd-catalog-exporter/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/dtd-catalog-exporter/package.json /app/packages/dtd-catalog-exporter/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json @@ -38,4 +39,4 @@ COPY --from=build /out /app WORKDIR /app/packages/dtd-catalog-exporter EXPOSE 3000 -CMD [ "node", "." ] \ No newline at end of file +CMD [ "node", "." ] diff --git a/packages/eservice-descriptors-archiver/Dockerfile b/packages/eservice-descriptors-archiver/Dockerfile index a70ab83d1f..5e3af8ade1 100644 --- a/packages/eservice-descriptors-archiver/Dockerfile +++ b/packages/eservice-descriptors-archiver/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/eservice-descriptors-archiver/package.json /app/packages/eservice-descriptors-archiver/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/eservice-descriptors-archiver/package.json b/packages/eservice-descriptors-archiver/package.json index 79c46dda91..2a7ce3a8c6 100644 --- a/packages/eservice-descriptors-archiver/package.json +++ b/packages/eservice-descriptors-archiver/package.json @@ -31,6 +31,7 @@ }, "dependencies": { "@zodios/core": "10.9.6", + "axios": "1.7.4", "dotenv-flow": "4.1.0", "kafka-iam-auth": "workspace:*", "kafkajs": "2.2.4", diff --git a/packages/ivass-certified-attributes-importer/Dockerfile b/packages/ivass-certified-attributes-importer/Dockerfile index c8afa507c1..3623a3e3a9 100644 --- a/packages/ivass-certified-attributes-importer/Dockerfile +++ b/packages/ivass-certified-attributes-importer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/ivass-certified-attributes-importer/package.json /app/packages/ivass-certified-attributes-importer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/kafka-iam-auth/package.json b/packages/kafka-iam-auth/package.json index 1a1aa26661..7faa145774 100644 --- a/packages/kafka-iam-auth/package.json +++ b/packages/kafka-iam-auth/package.json @@ -18,6 +18,8 @@ }, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/client-sso-oidc": "3.609.0", + "@aws-sdk/client-sts": "3.609.0", "aws-msk-iam-sasl-signer-js": "1.0.0", "kafkajs": "2.2.4", "pagopa-interop-commons": "workspace:*", diff --git a/packages/key-readmodel-writer/Dockerfile b/packages/key-readmodel-writer/Dockerfile index 1a9d23bbe1..8780ec0762 100644 --- a/packages/key-readmodel-writer/Dockerfile +++ b/packages/key-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/key-readmodel-writer/package.json /app/packages/key-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/notifier-seeder/Dockerfile b/packages/notifier-seeder/Dockerfile index a25d41560f..b1fc012d3f 100644 --- a/packages/notifier-seeder/Dockerfile +++ b/packages/notifier-seeder/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/notifier-seeder/package.json /app/packages/notifier-seeder/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json @@ -42,4 +43,4 @@ COPY --from=build /out /app WORKDIR /app/packages/notifier-seeder EXPOSE 3000 -CMD ["node", "."] \ No newline at end of file +CMD ["node", "."] diff --git a/packages/one-trust-notices/Dockerfile b/packages/one-trust-notices/Dockerfile index ba3d780407..5862581077 100644 --- a/packages/one-trust-notices/Dockerfile +++ b/packages/one-trust-notices/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/one-trust-notices/package.json /app/packages/one-trust-notices/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json @@ -38,4 +39,4 @@ COPY --from=build /out /app WORKDIR /app/packages/one-trust-notices EXPOSE 3000 -CMD [ "node", "." ] \ No newline at end of file +CMD [ "node", "." ] diff --git a/packages/pn-consumers/Dockerfile b/packages/pn-consumers/Dockerfile index 8429240973..dfa312051b 100644 --- a/packages/pn-consumers/Dockerfile +++ b/packages/pn-consumers/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/pn-consumers/package.json /app/packages/pn-consumers/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/producer-key-events-writer/Dockerfile b/packages/producer-key-events-writer/Dockerfile index c63c4e5bd8..59746502e2 100644 --- a/packages/producer-key-events-writer/Dockerfile +++ b/packages/producer-key-events-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/producer-key-events-writer/package.json /app/packages/producer-key-events-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/producer-key-readmodel-writer/Dockerfile b/packages/producer-key-readmodel-writer/Dockerfile index 0bd5604ba0..6a3b8e086b 100644 --- a/packages/producer-key-readmodel-writer/Dockerfile +++ b/packages/producer-key-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/producer-key-readmodel-writer/package.json /app/packages/producer-key-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/producer-keychain-readmodel-writer/Dockerfile b/packages/producer-keychain-readmodel-writer/Dockerfile index ee8a1cf6eb..73a71732ae 100644 --- a/packages/producer-keychain-readmodel-writer/Dockerfile +++ b/packages/producer-keychain-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/producer-keychain-readmodel-writer/package.json /app/packages/producer-keychain-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/purpose-outbound-writer/Dockerfile b/packages/purpose-outbound-writer/Dockerfile index 5ba3de5f46..b2dd44ec7b 100644 --- a/packages/purpose-outbound-writer/Dockerfile +++ b/packages/purpose-outbound-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/purpose-outbound-writer/package.json /app/packages/purpose-outbound-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index 244b9faa3f..c3e80434a4 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -18,6 +18,7 @@ "license": "Apache-2.0", "devDependencies": { "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", diff --git a/packages/purpose-platformstate-writer/Dockerfile b/packages/purpose-platformstate-writer/Dockerfile index 2fa2593320..31aca4b36e 100644 --- a/packages/purpose-platformstate-writer/Dockerfile +++ b/packages/purpose-platformstate-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/purpose-platformstate-writer/package.json /app/packages/purpose-platformstate-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/purpose-process/Dockerfile b/packages/purpose-process/Dockerfile index ca6ff9bde1..e3fe734e97 100644 --- a/packages/purpose-process/Dockerfile +++ b/packages/purpose-process/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/purpose-process/package.json /app/packages/purpose-process/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/purpose-readmodel-writer/Dockerfile b/packages/purpose-readmodel-writer/Dockerfile index f0cec09bee..3f8e6ef1ce 100644 --- a/packages/purpose-readmodel-writer/Dockerfile +++ b/packages/purpose-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/purpose-readmodel-writer/package.json /app/packages/purpose-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/selfcare-onboarding-consumer/Dockerfile b/packages/selfcare-onboarding-consumer/Dockerfile index 8c1c3eec88..276aa61618 100644 --- a/packages/selfcare-onboarding-consumer/Dockerfile +++ b/packages/selfcare-onboarding-consumer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/selfcare-onboarding-consumer/package.json /app/packages/selfcare-onboarding-consumer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/tenant-outbound-writer/Dockerfile b/packages/tenant-outbound-writer/Dockerfile index 41c20db36d..28368098be 100644 --- a/packages/tenant-outbound-writer/Dockerfile +++ b/packages/tenant-outbound-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/tenant-outbound-writer/package.json /app/packages/tenant-outbound-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 668b914f54..6e79cc66a9 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -18,6 +18,7 @@ "license": "Apache-2.0", "devDependencies": { "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", diff --git a/packages/tenant-process/Dockerfile b/packages/tenant-process/Dockerfile index 1e286d298b..cc78118316 100644 --- a/packages/tenant-process/Dockerfile +++ b/packages/tenant-process/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/tenant-process/package.json /app/packages/tenant-process/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/tenant-readmodel-writer/Dockerfile b/packages/tenant-readmodel-writer/Dockerfile index 9314ddfda5..30b6de7616 100644 --- a/packages/tenant-readmodel-writer/Dockerfile +++ b/packages/tenant-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/tenant-readmodel-writer/package.json /app/packages/tenant-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd0b0f8a82..e5cf7ee4f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,7 +1,7 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: - autoInstallPeers: true + autoInstallPeers: false excludeLinksFromLockfile: false importers: @@ -47,7 +47,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -131,9 +131,12 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -194,7 +197,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -224,7 +227,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -236,7 +239,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) pagopa-interop-agreement-lifecycle: specifier: workspace:* version: link:../agreement-lifecycle @@ -259,9 +262,12 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/express': specifier: 4.17.21 version: 4.17.21 @@ -332,9 +338,12 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -383,7 +392,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -441,7 +450,7 @@ importers: version: 10.1.0(openapi-types@12.1.3) '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -451,6 +460,9 @@ importers: handlebars: specifier: 4.7.7 version: 4.7.7 + openapi-types: + specifier: 12.1.3 + version: 12.1.3 openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -474,7 +486,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -502,7 +514,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/express': specifier: 4.17.21 version: 4.17.21 @@ -532,7 +544,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -547,7 +559,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -578,7 +590,7 @@ importers: version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -639,7 +651,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -669,7 +681,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -684,7 +696,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -709,7 +721,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -776,7 +788,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -812,7 +824,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) adm-zip: specifier: 0.5.15 version: 0.5.15 @@ -864,7 +876,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/adm-zip': specifier: 0.5.5 version: 0.5.5 @@ -935,9 +947,12 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -998,7 +1013,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1031,7 +1046,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -1043,7 +1058,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -1071,7 +1086,7 @@ importers: version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1138,7 +1153,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1236,7 +1251,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1278,7 +1293,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -1291,6 +1306,9 @@ importers: date-fns-tz: specifier: 3.1.3 version: 3.1.3(date-fns@3.6.0) + express: + specifier: 4.20.0 + version: 4.20.0 handlebars: specifier: 4.7.8 version: 4.7.8 @@ -1305,7 +1323,7 @@ importers: version: 2.2.4 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) nodemailer: specifier: 6.9.14 version: 6.9.14 @@ -1376,9 +1394,12 @@ importers: '@aws-sdk/util-dynamodb': specifier: 3.658.1 version: 3.658.1(@aws-sdk/client-dynamodb@3.637.0) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1457,7 +1478,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1481,7 +1502,7 @@ importers: version: 4.1.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) pagopa-interop-commons: specifier: workspace:* version: link:../commons @@ -1494,7 +1515,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1521,7 +1542,7 @@ importers: version: 4.1.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) pagopa-interop-commons: specifier: workspace:* version: link:../commons @@ -1562,6 +1583,9 @@ importers: '@zodios/core': specifier: 10.9.6 version: 10.9.6(axios@1.7.4)(zod@3.23.8) + axios: + specifier: 1.7.4 + version: 1.7.4 dotenv-flow: specifier: 4.1.0 version: 4.1.0 @@ -1592,7 +1616,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1634,7 +1658,7 @@ importers: version: 4.5.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) pagopa-interop-commons: specifier: workspace:* version: link:../commons @@ -1711,7 +1735,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/adm-zip': specifier: 0.5.5 version: 0.5.5 @@ -1742,9 +1766,15 @@ importers: packages/kafka-iam-auth: dependencies: + '@aws-sdk/client-sso-oidc': + specifier: 3.609.0 + version: 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/client-sts': + specifier: 3.609.0 + version: 3.609.0 aws-msk-iam-sasl-signer-js: specifier: 1.0.0 - version: 1.0.0(@aws-sdk/client-sso-oidc@3.645.0) + version: 1.0.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) kafkajs: specifier: 2.2.4 version: 2.2.4 @@ -1791,7 +1821,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1889,7 +1919,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1944,7 +1974,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/html2json': specifier: 1.0.1 version: 1.0.1 @@ -1990,7 +2020,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2045,7 +2075,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2094,7 +2124,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2146,7 +2176,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2205,9 +2235,12 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2268,7 +2301,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2298,7 +2331,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -2313,7 +2346,7 @@ importers: version: 4.7.8 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -2338,7 +2371,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/express': specifier: 4.17.21 version: 4.17.21 @@ -2399,7 +2432,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2454,7 +2487,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2507,9 +2540,12 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2539,7 +2575,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -2551,7 +2587,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -2573,7 +2609,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/express': specifier: 4.17.21 version: 4.17.21 @@ -2634,7 +2670,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2656,358 +2692,5180 @@ importers: packages: - /@ampproject/remapping@2.3.0: + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - /@anatine/zod-mock@3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8): + '@anatine/zod-mock@3.13.4': resolution: {integrity: sha512-yO/KeuyYsEDCTcQ+7CiRuY3dnafMHIZUMok6Ci7aERRCTQ+/XmsiPk/RnMx5wlLmWBTmX9kw+PavbMsjM+sAJA==} peerDependencies: '@faker-js/faker': ^7.0.0 || ^8.0.0 zod: ^3.21.4 - dependencies: - '@faker-js/faker': 8.4.1 - randexp: 0.5.3 - zod: 3.23.8 - dev: true - /@apidevtools/json-schema-ref-parser@9.0.6: + '@apidevtools/json-schema-ref-parser@9.0.6': resolution: {integrity: sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==} - dependencies: - '@jsdevtools/ono': 7.1.3 - call-me-maybe: 1.0.2 - js-yaml: 3.14.1 - /@apidevtools/openapi-schemas@2.1.0: + '@apidevtools/openapi-schemas@2.1.0': resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} engines: {node: '>=10'} - /@apidevtools/swagger-methods@3.0.2: + '@apidevtools/swagger-methods@3.0.2': resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} - /@apidevtools/swagger-parser@10.1.0(openapi-types@12.1.3): + '@apidevtools/swagger-parser@10.1.0': resolution: {integrity: sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==} peerDependencies: openapi-types: '>=7' - dependencies: - '@apidevtools/json-schema-ref-parser': 9.0.6 - '@apidevtools/openapi-schemas': 2.1.0 - '@apidevtools/swagger-methods': 3.0.2 - '@jsdevtools/ono': 7.1.3 - ajv: 8.16.0 - ajv-draft-04: 1.0.0(ajv@8.16.0) - call-me-maybe: 1.0.2 - openapi-types: 12.1.3 - /@aws-crypto/crc32@3.0.0: + '@aws-crypto/crc32@3.0.0': resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==} - dependencies: - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/crc32@5.2.0: + '@aws-crypto/crc32@5.2.0': resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} engines: {node: '>=16.0.0'} - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 - tslib: 2.6.3 - dev: false - /@aws-crypto/crc32c@3.0.0: + '@aws-crypto/crc32c@3.0.0': resolution: {integrity: sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==} - dependencies: - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/crc32c@5.2.0: + '@aws-crypto/crc32c@5.2.0': resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 - tslib: 2.6.3 - dev: false - /@aws-crypto/ie11-detection@3.0.0: + '@aws-crypto/ie11-detection@3.0.0': resolution: {integrity: sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==} - dependencies: - tslib: 1.14.1 - dev: false - /@aws-crypto/sha1-browser@3.0.0: + '@aws-crypto/sha1-browser@3.0.0': resolution: {integrity: sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==} - dependencies: - '@aws-crypto/ie11-detection': 3.0.0 - '@aws-crypto/supports-web-crypto': 3.0.0 - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-locate-window': 3.568.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/sha1-browser@5.2.0: + '@aws-crypto/sha1-browser@5.2.0': resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} - dependencies: - '@aws-crypto/supports-web-crypto': 5.2.0 - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-locate-window': 3.568.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.6.3 - dev: false - /@aws-crypto/sha256-browser@3.0.0: + '@aws-crypto/sha256-browser@3.0.0': resolution: {integrity: sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==} - dependencies: - '@aws-crypto/ie11-detection': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-crypto/supports-web-crypto': 3.0.0 - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-locate-window': 3.568.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/sha256-browser@5.2.0: + '@aws-crypto/sha256-browser@5.2.0': resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} - dependencies: - '@aws-crypto/sha256-js': 5.2.0 - '@aws-crypto/supports-web-crypto': 5.2.0 - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-locate-window': 3.568.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.6.3 - /@aws-crypto/sha256-js@3.0.0: + '@aws-crypto/sha256-js@3.0.0': resolution: {integrity: sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==} - dependencies: - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/sha256-js@4.0.0: + '@aws-crypto/sha256-js@4.0.0': resolution: {integrity: sha512-MHGJyjE7TX9aaqXj7zk2ppnFUOhaDs5sP+HtNS0evOxn72c+5njUmyJmpGd7TfyoDznZlHMmdo/xGUdu2NIjNQ==} - dependencies: - '@aws-crypto/util': 4.0.0 - '@aws-sdk/types': 3.609.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/sha256-js@5.2.0: + '@aws-crypto/sha256-js@5.2.0': resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} engines: {node: '>=16.0.0'} - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 - tslib: 2.6.3 - /@aws-crypto/supports-web-crypto@3.0.0: + '@aws-crypto/supports-web-crypto@3.0.0': resolution: {integrity: sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==} - dependencies: - tslib: 1.14.1 - dev: false - /@aws-crypto/supports-web-crypto@5.2.0: + '@aws-crypto/supports-web-crypto@5.2.0': resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} - dependencies: - tslib: 2.6.3 - /@aws-crypto/util@3.0.0: + '@aws-crypto/util@3.0.0': resolution: {integrity: sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==} - dependencies: - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/util@4.0.0: + '@aws-crypto/util@4.0.0': resolution: {integrity: sha512-2EnmPy2gsFZ6m8bwUQN4jq+IyXV3quHAcwPOS6ZA3k+geujiqI8aRokO2kFJe+idJ/P3v4qWI186rVMo0+zLDQ==} - dependencies: - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/util@5.2.0: + '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.6.3 - /@aws-sdk/client-cognito-identity@3.609.0: + '@aws-sdk/client-cognito-identity@3.609.0': resolution: {integrity: sha512-3kDTpia1iN/accayoH3MbZRbDvX2tzrKrBTU7wNNoazVrh+gOMS8KCOWrOB72F0V299l4FsfQhnl9BDMVrc1iw==} engines: {node: '>=16.0.0'} - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/middleware-host-header': 3.609.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.609.0 - '@aws-sdk/middleware-user-agent': 3.609.0 - '@aws-sdk/region-config-resolver': 3.609.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.609.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - dev: false - /@aws-sdk/client-dynamodb@3.602.0: + '@aws-sdk/client-dynamodb@3.602.0': resolution: {integrity: sha512-q7lH7YD9KvHLF3tyAG1UqaPv4a6KiHLunqKYh8vt3d1WJK7t4wzE97Vf19MfNpza1MuZ0OF/SK8Kl69vEMrtOA==} engines: {node: '>=16.0.0'} - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/client-sts': 3.600.0 - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/middleware-endpoint-discovery': 3.598.0 - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 - '@smithy/util-utf8': 3.0.0 - '@smithy/util-waiter': 3.1.2 - tslib: 2.6.3 - uuid: 9.0.1 - transitivePeerDependencies: - - aws-crt - dev: false - /@aws-sdk/client-dynamodb@3.637.0: + '@aws-sdk/client-dynamodb@3.637.0': resolution: {integrity: sha512-zUneT0yLgJjC69yry2fgYVWkv68OeV3amWaDXHirA8yJgygyc7tBLo+sQmtHczmKt8dBD9bU3OWpbAbtpF9Esw==} engines: {node: '>=16.0.0'} - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.637.0(@aws-sdk/client-sts@3.637.0) - '@aws-sdk/client-sts': 3.637.0 - '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0)(@aws-sdk/client-sts@3.637.0) - '@aws-sdk/middleware-endpoint-discovery': 3.620.0 - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.637.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.637.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 - '@smithy/util-utf8': 3.0.0 + + '@aws-sdk/client-dynamodb@3.648.0': + resolution: {integrity: sha512-61yU6wQRlwOhD0mfJS/N8SYmv9hxkVYGKsXqSJ5PNNnySutoNof7cmX8cTuijpTQqLL9sKPfvPMlJCv7/M1AiA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-kms@3.600.0': + resolution: {integrity: sha512-m1o8aiVrVjExw6O+8JszXV3hr8sCyXKOLq1WCwWJqYF6Uf4vCf8iTYISQB3skbKUnBJm4SxVA82iViGAtWB7JA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-s3@3.387.0': + resolution: {integrity: sha512-TX42MDfXnIy/U6f8XjCTR1Ezg1125Sv5k9kdKZJ0kkKcb/81N0+RfTqsR+Kpn/AbLjtvSUWrFIdc1o//GsfZAQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/client-s3@3.600.0': + resolution: {integrity: sha512-iYoKbJTputbf+ubkX6gSK/y/4uJEBRaXZ18jykLdBQ8UJuGrk2gqvV8h7OlGAhToCeysmmMqM0vDWyLt6lP8nw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sesv2@3.620.1': + resolution: {integrity: sha512-pu/CbRQuxCA0EmDqyAktc77pjbmeWuQao6aLDBuXLDbccwKgRECLCTscqUcnqtfFhPkiOAim9LdczqEXLNIpcA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sqs@3.600.0': + resolution: {integrity: sha512-GgjEiWbGbiHGU3yZcCr1hXfaq/B/3ncYclqLEbbxrWkQOdri3qfl278h+Qn0/DQ8On0kj1UUxld87TVIkYfG8w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso-oidc@3.600.0': + resolution: {integrity: sha512-7+I8RWURGfzvChyNQSyj5/tKrqRbzRl7H+BnTOf/4Vsw1nFOi5ROhlhD4X/Y0QCTacxnaoNcIrqnY7uGGvVRzw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso-oidc@3.609.0': + resolution: {integrity: sha512-0bNPAyPdkWkS9EGB2A9BZDkBNrnVCBzk5lYRezoT4K3/gi9w1DTYH5tuRdwaTZdxW19U1mq7CV0YJJARKO1L9Q==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.609.0 + + '@aws-sdk/client-sso-oidc@3.620.1': + resolution: {integrity: sha512-gm69ttbkr7Kbg/Zzr3SczyLWkLgmK3bEZtkvbM/40ZW5ItYhDzJE48Ovs2lyA64h2YsOftDqqwcbJirAAdTgSg==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.620.1 + + '@aws-sdk/client-sso-oidc@3.637.0': + resolution: {integrity: sha512-27bHALN6Qb6m6KZmPvRieJ/QRlj1lyac/GT2Rn5kJpre8Mpp+yxrtvp3h9PjNBty4lCeFEENfY4dGNSozBuBcw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.637.0 + + '@aws-sdk/client-sso-oidc@3.645.0': + resolution: {integrity: sha512-X9ULtdk3cO+1ysurEkJ1MSnu6U00qodXx+IVual+1jXX4RYY1WmQmfo7uDKf6FFkz7wW1DAqU+GJIBNQr0YH8A==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.645.0 + + '@aws-sdk/client-sso@3.387.0': + resolution: {integrity: sha512-E7uKSvbA0XMKSN5KLInf52hmMpe9/OKo6N9OPffGXdn3fNEQlvyQq3meUkqG7Is0ldgsQMz5EUBNtNybXzr3tQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/client-sso@3.598.0': + resolution: {integrity: sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso@3.609.0': + resolution: {integrity: sha512-gqXGFDkIpKHCKAbeJK4aIDt3tiwJ26Rf5Tqw9JS6BYXsdMeOB8FTzqD9R+Yc1epHd8s5L94sdqXT5PapgxFZrg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso@3.620.1': + resolution: {integrity: sha512-4Ox0BSs+atrAhLvjNHN2uiYvSTdpMv//IS4l4XRoQG0cJKIPLs3OU3PL5H0X1NfZehz9/8FTWl5Lv81uw4j1eA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso@3.637.0': + resolution: {integrity: sha512-+KjLvgX5yJYROWo3TQuwBJlHCY0zz9PsLuEolmXQn0BVK1L/m9GteZHtd+rEdAoDGBpE0Xqjy1oz5+SmtsaRUw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso@3.645.0': + resolution: {integrity: sha512-2rc8TjnsNddOeKQ/pfNN7deNvGLXAeKeYtHtGDAiM2qfTKxd2sNcAsZ+JCDLyshuD4xLM5fpUyR0X8As9EAouQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.387.0': + resolution: {integrity: sha512-RPOME0gpAViheH6xHyMg/XkE1G/fs6dgKK/NqlBZDjwMsSTPc8CmItEC6FOsCaLJktif0tD/u9m2uaQ4Lb1nVw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/client-sts@3.600.0': + resolution: {integrity: sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.609.0': + resolution: {integrity: sha512-A0B3sDKFoFlGo8RYRjDBWHXpbgirer2bZBkCIzhSPHc1vOFHt/m2NcUoE2xnBKXJFrptL1xDkvo1P+XYp/BfcQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.620.1': + resolution: {integrity: sha512-d+ECGFDg0IsDdmfKU2O0VeMYKZcmbfBaA9HkZnZ39wu1BlXGI73xJe8cfmzbobvu+Ly+bAfHdLCpgIY+pD4D7g==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.637.0': + resolution: {integrity: sha512-xUi7x4qDubtA8QREtlblPuAcn91GS/09YVEY/RwU7xCY0aqGuFwgszAANlha4OUIqva8oVj2WO4gJuG+iaSnhw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.645.0': + resolution: {integrity: sha512-6azXYtvtnAsPf2ShN9vKynIYVcJOpo6IoVmoMAVgNaBJyllP+s/RORzranYZzckqfmrudSxtct4rVapjLWuAMg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/core@3.598.0': + resolution: {integrity: sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/core@3.609.0': + resolution: {integrity: sha512-ptqw+DTxLr01+pKjDUuo53SEDzI+7nFM3WfQaEo0yhDg8vWw8PER4sWj1Ysx67ksctnZesPUjqxd5SHbtdBxiA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/core@3.620.1': + resolution: {integrity: sha512-6Ejce93dDlDnovl6oYtxj3I/SJMOQoFdmmtM4+4W/cgMWH+l00T5aszVxDLjjPfu3Ryt7dNhrXaYeK2Ue1ZBmg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/core@3.635.0': + resolution: {integrity: sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-cognito-identity@3.609.0': + resolution: {integrity: sha512-BqrpAXRr64dQ/uZsRB2wViGKTkVRlfp8Q+Zd7Bc8Ikk+YXjPtl+IyWXKtdKQ3LBO255KwAcPmra5oFC+2R1GOQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-env@3.387.0': + resolution: {integrity: sha512-PVqNk7XPIYe5CMYNvELkcALtkl/pIM8/uPtqEtTg+mgnZBeL4fAmgXZiZMahQo1DxP5t/JaK384f6JG+A0qDjA==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/credential-provider-env@3.598.0': + resolution: {integrity: sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-env@3.609.0': + resolution: {integrity: sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-env@3.620.1': + resolution: {integrity: sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-http@3.598.0': + resolution: {integrity: sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-http@3.609.0': + resolution: {integrity: sha512-GQQfB9Mk4XUZwaPsk4V3w8MqleS6ApkZKVQn3vTLAKa8Y7B2Imcpe5zWbKYjDd8MPpMWjHcBGFTVlDRFP4zwSQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-http@3.620.0': + resolution: {integrity: sha512-BI2BdrSKDmB/2ouB/NJR0PT0x/+5fmoF6XOE78hFBb4F5w/yynGgcJY936dF+oREfpME6ehjB2b0okGg78Scpw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-http@3.635.0': + resolution: {integrity: sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-ini@3.387.0': + resolution: {integrity: sha512-DS2Jg5E4Hd9fhJqTVNBG3SEwLwcyguPDcXSVCDz5pEHlYFM1U4x9b7aAbutzZujTH99MZ6Gua8kAotB/qjEjtw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/credential-provider-ini@3.598.0': + resolution: {integrity: sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.598.0 + + '@aws-sdk/credential-provider-ini@3.609.0': + resolution: {integrity: sha512-hwaBfXuBTv6/eAdEsDfGcteYUW6Km7lvvubbxEdxIuJNF3vswR7RMGIXaEC37hhPkTTgd3H0TONammhwZIfkog==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.609.0 + + '@aws-sdk/credential-provider-ini@3.620.1': + resolution: {integrity: sha512-m9jwigMPRlRRhoPxCQZMOwQUd6imEJbksF6tSMYNae76DIvrCi4z2Jhp6RJ9Mij8cnewUZCAmvu2FlK9+n9M7A==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.620.1 + + '@aws-sdk/credential-provider-ini@3.637.0': + resolution: {integrity: sha512-h+PFCWfZ0Q3Dx84SppET/TFpcQHmxFW8/oV9ArEvMilw4EBN+IlxgbL0CnHwjHW64szcmrM0mbebjEfHf4FXmw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.637.0 + + '@aws-sdk/credential-provider-ini@3.645.0': + resolution: {integrity: sha512-LlZW0qwUwNlTaAIDCNpLbPsyXvS42pRIwF92fgtCQedmdnpN3XRUC6hcwSYI7Xru3GGKp3RnceOvsdOaRJORsw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.645.0 + + '@aws-sdk/credential-provider-node@3.387.0': + resolution: {integrity: sha512-NviQ0EqigPWwX4avKheRzE2R4YPzO6qzdyxKZUookr+uTWYroQ4ePZbHK1/BD8LlqKKBlttX/d3ENXjynU4clA==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/credential-provider-node@3.600.0': + resolution: {integrity: sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-node@3.609.0': + resolution: {integrity: sha512-4J8/JRuqfxJDGD9jTHVCBxCvYt7/Vgj2Stlhj930mrjFPO/yRw8ilAAZxBWe0JHPX3QwepCmh4ErZe53F5ysxQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-node@3.620.1': + resolution: {integrity: sha512-KaprIJW2azM+oTIHi7S1ayJ3oQqoFwpMBWFpZM1nvSzaPucrZIUmX2m4uVrMM4LfXsfUsgMkrme2rBI1fGAjCg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-node@3.637.0': + resolution: {integrity: sha512-yoEhoxJJfs7sPVQ6Is939BDQJZpZCoUgKr/ySse4YKOZ24t4VqgHA6+wV7rYh+7IW24Rd91UTvEzSuHYTlxlNA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-node@3.645.0': + resolution: {integrity: sha512-eGFFuNvLeXjCJf5OCIuSEflxUowmK+bCS+lK4M8ofsYOEGAivdx7C0UPxNjHpvM8wKd8vpMl5phTeS9BWX5jMQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-process@3.387.0': + resolution: {integrity: sha512-tQScLHmDlqkQN+mqw4s3cxepEUeHYDhFl5eH+J8puvPqWjXMYpCEdY79SAtWs6SZd4CWiZ0VLeYU6xQBZengbQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/credential-provider-process@3.598.0': + resolution: {integrity: sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-process@3.609.0': + resolution: {integrity: sha512-Ux35nGOSJKZWUIM3Ny0ROZ8cqPRUEkh+tR3X2o9ydEbFiLq3eMMyEnHJqx4EeUjLRchidlm4CCid9GxMe5/gdw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-process@3.620.1': + resolution: {integrity: sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.387.0': + resolution: {integrity: sha512-sQgrEyTSrwLe8zgjP9VEUDz3dtGXSCc4k00bCwODbzdOWCA1nz9oF2tFmgjFsb1Q80pae01Pe50Esix5z2eHsQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/credential-provider-sso@3.598.0': + resolution: {integrity: sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.609.0': + resolution: {integrity: sha512-oQPGDKMMIxjvTcm86g07RPYeC7mCNk+29dPpY15ZAPRpAF7F0tircsC3wT9fHzNaKShEyK5LuI5Kg/uxsdy+Iw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.620.1': + resolution: {integrity: sha512-cFU8e6ctdkWR8BRCnHFzs37N+ilbHf1OT2EeMjt1ZDE9FgTD5L5BTgVWDxnPmyQnEoBs1p4PyNPHkpHY5EmswQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.637.0': + resolution: {integrity: sha512-Mvz+h+e62/tl+dVikLafhv+qkZJ9RUb8l2YN/LeKMWkxQylPT83CPk9aimVhCV89zth1zpREArl97+3xsfgQvA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.645.0': + resolution: {integrity: sha512-d6XuChAl5NCsCrUexc6AFb4efPmb9+66iwPylKG+iMTMYgO1ackfy1Q2/f35jdn0jolkPkzKsVyfzsEVoID6ew==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.387.0': + resolution: {integrity: sha512-6ueMPl+J3KWv6ZaAWF4Z138QCuBVFZRVAgwbtP3BNqWrrs4Q6TPksOQJ79lRDMpv0EUoyVl04B6lldNlhN8RdA==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.598.0': + resolution: {integrity: sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.598.0 + + '@aws-sdk/credential-provider-web-identity@3.609.0': + resolution: {integrity: sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.609.0 + + '@aws-sdk/credential-provider-web-identity@3.621.0': + resolution: {integrity: sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.621.0 + + '@aws-sdk/credential-providers@3.609.0': + resolution: {integrity: sha512-bJKMY4QwRVderh8R2s9kukoZhuNZew/xzwPa9DRRFVOIsznsS0faAdmAAFrKb8e06YyQq6DiZP0BfFyVHAXE2A==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/endpoint-cache@3.572.0': + resolution: {integrity: sha512-CzuRWMj/xtN9p9eP915nlPmlyniTzke732Ow/M60++gGgB3W+RtZyFftw3TEx+NzNhd1tH54dEcGiWdiNaBz3Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-bucket-endpoint@3.387.0': + resolution: {integrity: sha512-o7Dsq0YTUHFcKXD6+30/fXv/Wzdxqz9WonhCu3ZFPwTDLZgOM4QDDKW8EcC1SplKP1IUyaEli8Affodag9T1cQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-bucket-endpoint@3.598.0': + resolution: {integrity: sha512-PM7BcFfGUSkmkT6+LU9TyJiB4S8yI7dfuKQDwK5ZR3P7MKaK4Uj4yyDiv0oe5xvkF6+O2+rShj+eh8YuWkOZ/Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-endpoint-discovery@3.598.0': + resolution: {integrity: sha512-TaFo3rfapVP0FiddH2zDyA5R5XNk2M+zMeUZaBRveYamSQ11F+fMGcedBgbOsv7yNESvaZvjlcw2K+cx3jOchA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-endpoint-discovery@3.620.0': + resolution: {integrity: sha512-T6kuydHBF4BPP5CVH53Fze7c2b9rqxWP88XrGtmNMXXdY4sXur1v/itGdS2l3gqRjxKo0LsmjmuQm9zL4vGneQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-expect-continue@3.387.0': + resolution: {integrity: sha512-w415a4tjQc6a7isq0AEDWFBC0HWUCHXEDjDl94UACxfMmS9bVabuf04t9CQ+nBBVs6HdiNdfdc/pBR2pRwx2Yg==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-expect-continue@3.598.0': + resolution: {integrity: sha512-ZuHW18kaeHR8TQyhEOYMr8VwiIh0bMvF7J1OTqXHxDteQIavJWA3CbfZ9sgS4XGtrBZDyHJhjZKeCfLhN2rq3w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.387.0': + resolution: {integrity: sha512-QlH97rrKlcMyLG+2ps7+DtBHfPyRIpi7sD3y0iko2u3PGXk+PoLPK8wWyGql9sFopOYTl6/Jh2Rb1b6z6NbjEA==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.598.0': + resolution: {integrity: sha512-xukAzds0GQXvMEY9G6qt+CzwVzTx8NyKKh04O2Q+nOch6QQ8Rs+2kTRy3Z4wQmXq2pK9hlOWb5nXA7HWpmz6Ng==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-host-header@3.387.0': + resolution: {integrity: sha512-EWm9PXSr8dSp7hnRth1U7OfelXQp9dLf1yS1kUL+UhppYDJpjhdP7ql3NI4xJKw8e76sP2FuJYEuzWnJHuWoyQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-host-header@3.598.0': + resolution: {integrity: sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-host-header@3.609.0': + resolution: {integrity: sha512-iTKfo158lc4jLDfYeZmYMIBHsn8m6zX+XB6birCSNZ/rrlzAkPbGE43CNdKfvjyWdqgLMRXF+B+OcZRvqhMXPQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-host-header@3.620.0': + resolution: {integrity: sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-location-constraint@3.387.0': + resolution: {integrity: sha512-Ipdry2V58CpDcWD0ZTz6yFtpTASEBxbuWdqUUYW7pOkZ/5GPGH8NhBky7M38iGqAO6FNysvWEVCUpIqNGkI1lw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-location-constraint@3.598.0': + resolution: {integrity: sha512-8oybQxN3F1ISOMULk7JKJz5DuAm5hCUcxMW9noWShbxTJuStNvuHf/WLUzXrf8oSITyYzIHPtf8VPlKR7I3orQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-logger@3.387.0': + resolution: {integrity: sha512-FjAvJr1XyaInT81RxUwgifnbXoFJrRBFc64XeFJgFanGIQCWLYxRrK2HV9eBpao/AycbmuoHgLd/f0sa4hZFoQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-logger@3.598.0': + resolution: {integrity: sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-logger@3.609.0': + resolution: {integrity: sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.387.0': + resolution: {integrity: sha512-ZF45T785ru8OwvYZw6awD9Z76OwSMM1eZzj2eY+FDz1cHfkpLjxEiti2iIH1FxbyK7n9ZqDUx29lVlCv238YyQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.598.0': + resolution: {integrity: sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.609.0': + resolution: {integrity: sha512-6sewsYB7/o/nbUfA99Aa/LokM+a/u4Wpm/X2o0RxOsDtSB795ObebLJe2BxY5UssbGaWkn7LswyfvrdZNXNj1w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.620.0': + resolution: {integrity: sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.387.0': + resolution: {integrity: sha512-OIUBDzGhglI6KjXVwPLh7hRbrfCpSTwWRkbXbLrPgZZuzWMoJJ3q59RVkpLnAV9Mdkg6+YA6JTw4k4hcmJblVw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.598.0': + resolution: {integrity: sha512-5AGtLAh9wyK6ANPYfaKTqJY1IFJyePIxsEbxa7zS6REheAqyVmgJFaGu3oQ5XlxfGr5Uq59tFTRkyx26G1HkHA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.622.0': + resolution: {integrity: sha512-tX9wZ2ALx5Ez4bkY+SvSj6DpNZ6TmY4zlsVsdgV95LZFLjNwqnZkKkS+uKnsIyLBiBp6g92JVQwnUEIp7ov2Zw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-sdk-sqs@3.598.0': + resolution: {integrity: sha512-BVHR5cKwxXTovezHHPzP7iSNZQdMp+Pn9l2zVfFzryE2Enahkg5oxgUcLD2jeFx14QxS1k7czIEIvKh991CWsg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-sdk-sts@3.387.0': + resolution: {integrity: sha512-7ZzRKOJ4V/JDQmKz9z+FjZqw59mrMATEMLR6ff0H0JHMX0Uk5IX8TQB058ss+ar14qeJ4UcteYzCqHNI0O1BHw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-signing@3.387.0': + resolution: {integrity: sha512-oJXlE0MES8gxNLo137PPNNiOICQGOaETTvq3kBSJgb/gtEAxQajMIlaNT7s1wsjOAruFHt4975nCXuY4lpx7GQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-signing@3.598.0': + resolution: {integrity: sha512-XKb05DYx/aBPqz6iCapsCbIl8aD8EihTuPCs51p75QsVfbQoVr4TlFfIl5AooMSITzojdAQqxt021YtvxjtxIQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-ssec@3.387.0': + resolution: {integrity: sha512-Jtie1gqqcs7ZuYDlz/kuI3CKCXoCL5Ov/Gj5X8/XmwrQJEpuB6z0KY5H1qY0xo+jtAhC8nDPv0GnuLoOfn85hw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-ssec@3.598.0': + resolution: {integrity: sha512-f0p2xP8IC1uJ5e/tND1l81QxRtRFywEdnbtKCE0H6RSn4UIt2W3Dohe1qQDbnh27okF0PkNW6BJGdSAz3p7qbA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.387.0': + resolution: {integrity: sha512-hTfFTwDtp86xS98BKa+RFuLfcvGftxwzrbZeisZV8hdb4ZhvNXjSxnvM3vetW0GUEnY9xHPSGyp2ERRTinPKFQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-user-agent@3.598.0': + resolution: {integrity: sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.609.0': + resolution: {integrity: sha512-nbq7MXRmeXm4IDqh+sJRAxGPAq0OfGmGIwKvJcw66hLoG8CmhhVMZmIAEBDFr57S+YajGwnLLRt+eMI05MMeVA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.620.0': + resolution: {integrity: sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.637.0': + resolution: {integrity: sha512-EYo0NE9/da/OY8STDsK2LvM4kNa79DBsf4YVtaG4P5pZ615IeFsD8xOHZeuJmUrSMlVQ8ywPRX7WMucUybsKug==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.645.0': + resolution: {integrity: sha512-NpTAtqWK+49lRuxfz7st9for80r4NriCMK0RfdJSoPFVntjsSQiQ7+2nW2XL05uVY633e9DvCAw8YatX3zd1mw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/region-config-resolver@3.598.0': + resolution: {integrity: sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/region-config-resolver@3.609.0': + resolution: {integrity: sha512-lMHBG8zg9GWYBc9/XVPKyuAUd7iKqfPP7z04zGta2kGNOKbUTeqmAdc1gJGku75p4kglIPlGBorOxti8DhRmKw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/region-config-resolver@3.614.0': + resolution: {integrity: sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/s3-request-presigner@3.623.0': + resolution: {integrity: sha512-xdY7x4GQ3jVhkge0I8P2V/18p2unP3AD0m1zvacgFmxZ8tptjVpEg2fwR39gKv3pfri0DdfiPDrVONsPC2KlLw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.387.0': + resolution: {integrity: sha512-SGuUbEFi8BXYVv4M7Hc0488I7uZbTVBW19j/B7bnyfbKFrndBXM366s/mChx4iELtESQ61AAstyafx5nGj5tIg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@aws-sdk/signature-v4-crt': ^3.118.0 + peerDependenciesMeta: + '@aws-sdk/signature-v4-crt': + optional: true + + '@aws-sdk/signature-v4-multi-region@3.598.0': + resolution: {integrity: sha512-1r/EyTrO1gSa1FirnR8V7mabr7gk+l+HkyTI0fcTSr8ucB7gmYyW6WjkY8JCz13VYHFK62usCEDS7yoJoJOzTA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.622.0': + resolution: {integrity: sha512-K7ddofVNzwTFRjmLZLfs/v+hiE9m5LguajHk8WULxXQgkcDI3nPgOfmMMGuslYohaQhRwW+ic+dzYlateLUudQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/token-providers@3.387.0': + resolution: {integrity: sha512-W9lPW6zR8yrfvDDLJnKCvHs2KwmydSo+1bG5i6WzFnY3aeOgPBJO2eDIJajZG8Q/L++ZwDaNDLL+ROnIMcg6GA==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/token-providers@3.598.0': + resolution: {integrity: sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sso-oidc': ^3.598.0 + + '@aws-sdk/token-providers@3.609.0': + resolution: {integrity: sha512-WvhW/7XSf+H7YmtiIigQxfDVZVZI7mbKikQ09YpzN7FeN3TmYib1+0tB+EE9TbICkwssjiFc71FEBEh4K9grKQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sso-oidc': ^3.609.0 + + '@aws-sdk/token-providers@3.614.0': + resolution: {integrity: sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sso-oidc': ^3.614.0 + + '@aws-sdk/types@3.387.0': + resolution: {integrity: sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/types@3.598.0': + resolution: {integrity: sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/types@3.609.0': + resolution: {integrity: sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-arn-parser@3.310.0': + resolution: {integrity: sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/util-arn-parser@3.568.0': + resolution: {integrity: sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-dynamodb@3.602.0': + resolution: {integrity: sha512-0QsRLE4cK0h9jseCbaBZpcLzBcOgPMdsEy4wIYvcXivHIdgt/JQxs329NF7EfWQU8h3PU5hRy3tiTBLuB4TmgQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-dynamodb': ^3.602.0 + + '@aws-sdk/util-dynamodb@3.637.0': + resolution: {integrity: sha512-C2q8HcGRiahtf46Mhaqydh1gofeksj7m74PJXHYKW+pKBMLPlpou1+w2o5QSpVEp0dSBtKw30eRVQzxhqg/ACA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-dynamodb': ^3.637.0 + + '@aws-sdk/util-dynamodb@3.648.0': + resolution: {integrity: sha512-w8cF5Ap8AL6VvA8bIbDNnrfpVvN3klsZRQ/QLVAhW1k3R3t9L+eKzoS3bBTVeyBlIh/eyXnSkQ8eduehS82FMw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-dynamodb': ^3.648.0 + + '@aws-sdk/util-dynamodb@3.658.1': + resolution: {integrity: sha512-lzlnis+35a2OhGZlVJvM3/30iIVoP2cIv5Bkw1F2nkM6Pr+1NOd3XvYhCY1Ud5zWtV6HUSptzessvUPqJTMfjQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-dynamodb': ^3.658.1 + + '@aws-sdk/util-endpoints@3.387.0': + resolution: {integrity: sha512-g7kvuCXehGXHHBw9PkSQdwVyDFmNUZLmfrRmqMyrMDG9QLQrxr4pyWcSaYgTE16yUzhQQOR+QSey+BL6W9/N6g==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/util-endpoints@3.598.0': + resolution: {integrity: sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-endpoints@3.609.0': + resolution: {integrity: sha512-Rh+3V8dOvEeE1aQmUy904DYWtLUEJ7Vf5XBPlQ6At3pBhp+zpXbsnpZzVL33c8lW1xfj6YPwtO6gOeEsl1juCQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-endpoints@3.614.0': + resolution: {integrity: sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-endpoints@3.637.0': + resolution: {integrity: sha512-pAqOKUHeVWHEXXDIp/qoMk/6jyxIb6GGjnK1/f8dKHtKIEs4tKsnnL563gceEvdad53OPXIt86uoevCcCzmBnw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-endpoints@3.645.0': + resolution: {integrity: sha512-Oe+xaU4ic4PB1k3pb5VTC1/MWES13IlgpaQw01bVHGfwP6Yv6zZOxizRzca2Y3E+AyR+nKD7vXtHRY+w3bi4bg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-format-url@3.609.0': + resolution: {integrity: sha512-fuk29BI/oLQlJ7pfm6iJ4gkEpHdavffAALZwXh9eaY1vQ0ip0aKfRTiNudPoJjyyahnz5yJ1HkmlcDitlzsOrQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-locate-window@3.568.0': + resolution: {integrity: sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-user-agent-browser@3.387.0': + resolution: {integrity: sha512-lpgSVvDqx+JjHZCTYs/yQSS7J71dPlJeAlvxc7bmx5m+vfwKe07HAnIs+929DngS0QbAp/VaXbTiMFsInLkO4Q==} + + '@aws-sdk/util-user-agent-browser@3.598.0': + resolution: {integrity: sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==} + + '@aws-sdk/util-user-agent-browser@3.609.0': + resolution: {integrity: sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==} + + '@aws-sdk/util-user-agent-node@3.387.0': + resolution: {integrity: sha512-r9OVkcWpRYatjLhJacuHFgvO2T5s/Nu5DDbScMrkUD8b4aGIIqsrdZji0vZy9FCjsUFQMM92t9nt4SejrGjChA==} + engines: {node: '>=14.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/util-user-agent-node@3.598.0': + resolution: {integrity: sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==} + engines: {node: '>=16.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/util-user-agent-node@3.609.0': + resolution: {integrity: sha512-DlZBwQ/HkZyf3pOWc7+wjJRk5R7x9YxHhs2szHwtv1IW30KMabjjjX0GMlGJ9LLkBHkbaaEY/w9Tkj12XRLhRg==} + engines: {node: '>=16.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/util-user-agent-node@3.614.0': + resolution: {integrity: sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==} + engines: {node: '>=16.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/util-utf8-browser@3.259.0': + resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} + + '@aws-sdk/xml-builder@3.310.0': + resolution: {integrity: sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/xml-builder@3.598.0': + resolution: {integrity: sha512-ZIa2RK7CHFTZ4gwK77WRtsZ6vF7xwRXxJ8KQIxK2duhoTVcn0xYxpFLdW9WZZZvdP9GIF3Loqvf8DRdeU5Jc7Q==} + engines: {node: '>=16.0.0'} + + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.24.7': + resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.24.7': + resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.24.7': + resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.24.7': + resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-environment-visitor@7.24.7': + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-function-name@7.24.7': + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-hoist-variables@7.24.7': + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.24.7': + resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-split-export-declaration@7.24.7': + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.24.7': + resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.24.7': + resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.24.7': + resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.24.7': + resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/template@7.24.7': + resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.24.7': + resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.24.7': + resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} + engines: {node: '>=6.9.0'} + + '@balena/dockerignore@1.0.2': + resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} + + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@dabh/diagnostics@2.0.3': + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + + '@es-joy/jsdoccomment@0.36.1': + resolution: {integrity: sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==} + engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.0': + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.0': + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@ewoudenberg/difflib@0.1.0': + resolution: {integrity: sha512-OU5P5mJyD3OoWYMWY+yIgwvgNS9cFAU10f+DDuvtogcWQOoJIsQ4Hy2McSfUfhKjq8L0FuWVb4Rt7kgA+XK86A==} + + '@faker-js/faker@8.4.1': + resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} + + '@humanwhocodes/config-array@0.11.14': + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.4.15': + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@jsdevtools/ono@7.1.3': + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + + '@liuli-util/fs-extra@0.1.0': + resolution: {integrity: sha512-eaAyDyMGT23QuRGbITVY3SOJff3G9ekAAyGqB9joAnTBmqvFN+9a1FazOdO70G6IUqgpKV451eBHYSRcOJ/FNQ==} + + '@mongodb-js/saslprep@1.1.7': + resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@pagopa/eslint-config@3.0.0': + resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} + + '@pagopa/interop-outbound-models@1.0.4-b': + resolution: {integrity: sha512-c1kxjIBCU0gqV2s3bLIdor+fIfvGLZgML2Y26dTdY3ppoXOhJBVTWKVBu5mq8nmZvZj0KnmK7tjeD6NUJjlywg==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@protobuf-ts/plugin-framework@2.9.4': + resolution: {integrity: sha512-9nuX1kjdMliv+Pes8dQCKyVhjKgNNfwxVHg+tx3fLXSfZZRcUHMc1PMwB9/vTvc6gBKt9QGz5ERqSqZc0++E9A==} + + '@protobuf-ts/plugin@2.9.4': + resolution: {integrity: sha512-Db5Laq5T3mc6ERZvhIhkj1rn57/p8gbWiCKxQWbZBBl20wMuqKoHbRw4tuD7FyXi+IkwTToaNVXymv5CY3E8Rw==} + hasBin: true + + '@protobuf-ts/protoc@2.9.4': + resolution: {integrity: sha512-hQX+nOhFtrA+YdAXsXEDrLoGJqXHpgv4+BueYF0S9hy/Jq0VRTVlJS1Etmf4qlMt/WdigEes5LOd/LDzui4GIQ==} + hasBin: true + + '@protobuf-ts/runtime-rpc@2.9.4': + resolution: {integrity: sha512-y9L9JgnZxXFqH5vD4d7j9duWvIJ7AShyBRoNKJGhu9Q27qIbchfzli66H9RvrQNIFk5ER7z1Twe059WZGqERcA==} + + '@protobuf-ts/runtime@2.9.4': + resolution: {integrity: sha512-vHRFWtJJB/SiogWDF0ypoKfRIZ41Kq+G9cEFj6Qm1eQaAhJ1LDFvgZ7Ja4tb3iLOQhz0PaoPnnOijF1qmEqTxg==} + + '@puppeteer/browsers@2.2.3': + resolution: {integrity: sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==} + engines: {node: '>=18'} + hasBin: true + + '@redis/bloom@1.2.0': + resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/client@1.5.17': + resolution: {integrity: sha512-IPvU9A31qRCZ7lds/x+ksuK/UMndd0EASveAvCvEtFFKIZjZ+m/a4a0L7S28KEWoR5ka8526hlSghDo4Hrc2Hg==} + engines: {node: '>=14'} + + '@redis/graph@1.1.1': + resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/json@1.0.6': + resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/search@1.1.6': + resolution: {integrity: sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/time-series@1.0.5': + resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@rollup/rollup-android-arm-eabi@4.18.1': + resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.18.1': + resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.18.1': + resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.18.1': + resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': + resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.18.1': + resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.18.1': + resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.18.1': + resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': + resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.18.1': + resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.18.1': + resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.18.1': + resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.18.1': + resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.18.1': + resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.18.1': + resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.18.1': + resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} + cpu: [x64] + os: [win32] + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + + '@sinonjs/fake-timers@11.3.1': + resolution: {integrity: sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==} + + '@sinonjs/samsam@8.0.2': + resolution: {integrity: sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==} + + '@sinonjs/text-encoding@0.7.3': + resolution: {integrity: sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==} + + '@smithy/abort-controller@2.2.0': + resolution: {integrity: sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==} + engines: {node: '>=14.0.0'} + + '@smithy/abort-controller@3.1.1': + resolution: {integrity: sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==} + engines: {node: '>=16.0.0'} + + '@smithy/chunked-blob-reader-native@2.2.0': + resolution: {integrity: sha512-VNB5+1oCgX3Fzs072yuRsUoC2N4Zg/LJ11DTxX3+Qu+Paa6AmbIF0E9sc2wthz9Psrk/zcOlTCyuposlIhPjZQ==} + + '@smithy/chunked-blob-reader-native@3.0.0': + resolution: {integrity: sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg==} + + '@smithy/chunked-blob-reader@2.2.0': + resolution: {integrity: sha512-3GJNvRwXBGdkDZZOGiziVYzDpn4j6zfyULHMDKAGIUo72yHALpE9CbhfQp/XcLNVoc1byfMpn6uW5H2BqPjgaQ==} + + '@smithy/chunked-blob-reader@3.0.0': + resolution: {integrity: sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA==} + + '@smithy/config-resolver@2.2.0': + resolution: {integrity: sha512-fsiMgd8toyUba6n1WRmr+qACzXltpdDkPTAaDqc8QqPBUzO+/JKwL6bUBseHVi8tu9l+3JOK+tSf7cay+4B3LA==} + engines: {node: '>=14.0.0'} + + '@smithy/config-resolver@3.0.4': + resolution: {integrity: sha512-VwiOk7TwXoE7NlNguV/aPq1hFH72tqkHCw8eWXbr2xHspRyyv9DLpLXhq+Ieje+NwoqXrY0xyQjPXdOE6cGcHA==} + engines: {node: '>=16.0.0'} + + '@smithy/config-resolver@3.0.5': + resolution: {integrity: sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==} + engines: {node: '>=16.0.0'} + + '@smithy/core@2.2.4': + resolution: {integrity: sha512-qdY3LpMOUyLM/gfjjMQZui+UTNS7kBRDWlvyIhVOql5dn2J3isk9qUTBtQ1CbDH8MTugHis1zu3h4rH+Qmmh4g==} + engines: {node: '>=16.0.0'} + + '@smithy/core@2.4.0': + resolution: {integrity: sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==} + engines: {node: '>=16.0.0'} + + '@smithy/credential-provider-imds@2.3.0': + resolution: {integrity: sha512-BWB9mIukO1wjEOo1Ojgl6LrG4avcaC7T/ZP6ptmAaW4xluhSIPZhY+/PI5YKzlk+jsm+4sQZB45Bt1OfMeQa3w==} + engines: {node: '>=14.0.0'} + + '@smithy/credential-provider-imds@3.1.3': + resolution: {integrity: sha512-U1Yrv6hx/mRK6k8AncuI6jLUx9rn0VVSd9NPEX6pyYFBfkSkChOc/n4zUb8alHUVg83TbI4OdZVo1X0Zfj3ijA==} + engines: {node: '>=16.0.0'} + + '@smithy/credential-provider-imds@3.2.0': + resolution: {integrity: sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-codec@2.2.0': + resolution: {integrity: sha512-8janZoJw85nJmQZc4L8TuePp2pk1nxLgkxIR0TUjKJ5Dkj5oelB9WtiSSGXCQvNsJl0VSTvK/2ueMXxvpa9GVw==} + + '@smithy/eventstream-codec@3.1.2': + resolution: {integrity: sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==} + + '@smithy/eventstream-serde-browser@2.2.0': + resolution: {integrity: sha512-UaPf8jKbcP71BGiO0CdeLmlg+RhWnlN8ipsMSdwvqBFigl5nil3rHOI/5GE3tfiuX8LvY5Z9N0meuU7Rab7jWw==} + engines: {node: '>=14.0.0'} + + '@smithy/eventstream-serde-browser@3.0.4': + resolution: {integrity: sha512-Eo4anLZX6ltGJTZ5yJMc80gZPYYwBn44g0h7oFq6et+TYr5dUsTpIcDbz2evsOKIZhZ7zBoFWHtBXQ4QQeb5xA==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-config-resolver@2.2.0': + resolution: {integrity: sha512-RHhbTw/JW3+r8QQH7PrganjNCiuiEZmpi6fYUAetFfPLfZ6EkiA08uN3EFfcyKubXQxOwTeJRZSQmDDCdUshaA==} + engines: {node: '>=14.0.0'} + + '@smithy/eventstream-serde-config-resolver@3.0.3': + resolution: {integrity: sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-node@2.2.0': + resolution: {integrity: sha512-zpQMtJVqCUMn+pCSFcl9K/RPNtQE0NuMh8sKpCdEHafhwRsjP50Oq/4kMmvxSRy6d8Jslqd8BLvDngrUtmN9iA==} + engines: {node: '>=14.0.0'} + + '@smithy/eventstream-serde-node@3.0.4': + resolution: {integrity: sha512-mjlG0OzGAYuUpdUpflfb9zyLrBGgmQmrobNT8b42ZTsGv/J03+t24uhhtVEKG/b2jFtPIHF74Bq+VUtbzEKOKg==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-universal@2.2.0': + resolution: {integrity: sha512-pvoe/vvJY0mOpuF84BEtyZoYfbehiFj8KKWk1ds2AT0mTLYFVs+7sBJZmioOFdBXKd48lfrx1vumdPdmGlCLxA==} + engines: {node: '>=14.0.0'} + + '@smithy/eventstream-serde-universal@3.0.4': + resolution: {integrity: sha512-Od9dv8zh3PgOD7Vj4T3HSuox16n0VG8jJIM2gvKASL6aCtcS8CfHZDWe1Ik3ZXW6xBouU+45Q5wgoliWDZiJ0A==} + engines: {node: '>=16.0.0'} + + '@smithy/fetch-http-handler@2.5.0': + resolution: {integrity: sha512-BOWEBeppWhLn/no/JxUL/ghTfANTjT7kg3Ww2rPqTUY9R4yHPXxJ9JhMe3Z03LN3aPwiwlpDIUcVw1xDyHqEhw==} + + '@smithy/fetch-http-handler@3.2.0': + resolution: {integrity: sha512-vFvDxMrc6sO5Atec8PaISckMcAwsCrRhYxwUylg97bRT2KZoumOF7qk5+6EVUtuM1IG9AJV5aqXnHln9ZdXHpg==} + + '@smithy/fetch-http-handler@3.2.4': + resolution: {integrity: sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==} + + '@smithy/hash-blob-browser@2.2.0': + resolution: {integrity: sha512-SGPoVH8mdXBqrkVCJ1Hd1X7vh1zDXojNN1yZyZTZsCno99hVue9+IYzWDjq/EQDDXxmITB0gBmuyPh8oAZSTcg==} + + '@smithy/hash-blob-browser@3.1.2': + resolution: {integrity: sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg==} + + '@smithy/hash-node@2.2.0': + resolution: {integrity: sha512-zLWaC/5aWpMrHKpoDF6nqpNtBhlAYKF/7+9yMN7GpdR8CzohnWfGtMznPybnwSS8saaXBMxIGwJqR4HmRp6b3g==} + engines: {node: '>=14.0.0'} + + '@smithy/hash-node@3.0.3': + resolution: {integrity: sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==} + engines: {node: '>=16.0.0'} + + '@smithy/hash-stream-node@2.2.0': + resolution: {integrity: sha512-aT+HCATOSRMGpPI7bi7NSsTNVZE/La9IaxLXWoVAYMxHT5hGO3ZOGEMZQg8A6nNL+pdFGtZQtND1eoY084HgHQ==} + engines: {node: '>=14.0.0'} + + '@smithy/hash-stream-node@3.1.2': + resolution: {integrity: sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g==} + engines: {node: '>=16.0.0'} + + '@smithy/invalid-dependency@2.2.0': + resolution: {integrity: sha512-nEDASdbKFKPXN2O6lOlTgrEEOO9NHIeO+HVvZnkqc8h5U9g3BIhWsvzFo+UcUbliMHvKNPD/zVxDrkP1Sbgp8Q==} + + '@smithy/invalid-dependency@3.0.3': + resolution: {integrity: sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@3.0.0': + resolution: {integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==} + engines: {node: '>=16.0.0'} + + '@smithy/md5-js@2.2.0': + resolution: {integrity: sha512-M26XTtt9IIusVMOWEAhIvFIr9jYj4ISPPGJROqw6vXngO3IYJCnVVSMFn4Tx1rUTG5BiKJNg9u2nxmBiZC5IlQ==} + + '@smithy/md5-js@3.0.3': + resolution: {integrity: sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==} + + '@smithy/middleware-content-length@2.2.0': + resolution: {integrity: sha512-5bl2LG1Ah/7E5cMSC+q+h3IpVHMeOkG0yLRyQT1p2aMJkSrZG7RlXHPuAgb7EyaFeidKEnnd/fNaLLaKlHGzDQ==} + engines: {node: '>=14.0.0'} + + '@smithy/middleware-content-length@3.0.3': + resolution: {integrity: sha512-Dbz2bzexReYIQDWMr+gZhpwBetNXzbhnEMhYKA6urqmojO14CsXjnsoPYO8UL/xxcawn8ZsuVU61ElkLSltIUQ==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-content-length@3.0.5': + resolution: {integrity: sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-endpoint@2.5.1': + resolution: {integrity: sha512-1/8kFp6Fl4OsSIVTWHnNjLnTL8IqpIb/D3sTSczrKFnrE9VMNWxnrRKNvpUHOJ6zpGD5f62TPm7+17ilTJpiCQ==} + engines: {node: '>=14.0.0'} + + '@smithy/middleware-endpoint@3.0.4': + resolution: {integrity: sha512-whUJMEPwl3ANIbXjBXZVdJNgfV2ZU8ayln7xUM47rXL2txuenI7jQ/VFFwCzy5lCmXScjp6zYtptW5Evud8e9g==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-endpoint@3.1.0': + resolution: {integrity: sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-retry@2.3.1': + resolution: {integrity: sha512-P2bGufFpFdYcWvqpyqqmalRtwFUNUA8vHjJR5iGqbfR6mp65qKOLcUd6lTr4S9Gn/enynSrSf3p3FVgVAf6bXA==} + engines: {node: '>=14.0.0'} + + '@smithy/middleware-retry@3.0.15': + resolution: {integrity: sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-retry@3.0.7': + resolution: {integrity: sha512-f5q7Y09G+2h5ivkSx5CHvlAT4qRR3jBFEsfXyQ9nFNiWQlr8c48blnu5cmbTQ+p1xmIO14UXzKoF8d7Tm0Gsjw==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-serde@2.3.0': + resolution: {integrity: sha512-sIADe7ojwqTyvEQBe1nc/GXB9wdHhi9UwyX0lTyttmUWDJLP655ZYE1WngnNyXREme8I27KCaUhyhZWRXL0q7Q==} + engines: {node: '>=14.0.0'} + + '@smithy/middleware-serde@3.0.3': + resolution: {integrity: sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-stack@2.2.0': + resolution: {integrity: sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA==} + engines: {node: '>=14.0.0'} + + '@smithy/middleware-stack@3.0.3': + resolution: {integrity: sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==} + engines: {node: '>=16.0.0'} + + '@smithy/node-config-provider@2.3.0': + resolution: {integrity: sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg==} + engines: {node: '>=14.0.0'} + + '@smithy/node-config-provider@3.1.3': + resolution: {integrity: sha512-rxdpAZczzholz6CYZxtqDu/aKTxATD5DAUDVj7HoEulq+pDSQVWzbg0btZDlxeFfa6bb2b5tUvgdX5+k8jUqcg==} + engines: {node: '>=16.0.0'} + + '@smithy/node-config-provider@3.1.4': + resolution: {integrity: sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==} + engines: {node: '>=16.0.0'} + + '@smithy/node-http-handler@2.5.0': + resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==} + engines: {node: '>=14.0.0'} + + '@smithy/node-http-handler@3.1.1': + resolution: {integrity: sha512-L71NLyPeP450r2J/mfu1jMc//Z1YnqJt2eSNw7uhiItaONnBLDA68J5jgxq8+MBDsYnFwNAIc7dBG1ImiWBiwg==} + engines: {node: '>=16.0.0'} + + '@smithy/node-http-handler@3.1.4': + resolution: {integrity: sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==} + engines: {node: '>=16.0.0'} + + '@smithy/property-provider@2.2.0': + resolution: {integrity: sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==} + engines: {node: '>=14.0.0'} + + '@smithy/property-provider@3.1.3': + resolution: {integrity: sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==} + engines: {node: '>=16.0.0'} + + '@smithy/protocol-http@2.0.5': + resolution: {integrity: sha512-d2hhHj34mA2V86doiDfrsy2fNTnUOowGaf9hKb0hIPHqvcnShU4/OSc4Uf1FwHkAdYF3cFXTrj5VGUYbEuvMdw==} + engines: {node: '>=14.0.0'} + + '@smithy/protocol-http@3.3.0': + resolution: {integrity: sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==} + engines: {node: '>=14.0.0'} + + '@smithy/protocol-http@4.0.3': + resolution: {integrity: sha512-x5jmrCWwQlx+Zv4jAtc33ijJ+vqqYN+c/ZkrnpvEe/uDas7AT7A/4Rc2CdfxgWv4WFGmEqODIrrUToPN6DDkGw==} + engines: {node: '>=16.0.0'} + + '@smithy/protocol-http@4.1.0': + resolution: {integrity: sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==} + engines: {node: '>=16.0.0'} + + '@smithy/querystring-builder@2.2.0': + resolution: {integrity: sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==} + engines: {node: '>=14.0.0'} + + '@smithy/querystring-builder@3.0.3': + resolution: {integrity: sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==} + engines: {node: '>=16.0.0'} + + '@smithy/querystring-parser@2.2.0': + resolution: {integrity: sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA==} + engines: {node: '>=14.0.0'} + + '@smithy/querystring-parser@3.0.3': + resolution: {integrity: sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==} + engines: {node: '>=16.0.0'} + + '@smithy/service-error-classification@2.1.5': + resolution: {integrity: sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ==} + engines: {node: '>=14.0.0'} + + '@smithy/service-error-classification@3.0.3': + resolution: {integrity: sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==} + engines: {node: '>=16.0.0'} + + '@smithy/shared-ini-file-loader@2.4.0': + resolution: {integrity: sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA==} + engines: {node: '>=14.0.0'} + + '@smithy/shared-ini-file-loader@3.1.3': + resolution: {integrity: sha512-Z8Y3+08vgoDgl4HENqNnnzSISAaGrF2RoKupoC47u2wiMp+Z8P/8mDh1CL8+8ujfi2U5naNvopSBmP/BUj8b5w==} + engines: {node: '>=16.0.0'} + + '@smithy/shared-ini-file-loader@3.1.4': + resolution: {integrity: sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==} + engines: {node: '>=16.0.0'} + + '@smithy/signature-v4@2.3.0': + resolution: {integrity: sha512-ui/NlpILU+6HAQBfJX8BBsDXuKSNrjTSuOYArRblcrErwKFutjrCNb/OExfVRyj9+26F9J+ZmfWT+fKWuDrH3Q==} + engines: {node: '>=14.0.0'} + + '@smithy/signature-v4@3.1.2': + resolution: {integrity: sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==} + engines: {node: '>=16.0.0'} + + '@smithy/signature-v4@4.1.0': + resolution: {integrity: sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==} + engines: {node: '>=16.0.0'} + + '@smithy/smithy-client@2.5.1': + resolution: {integrity: sha512-jrbSQrYCho0yDaaf92qWgd+7nAeap5LtHTI51KXqmpIFCceKU3K9+vIVTUH72bOJngBMqa4kyu1VJhRcSrk/CQ==} + engines: {node: '>=14.0.0'} + + '@smithy/smithy-client@3.1.5': + resolution: {integrity: sha512-x9bL9Mx2CT2P1OiUlHM+ZNpbVU6TgT32f9CmTRzqIHA7M4vYrROCWEoC3o4xHNJASoGd4Opos3cXYPgh+/m4Ww==} + engines: {node: '>=16.0.0'} + + '@smithy/smithy-client@3.2.0': + resolution: {integrity: sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==} + engines: {node: '>=16.0.0'} + + '@smithy/types@2.12.0': + resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==} + engines: {node: '>=14.0.0'} + + '@smithy/types@3.3.0': + resolution: {integrity: sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==} + engines: {node: '>=16.0.0'} + + '@smithy/url-parser@2.2.0': + resolution: {integrity: sha512-hoA4zm61q1mNTpksiSWp2nEl1dt3j726HdRhiNgVJQMj7mLp7dprtF57mOB6JvEk/x9d2bsuL5hlqZbBuHQylQ==} + + '@smithy/url-parser@3.0.3': + resolution: {integrity: sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==} + + '@smithy/util-base64@2.3.0': + resolution: {integrity: sha512-s3+eVwNeJuXUwuMbusncZNViuhv2LjVJ1nMwTqSA0XAC7gjKhqqxRdJPhR8+YrkoZ9IiIbFk/yK6ACe/xlF+hw==} + engines: {node: '>=14.0.0'} + + '@smithy/util-base64@3.0.0': + resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-body-length-browser@2.2.0': + resolution: {integrity: sha512-dtpw9uQP7W+n3vOtx0CfBD5EWd7EPdIdsQnWTDoFf77e3VUf05uA7R7TGipIo8e4WL2kuPdnsr3hMQn9ziYj5w==} + + '@smithy/util-body-length-browser@3.0.0': + resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} + + '@smithy/util-body-length-node@2.3.0': + resolution: {integrity: sha512-ITWT1Wqjubf2CJthb0BuT9+bpzBfXeMokH/AAa5EJQgbv9aPMVfnM76iFIZVFf50hYXGbtiV71BHAthNWd6+dw==} + engines: {node: '>=14.0.0'} + + '@smithy/util-body-length-node@3.0.0': + resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@3.0.0': + resolution: {integrity: sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-config-provider@2.3.0': + resolution: {integrity: sha512-HZkzrRcuFN1k70RLqlNK4FnPXKOpkik1+4JaBoHNJn+RnJGYqaa3c5/+XtLOXhlKzlRgNvyaLieHTW2VwGN0VQ==} + engines: {node: '>=14.0.0'} + + '@smithy/util-config-provider@3.0.0': + resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-defaults-mode-browser@2.2.1': + resolution: {integrity: sha512-RtKW+8j8skk17SYowucwRUjeh4mCtnm5odCL0Lm2NtHQBsYKrNW0od9Rhopu9wF1gHMfHeWF7i90NwBz/U22Kw==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-browser@3.0.15': + resolution: {integrity: sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-browser@3.0.7': + resolution: {integrity: sha512-Q2txLyvQyGfmjsaDbVV7Sg8psefpFcrnlGapDzXGFRPFKRBeEg6OvFK8FljqjeHSaCZ6/UuzQExUPqBR/2qlDA==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-node@2.3.1': + resolution: {integrity: sha512-vkMXHQ0BcLFysBMWgSBLSk3+leMpFSyyFj8zQtv5ZyUBx8/owVh1/pPEkzmW/DR/Gy/5c8vjLDD9gZjXNKbrpA==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-node@3.0.15': + resolution: {integrity: sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-node@3.0.7': + resolution: {integrity: sha512-F4Qcj1fG6MGi2BSWCslfsMSwllws/WzYONBGtLybyY+halAcXdWhcew+mej8M5SKd5hqPYp4f7b+ABQEaeytgg==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-endpoints@2.0.4': + resolution: {integrity: sha512-ZAtNf+vXAsgzgRutDDiklU09ZzZiiV/nATyqde4Um4priTmasDH+eLpp3tspL0hS2dEootyFMhu1Y6Y+tzpWBQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-endpoints@2.0.5': + resolution: {integrity: sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==} + engines: {node: '>=16.0.0'} + + '@smithy/util-hex-encoding@2.2.0': + resolution: {integrity: sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ==} + engines: {node: '>=14.0.0'} + + '@smithy/util-hex-encoding@3.0.0': + resolution: {integrity: sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-middleware@2.2.0': + resolution: {integrity: sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw==} + engines: {node: '>=14.0.0'} + + '@smithy/util-middleware@3.0.3': + resolution: {integrity: sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==} + engines: {node: '>=16.0.0'} + + '@smithy/util-retry@2.2.0': + resolution: {integrity: sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g==} + engines: {node: '>= 14.0.0'} + + '@smithy/util-retry@3.0.3': + resolution: {integrity: sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==} + engines: {node: '>=16.0.0'} + + '@smithy/util-stream@2.2.0': + resolution: {integrity: sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-stream@3.0.5': + resolution: {integrity: sha512-xC3L5PKMAT/Bh8fmHNXP9sdQ4+4aKVUU3EEJ2CF/lLk7R+wtMJM+v/1B4en7jO++Wa5spGzFDBCl0QxgbUc5Ug==} + engines: {node: '>=16.0.0'} + + '@smithy/util-stream@3.1.3': + resolution: {integrity: sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==} + engines: {node: '>=16.0.0'} + + '@smithy/util-uri-escape@2.2.0': + resolution: {integrity: sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-uri-escape@3.0.0': + resolution: {integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==} + engines: {node: '>=16.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@3.0.0': + resolution: {integrity: sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-waiter@2.2.0': + resolution: {integrity: sha512-IHk53BVw6MPMi2Gsn+hCng8rFA3ZmR3Rk7GllxDUW9qFJl/hiSvskn7XldkECapQVkIg/1dHpMAxI9xSTaLLSA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-waiter@3.1.2': + resolution: {integrity: sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==} + engines: {node: '>=16.0.0'} + + '@testcontainers/postgresql@10.9.0': + resolution: {integrity: sha512-Z3K/TFkl/PVE2v8A6yKqgF4pSFk9ilFG02yeGhPswUjmBlcig/rpVOjBQOkQ/yJCcQ/r2RrX3RR+7vr+UO4QlQ==} + + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + + '@tsconfig/node-lts@20.1.3': + resolution: {integrity: sha512-m3b7EP2U+h5tNSpaBMfcTuHmHn04wrgRPQQrfKt75YIPq6kPs2153/KfPHdqkEWGx5pEBvS6rnvToT+yTtC1iw==} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@tsconfig/strictest@2.0.5': + resolution: {integrity: sha512-ec4tjL2Rr0pkZ5hww65c+EEPYwxOi4Ryv+0MtjeaSQRJyq322Q27eOQiFbuNgw2hpL4hB1/W/HBGk3VKS43osg==} + + '@types/adm-zip@0.5.5': + resolution: {integrity: sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==} + + '@types/body-parser@1.19.5': + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + + '@types/buffers@0.1.31': + resolution: {integrity: sha512-wEZBb3o0Kh5RAj3V172vJCcxaCV8C2HJ7YLBBlG5Mwue0g4uRg5LWv8C6ap8MyFbXE6UbYEuvtHY7oTWAPeXEw==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/docker-modem@3.0.6': + resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==} + + '@types/dockerode@3.3.29': + resolution: {integrity: sha512-5PRRq/yt5OT/Jf77ltIdz4EiR9+VLnPF+HpU4xGFwUqmV24Co2HKBNW3w+slqZ1CYchbcDeqJASHDYWzZCcMiQ==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/express-serve-static-core@4.19.5': + resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} + + '@types/express@4.17.21': + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + + '@types/fs-extra@9.0.13': + resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + + '@types/html2json@1.0.1': + resolution: {integrity: sha512-D+cq6HcgfMdrbIpXzxsmJ9mBz9VlyagqaIB9OZVHGrOeyWbwjTLWR2qUGh1w/VZLtG4wy8mp7v4EpJQ1dYqWzw==} + + '@types/http-errors@2.0.4': + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + + '@types/json-diff@1.0.3': + resolution: {integrity: sha512-Qvxm8fpRMv/1zZR3sQWImeRK2mBYJji20xF51Fq9Gt//Ed18u0x6/FNLogLS1xhfUWTEmDyqveJqn95ltB6Kvw==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/jsonwebtoken@9.0.6': + resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} + + '@types/lodash.isequal@4.5.8': + resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} + + '@types/lodash@4.14.196': + resolution: {integrity: sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==} + + '@types/lodash@4.17.6': + resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/multer@1.4.11': + resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==} + + '@types/node@18.19.39': + resolution: {integrity: sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==} + + '@types/node@20.14.6': + resolution: {integrity: sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==} + + '@types/node@20.4.9': + resolution: {integrity: sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==} + + '@types/nodemailer@6.4.15': + resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==} + + '@types/nodemailer@6.4.9': + resolution: {integrity: sha512-XYG8Gv+sHjaOtUpiuytahMy2mM3rectgroNbs6R3djZEKmPNiIJwe9KqOJBGzKKnNZNKvnuvmugBgpq3w/S0ig==} + + '@types/qs@6.9.15': + resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@types/send@0.17.4': + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + + '@types/serve-static@1.15.7': + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + + '@types/sinon@10.0.20': + resolution: {integrity: sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==} + + '@types/sinonjs__fake-timers@8.1.5': + resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} + + '@types/ssh2-sftp-client@9.0.4': + resolution: {integrity: sha512-gnIn56MTB9W3A3hPL/1sHI23t8YwcE3eVYa1O2XjT9vaqimFdtNHxyQiy5Y78+ociQTKazMSD8YyMEO4QjNMrg==} + + '@types/ssh2-streams@0.1.12': + resolution: {integrity: sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==} + + '@types/ssh2@0.5.52': + resolution: {integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==} + + '@types/ssh2@1.15.0': + resolution: {integrity: sha512-YcT8jP5F8NzWeevWvcyrrLB3zcneVjzYY9ZDSMAMboI+2zR1qYWFhwsyOFVzT7Jorn67vqxC0FRiw8YyG9P1ww==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + + '@types/webidl-conversions@7.0.3': + resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} + + '@types/whatwg-url@11.0.5': + resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + + '@typescript-eslint/eslint-plugin@5.62.0': + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@5.62.0': + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@5.62.0': + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/type-utils@5.62.0': + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@5.62.0': + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/typescript-estree@5.62.0': + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@5.62.0': + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@typescript-eslint/visitor-keys@5.62.0': + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + + '@vitest/expect@1.6.0': + resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} + + '@vitest/runner@1.6.0': + resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} + + '@vitest/snapshot@1.6.0': + resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} + + '@vitest/spy@1.6.0': + resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} + + '@vitest/utils@1.6.0': + resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} + + '@zodios/core@10.9.6': + resolution: {integrity: sha512-aH4rOdb3AcezN7ws8vDgBfGboZMk2JGGzEq/DtW65MhnRxyTGRuLJRWVQ/2KxDgWvV2F5oTkAS+5pnjKbl0n+A==} + peerDependencies: + axios: ^0.x || ^1.0.0 + zod: ^3.x + + '@zodios/express@10.6.1': + resolution: {integrity: sha512-FNgOq8mvwvWP5B2howMKGm6EPp6i/0XFAsQnX5Ov3MLbanzD1oE4WJtBkTL3cmJYvD0nyykbWSeHOh51bCmhUA==} + peerDependencies: + '@zodios/core': '>=10.4.4 <11.0.0' + express: 4.x + zod: ^3.x + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + acorn-jsx@2.0.1: + resolution: {integrity: sha512-rbNtu2WkMJAZNnw2rh35whZO2e2N8Q1Dp4PBf/pKJAals6uFbPvVgVcKZ8poUnrkF50thOea1ApmF8W56apnwA==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} + engines: {node: '>=0.4.0'} + + acorn@2.7.0: + resolution: {integrity: sha512-pXK8ez/pVjqFdAgBkF1YPVRacuLQ9EXBKaKWaeh58WNfMkCmZhOZzu+NtKSPD5PHmCCHheQ5cD29qM1K4QTxIg==} + engines: {node: '>=0.4.0'} + hasBin: true + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + adm-zip@0.5.15: + resolution: {integrity: sha512-jYPWSeOA8EFoZnucrKCNihqBjoEGQSU4HKgHYQgKNEQ0pQF9a/DYuo/+fAxY76k4qe75LUlLWpAM1QWcBMTOKw==} + engines: {node: '>=12.0'} + + agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.16.0: + resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + append-field@1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + + archiver-utils@2.1.0: + resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} + engines: {node: '>= 6'} + + archiver-utils@3.0.4: + resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} + engines: {node: '>= 10'} + + archiver@5.3.2: + resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} + engines: {node: '>= 10'} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.5: + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + + array.prototype.toreversed@1.1.2: + resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + + asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + + assert-options@0.8.1: + resolution: {integrity: sha512-5lNGRB5g5i2bGIzb+J1QQE1iKU/WEMVBReFIc5pPDWjcPj23otPL0eI6PB2v7QPi0qU6Mhym5D3y0ZiSIOf3GA==} + engines: {node: '>=10.0.0'} + + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + + ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + + async-lock@1.4.1: + resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} + + async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + aws-msk-iam-sasl-signer-js@1.0.0: + resolution: {integrity: sha512-L0Jk0k2XNHMSGipJ8rRdTq51KrH/gwrfZ39iKY9BWHGOAv7EygsG4qJC7lIRsbu5/ZHB886Z3WsOsFxqR2R4XQ==} + engines: {node: '>=14.x'} + + aws-sdk-client-mock@4.0.1: + resolution: {integrity: sha512-yD2mmgy73Xce097G5hIpr1k7j50qzvJ49/+6osGZiCyk4m6cwhb+2x7kKFY1gEMwTzaS8+m8fXv9SB29SkRYyQ==} + + axios-logger@2.8.1: + resolution: {integrity: sha512-Bbl7XRR/Rkxg2Owv/kRgAZ/0qf8kMPLc08LtiUcGCWV5RmoI7vHr+eee6SUc8jRi2nE5KWShziCVh35C1SBEaw==} + + axios@1.7.4: + resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==} + + b4a@1.6.6: + resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bare-events@2.4.2: + resolution: {integrity: sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==} + + bare-fs@2.3.1: + resolution: {integrity: sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==} + + bare-os@2.4.0: + resolution: {integrity: sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==} + + bare-path@2.1.3: + resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==} + + bare-stream@2.1.3: + resolution: {integrity: sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + basic-ftp@5.0.5: + resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} + engines: {node: '>=10.0.0'} + + bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + bowser@2.11.0: + resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.23.1: + resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bson@6.8.0: + resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} + engines: {node: '>=16.20.1'} + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buildcheck@0.0.6: + resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==} + engines: {node: '>=10.0.0'} + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + + byline@5.0.0: + resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} + engines: {node: '>=0.10.0'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001640: + resolution: {integrity: sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==} + + chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + engines: {node: '>=4'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + chromium-bidi@0.5.23: + resolution: {integrity: sha512-1o/gLU9wDqbN5nL2MtfjykjOuighGXc3/hnWueO1haiEoFgX8h5vbvcA4tgdQfjw1mkZ1OEF4x/+HVeqEX6NoA==} + peerDependencies: + devtools-protocol: '*' + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + + colors@1.4.0: + resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} + engines: {node: '>=0.1.90'} + + colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comment-parser@1.3.1: + resolution: {integrity: sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==} + engines: {node: '>= 12.0.0'} + + compress-commons@4.1.2: + resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} + engines: {node: '>= 10'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + + concat-stream@2.0.0: + resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} + engines: {'0': node >= 6.0} + + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + + connection-string@4.4.0: + resolution: {integrity: sha512-D4xsUjSoE8m/B5yMOvCIHY+2ME6FIZhCq0NzBBT57Q8BuL7ArFhBK04osOfReoW4KFr5ztzFwWRdmnv9rCvu2w==} + engines: {node: '>=14'} + + console-assert@1.0.0: + resolution: {integrity: sha512-YtowQtZLdzPUlXL+kxMEBclXVOrWzR/+9TAUbIdgnjCkRW8+Dj0y4ajMJtOoQFXEubMONX0fkFS3SNLxx4FQRA==} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cpu-features@0.0.10: + resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==} + engines: {node: '>=10.0.0'} + + cpx2@7.0.1: + resolution: {integrity: sha512-ZgK/DRvPFM5ATZ5DQ5UzY6ajkBrI/p9Uc7VyLHc7b4OSFeBO4yOQz/GEmccc4Om6capGYlY4K1XX+BtYQiPPIA==} + engines: {node: '>=18', npm: '>=10'} + hasBin: true + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@4.0.3: + resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} + engines: {node: '>= 10'} + + create-eslint-index@1.0.0: + resolution: {integrity: sha512-nXvJjnfDytOOaPOonX0h0a1ggMoqrhdekGeZkD6hkcWYvlCWhU719tKFVh8eU04CnMwu3uwe1JjwuUF2C3k2qg==} + engines: {node: '>=4.0.0'} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + csv-generate@4.4.1: + resolution: {integrity: sha512-O/einO0v4zPmXaOV+sYqGa02VkST4GP5GLpWBNHEouIU7pF3kpGf3D0kCCvX82ydIY4EKkOK+R8b1BYsRXravg==} + + csv-parse@5.5.6: + resolution: {integrity: sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A==} + + csv-stringify@6.5.1: + resolution: {integrity: sha512-+9lpZfwpLntpTIEpFbwQyWuW/hmI/eHuJZD1XzeZpfZTqkf1fyvBbBLXTJJMsBuuS11uTShMqPwzx4A6ffXgRQ==} + + csv@6.3.2: + resolution: {integrity: sha512-fOm1LBmt4/kjC1RFanNtjSFVjvoh6MS5E/CuQrED5gCfvjHESZD97Fbjfz/W8ZN4wQAxFjzOonATE790UIuLTg==} + engines: {node: '>= 0.1.90'} + + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + + data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + + date-fns-tz@3.1.3: + resolution: {integrity: sha512-ZfbMu+nbzW0mEzC8VZrLiSWvUIaI3aRHeq33mTe7Y38UctKukgqPR4nTDwcwS4d64Gf8GghnVsroBuMY3eiTeA==} + peerDependencies: + date-fns: ^3.0.0 + + date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + + dateformat@3.0.3: + resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} + + debounce@2.1.0: + resolution: {integrity: sha512-OkL3+0pPWCqoBc/nhO9u6TIQNTK44fnBnzuVtJAbp13Naxw9R6u21x+8tVTka87AhDZ3htqZ2pSSsZl9fqL2Wg==} + engines: {node: '>=18'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge-ts@4.3.0: + resolution: {integrity: sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw==} + engines: {node: '>=12.4.0'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + depseek@0.4.1: + resolution: {integrity: sha512-YYfPPajzH9s2qnEva411VJzCMWtArBTfluI9USiKQ+T6xBWFh3C7yPxhaa1KVgJa17v9aRKc+LcRhgxS5/9mOA==} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + devtools-protocol@0.0.1299070: + resolution: {integrity: sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + docker-compose@0.24.8: + resolution: {integrity: sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==} + engines: {node: '>= 6.0.0'} + + docker-modem@3.0.8: + resolution: {integrity: sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==} + engines: {node: '>= 8.0'} + + dockerode@3.3.5: + resolution: {integrity: sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==} + engines: {node: '>= 8.0'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dotenv-flow@4.1.0: + resolution: {integrity: sha512-0cwP9jpQBQfyHwvE0cRhraZMkdV45TQedA8AAUZMsFzvmLcQyc1HPv+oX0OOYwLFjIlvgVepQ+WuQHbqDaHJZg==} + engines: {node: '>= 12.0.0'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + drange@1.1.1: + resolution: {integrity: sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==} + engines: {node: '>=4'} + + dreamopt@0.8.0: + resolution: {integrity: sha512-vyJTp8+mC+G+5dfgsY+r3ckxlz+QMX40VjPQsZc5gxVAxLmi64TBoVkP54A/pRAXMXsbu2GMMBrZPxNv23waMg==} + engines: {node: '>=0.4.0'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.4.818: + resolution: {integrity: sha512-eGvIk2V0dGImV9gWLq8fDfTTsCAeMDwZqEPMr+jMInxZdnp9Us8UpovYpRCf9NQ7VOFgrN2doNSgvISbsbNpxA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.0.19: + resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + + es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-ast-utils@1.1.0: + resolution: {integrity: sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA==} + engines: {node: '>=4'} + + eslint-config-prettier@8.10.0: + resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-module-utils@2.8.1: + resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-extra-rules@0.0.0-development: + resolution: {integrity: sha512-Lib5tzYuLE8IneAYm8LY5oFhAaQ40IgO8BemKZGBpmZgQwgG7zzKLHs+pvUcgn5cjdoPdbZMcr2vTYmuss2l/g==} + engines: {node: '> 0.10.*'} + + eslint-plugin-fp@2.3.0: + resolution: {integrity: sha512-3n2oHibwoIxAht9/+ZaTldhI6brXORgl8oNXqZd+d9xuAQt2SBJ2/aml0oQRMWvXrgsz2WG6wfC++NjzSG3prA==} + engines: {node: '>=4.0.0'} + peerDependencies: + eslint: '>=3' + + eslint-plugin-functional@4.4.1: + resolution: {integrity: sha512-YhSfHS52Si62Sn126g9wGx+XnWMoWhwEt6ctVXfcJj+xMUiggjOqUVMca7fuLNzX8jYiNBIeU1Y0teHGePZ3NA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^8.0.0 + tsutils: ^3.0.0 + typescript: ^3.4.1 || ^4.0.0 + peerDependenciesMeta: + tsutils: + optional: true + typescript: + optional: true + + eslint-plugin-import@2.29.1: + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsdoc@39.9.1: + resolution: {integrity: sha512-Rq2QY6BZP2meNIs48aZ3GlIlJgBqFCmR55+UBvaDkA3ZNQ0SvQXOs2QKkubakEijV8UbIVbVZKsOVN8G3MuqZw==} + engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + + eslint-plugin-prefer-arrow@1.2.3: + resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==} + peerDependencies: + eslint: '>=2.0.0' + + eslint-plugin-prettier@4.2.1: + resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + + eslint-plugin-react@7.34.3: + resolution: {integrity: sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + + eslint-plugin-sonarjs@0.13.0: + resolution: {integrity: sha512-t3m7ta0EspzDxSOZh3cEOJIJVZgN/TlJYaBGnQlK6W/PZNbWep8q4RQskkJkA7/zwNpX0BaoEOSUUrqaADVoqA==} + engines: {node: '>=12'} + peerDependencies: + eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + + espree@3.0.0-alpha-1: + resolution: {integrity: sha512-HIv6P6qCt3ciLWri1KrO7EPigKPetBZwfCf0o9TuAxRBEPoUUisCepsZqvM76xRfQf2sheO4BC5R/w3UKhwx4w==} + engines: {node: '>=0.10.0'} + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eval-estree-expression@2.0.0: + resolution: {integrity: sha512-e1VweC8biANiuLBY1kZSva3PpaiqaL79S9RR9ql9ngidCYM6tsY20xrUBEJsN63A1yVSmO92chPaBpIGBmGsPg==} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + express@4.20.0: + resolution: {integrity: sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==} + engines: {node: '>= 0.10.0'} + + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-xml-parser@4.2.5: + resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==} + hasBin: true + + fast-xml-parser@4.4.1: + resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} + hasBin: true + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + + find-index@0.1.1: + resolution: {integrity: sha512-uJ5vWrfBKMcE6y2Z8834dwEZj9mNGxYa3t3I53OwFeuZ8D9oc2E5zcsrkuhX6h4iYrjhiv0T3szQmxlAV9uxDg==} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + + follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + engines: {node: '>=14'} + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-port@5.1.1: + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} + engines: {node: '>=8'} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + + get-uri@6.0.3: + resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} + engines: {node: '>= 14'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob2base@0.0.12: + resolution: {integrity: sha512-ZyqlgowMbfj2NPjxaZZ/EtsXlOch28FRXgMd64vqZWk1bT9+wvSRLYD1om9M7QfQru51zJPAT17qXm4/zd+9QA==} + engines: {node: '>= 0.10'} + + glob@10.4.3: + resolution: {integrity: sha512-Q38SGlYRpVtDBPSWEylRyctn7uDeTp4NQERTLiCT1FqA9JXPYWqAVmQU6qh4r/zMM5ehxTcbaO8EjhWnvEhmyg==} + engines: {node: '>=18'} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + handlebars@4.7.7: + resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==} + engines: {node: '>=0.4.7'} + hasBin: true + + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + heap@0.2.7: + resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==} + + html2json@1.0.2: + resolution: {integrity: sha512-tCdVt82U+/D1GCXFIoN5VfCzx767065EZJ5B8nStQUGSXU9PQ4L/0kFvF2of3Qsoe9HGaJni+lxOkqakfw0zpA==} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} + engines: {node: '>= 14'} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + + ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + + is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + + is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + + is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + + is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + + is-weakset@2.0.3: + resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + engines: {node: '>= 0.4'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + + jackspeak@3.4.1: + resolution: {integrity: sha512-U23pQPDnmYybVkYjObcuYMk43VRlMLLqLI+RdZy8s8WV8WsxO9SnqSroKaluuvcNOdCAlauKszDwd+umbot5Mg==} + engines: {node: '>=18'} + + jose@4.15.9: + resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} + + jose@5.9.4: + resolution: {integrity: sha512-WBBl6au1qg6OHj67yCffCgFR3BADJBXN8MdRvCgJDuMv3driV2nHr7jdGvaKX9IolosAsn+M0XRArqLXUhyJHQ==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.0: + resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + + jsdoc-type-pratt-parser@3.1.0: + resolution: {integrity: sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==} + engines: {node: '>=12.0.0'} + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-diff@1.0.6: + resolution: {integrity: sha512-tcFIPRdlc35YkYdGxcamJjllUhXWv4n2rK9oJ2RsAzV4FBkuV4ojKEDgcZ+kpKxDmJKv+PFK65+1tVVOnSeEqA==} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + just-extend@6.2.0: + resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==} + + jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + + jwks-rsa@3.1.0: + resolution: {integrity: sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==} + engines: {node: '>=14'} + + jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + + kafkajs@2.2.4: + resolution: {integrity: sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==} + engines: {node: '>=14.0.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + limiter@1.1.5: + resolution: {integrity: sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.difference@4.5.0: + resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} + + lodash.flatten@4.4.0: + resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} + + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isempty@4.4.0: + resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + + lodash.union@4.6.0: + resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} + + lodash.zip@4.2.0: + resolution: {integrity: sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + logform@2.6.0: + resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} + engines: {node: '>= 12.0.0'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + + lru-cache@10.4.0: + resolution: {integrity: sha512-bfJaPTuEiTYBu+ulDaeQ0F+uLmlfFkMgXj4cbwfuMSjgObGMzb55FMMbDvbRU0fAHZ4sLGkz2mKwcMg8Dvm8Ww==} + engines: {node: '>=18'} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + lru-memoizer@2.3.0: + resolution: {integrity: sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==} + + magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + memory-pager@1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mime@4.0.4: + resolution: {integrity: sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==} + engines: {node: '>=16'} + hasBin: true + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + + mnemonist@0.38.3: + resolution: {integrity: sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==} + + mongodb-connection-string-url@3.0.1: + resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + + mongodb@6.7.0: + resolution: {integrity: sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==} + engines: {node: '>=16.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.1.0 + gcp-metadata: ^5.2.0 + kerberos: ^2.0.1 + mongodb-client-encryption: '>=6.0.0 <7' + snappy: ^7.2.2 + socks: ^2.7.1 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + '@mongodb-js/zstd': + optional: true + gcp-metadata: + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + socks: + optional: true + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multer@1.4.5-lts.1: + resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==} + engines: {node: '>= 6.0.0'} + + nan@2.20.0: + resolution: {integrity: sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + + nise@5.1.9: + resolution: {integrity: sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + + nodemailer@6.9.14: + resolution: {integrity: sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==} + engines: {node: '>=6.0.0'} + + nodemailer@6.9.9: + resolution: {integrity: sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==} + engines: {node: '>=6.0.0'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + + object.entries@1.1.8: + resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.hasown@1.1.4: + resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} + engines: {node: '>= 0.4'} + + object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} + + obliterator@1.6.1: + resolution: {integrity: sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + + openapi-zod-client@1.18.1: + resolution: {integrity: sha512-L0GzU/7Sx9ugbWWoQwOJdKtyxr8ZnjxIK2RJP63//OkmKws2w7c5HSgS2bdNxPVCIp/eJuYk+CtaKfvCoJ08Yw==} + hasBin: true + + openapi3-ts@3.1.0: + resolution: {integrity: sha512-1qKTvCCVoV0rkwUh1zq5o8QyghmwYPuhdvtjv1rFjuOnJToXhQyF8eGjNETQ8QmGjr9Jz/tkAKLITIl2s7dw3A==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-map@6.0.0: + resolution: {integrity: sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==} + engines: {node: '>=16'} + + pac-proxy-agent@7.0.2: + resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==} + engines: {node: '>= 14'} + + pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + pastable@2.2.1: + resolution: {integrity: sha512-K4ClMxRKpgN4sXj6VIPPrvor/TMp2yPNCGtfhvV106C73SwefQ3FuegURsH7AQHpqu0WwbvKXRl1HQxF6qax9w==} + engines: {node: '>=14.x'} + peerDependencies: + react: '>=17' + xstate: '>=4.32.1' + peerDependenciesMeta: + react: + optional: true + xstate: + optional: true + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@0.1.10: + resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + + pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + + pg-connection-string@2.6.4: + resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-minify@1.6.4: + resolution: {integrity: sha512-cf6hBt1YqzqPX0OznXKSv4U7e4o7eUU4zp2zQsbJ+4OCNNr7EnnAVWkIz4k0dv6UN4ouS1ZL4WlXxCrZHHl69g==} + engines: {node: '>=14.0.0'} + + pg-pool@3.6.2: + resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==} + peerDependencies: + pg: '>=8.0' + + pg-promise@11.8.0: + resolution: {integrity: sha512-w9hTFpkM4FByJTJ7KCWLtZSOtQa2BKC+XIV8+3ZvDlfYfBYdz8V4V+BttnqhUPY/d12Itug7Bft4XdILihsY+w==} + engines: {node: '>=14.0'} + + pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg@8.11.5: + resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pkg-types@1.1.3: + resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + + possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + + postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + engines: {node: ^10 || ^12 || >=14} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + + properties-reader@2.3.0: + resolution: {integrity: sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==} + engines: {node: '>=14'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-agent@6.4.0: + resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==} + engines: {node: '>= 14'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + puppeteer-core@22.11.2: + resolution: {integrity: sha512-vQo+YDuePyvj+92Z9cdtxi/HalKf+k/R4tE80nGtQqJRNqU81eHaHkbVfnLszdaLlvwFF5tipnnSCzqWlEddtw==} + engines: {node: '>=18'} + + puppeteer@22.11.2: + resolution: {integrity: sha512-8fjdQSgW0sq7471ftca24J7sXK+jXZ7OW7Gx+NEBFNyXrcTiBfukEI46gNq6hiMhbLEDT30NeylK/1ZoPdlKSA==} + engines: {node: '>=18'} + hasBin: true + + qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + + qs@6.12.3: + resolution: {integrity: sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==} + engines: {node: '>=0.6'} + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + queue-tick@1.0.1: + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + + quote@0.4.0: + resolution: {integrity: sha512-KHp3y3xDjuBhRx+tYKOgzPnVHMRlgpn2rU450GcU4PL24r1H6ls/hfPrxDwX2pvYMlwODHI2l8WwgoV69x5rUQ==} + + randexp@0.5.3: + resolution: {integrity: sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==} + engines: {node: '>=4'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + rate-limiter-flexible@5.0.3: + resolution: {integrity: sha512-lWx2y8NBVlTOLPyqs+6y7dxfEpT6YFqKy3MzWbCy95sTTOhOuxufP2QvRyOHpfXpB9OUJPbVLybw3z3AVAS5fA==} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + + redis@4.6.15: + resolution: {integrity: sha512-2NtuOpMW3tnYzBw6S8mbXSX7RPzvVFCA2wFJq9oErushO2UeBkxObk+uvo7gv7n0rhWeOj/IzrHO8TjcFlRSOg==} + + reflect.getprototypeof@1.0.6: + resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + + req-all@0.1.0: + resolution: {integrity: sha512-ZdvPr8uXy9ujX3KujwE2P1HWkMYgogIhqeAeyb47MqWjSfyxERSm0TNbN/IapCCmWDufXab04AYrRgObaJCJ6Q==} + engines: {node: '>=4'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + ret@0.2.2: + resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} + engines: {node: '>=4'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup@4.18.1: + resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + + safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + + semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + engines: {node: '>=10'} + hasBin: true + + send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.0: + resolution: {integrity: sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==} + engines: {node: '>= 0.8.0'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + sinon@16.1.3: + resolution: {integrity: sha512-mjnWWeyxcAf9nC0bXcPmiDut+oE8HYridTNzBbF98AYVLmWwGRp2ISEpyhYflG1ifILT+eNn3BmKUJPxjXUPlA==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.4: + resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==} + engines: {node: '>= 14'} + + socks@2.8.3: + resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.18: + resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} + + spex@3.3.0: + resolution: {integrity: sha512-VNiXjFp6R4ldPbVRYbpxlD35yRHceecVXlct1J4/X80KuuPnW2AXMq3sGwhnJOhKkUsOxAT6nRGfGE5pocVw5w==} + engines: {node: '>=10.0.0'} + + split-ca@1.0.1: + resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + + ssh-remote-port-forward@1.0.4: + resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} + + ssh2-sftp-client@9.1.0: + resolution: {integrity: sha512-Hzdr9OE6GxZjcmyM9tgBSIFVyrHAp9c6U2Y4yBkmYOHoQvZ7pIm27dmltvcmRfxcWiIcg8HBvG5iAikDf+ZuzQ==} + engines: {node: '>=10.24.1'} + + ssh2@1.15.0: + resolution: {integrity: sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==} + engines: {node: '>=10.16.0'} + + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + + stream-transform@3.3.2: + resolution: {integrity: sha512-v64PUnPy9Qw94NGuaEMo+9RHQe4jTBYf+NkTtqkCgeuiNo8NlL0LtLR7fkKWNVFtp3RhIm5Dlxkgm5uz7TDimQ==} + + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + streamx@2.18.0: + resolution: {integrity: sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string.prototype.matchall@4.0.11: + resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@2.1.0: + resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + + strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + + subarg@1.0.0: + resolution: {integrity: sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tanu@0.1.13: + resolution: {integrity: sha512-UbRmX7ccZ4wMVOY/Uw+7ji4VOkEYSYJG1+I4qzbnn4qh/jtvVbrm6BFnF12NQQ4+jGv21wKmjb1iFyUSVnBWcQ==} + + tar-fs@2.0.1: + resolution: {integrity: sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==} + + tar-fs@3.0.5: + resolution: {integrity: sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==} + + tar-fs@3.0.6: + resolution: {integrity: sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + + testcontainers@10.9.0: + resolution: {integrity: sha512-LN+cKAOd61Up9SVMJW+3VFVGeVQG8JBqZhEQo2U0HBfIsAynyAXcsLBSo+KZrOfy9SBz7pGHctWN/KabLDbNFA==} + + text-decoder@1.1.1: + resolution: {integrity: sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==} + + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tinybench@2.8.0: + resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} + + tinypool@0.8.4: + resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} + engines: {node: '>=14.0.0'} + + tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + + tmp@0.2.3: + resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} + engines: {node: '>=14.14'} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@4.1.1: + resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} + engines: {node: '>=14'} + + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + 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 + + ts-pattern@5.2.0: + resolution: {integrity: sha512-aGaSpOlDcns7ZoeG/OMftWyQG1KqPVhgplhJxNCvyIXqWrumM5uIoOSarw/hmmi/T1PnuQ/uD8NaFHvLpHicDg==} + + ts-toolbelt@9.6.0: + resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} + + tsc-esm-fix@2.20.27: + resolution: {integrity: sha512-bfoSY29XN4yRvXgfxc4rtKQPe9Xx02BahWSZ3D4GgBXIWSE+TJ/BXGSrpUIBkrsKIUQv2zA3qiwJVFnUV59Xdw==} + engines: {node: '>=16.0.0'} + hasBin: true + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + + tsutils@3.21.0: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + 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' + + tsx@4.19.1: + resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} + engines: {node: '>=18.0.0'} + hasBin: true + + turbo-darwin-64@2.0.4: + resolution: {integrity: sha512-x9mvmh4wudBstML8Z8IOmokLWglIhSfhQwnh2gBCSqabgVBKYvzl8Y+i+UCNPxheCGTgtsPepTcIaKBIyFIcvw==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.0.4: + resolution: {integrity: sha512-/B1Ih8zPRGVw5vw4SlclOf3C/woJ/2T6ieH6u54KT4wypoaVyaiyMqBcziIXycdObIYr7jQ+raHO7q3mhay9/A==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.0.4: + resolution: {integrity: sha512-6aG670e5zOWu6RczEYcB81nEl8EhiGJEvWhUrnAfNEUIMBEH1pR5SsMmG2ol5/m3PgiRM12r13dSqTxCLcHrVg==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.0.4: + resolution: {integrity: sha512-AXfVOjst+mCtPDFT4tCu08Qrfv12Nj7NDd33AjGwV79NYN1Y1rcFY59UQ4nO3ij3rbcvV71Xc+TZJ4csEvRCSg==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.0.4: + resolution: {integrity: sha512-QOnUR9hKl0T5gq5h1fAhVEqBSjpcBi/BbaO71YGQNgsr6pAnCQdbG8/r3MYXet53efM0KTdOhieWeO3KLNKybA==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.0.4: + resolution: {integrity: sha512-3v8WpdZy1AxZw0gha0q3caZmm+0gveBQ40OspD6mxDBIS+oBtO5CkxhIXkFJJW+jDKmDlM7wXDIGfMEq+QyNCQ==} + cpu: [arm64] + os: [win32] + + turbo@2.0.4: + resolution: {integrity: sha512-Ilme/2Q5kYw0AeRr+aw3s02+WrEYaY7U8vPnqSZU/jaDG/qd6jHVN6nRWyd/9KXvJGYM69vE6JImoGoyNjLwaw==} + hasBin: true + + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + + typescript@3.9.10: + resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.5.3: + resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + + uglify-js@3.18.0: + resolution: {integrity: sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==} + engines: {node: '>=0.8.0'} + hasBin: true + + unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + + unbzip2-stream@1.4.3: + resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + urlpattern-polyfill@10.0.0: + resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite-node@1.6.0: + resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + vite@5.3.3: + resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@1.6.0: + resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 1.6.0 + '@vitest/ui': 1.6.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-url@13.0.0: + resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} + engines: {node: '>=16'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + whence@2.0.1: + resolution: {integrity: sha512-VtcCE1Pe3BKofF/k+P5xcpuoqQ0f1NJY6TmdUw5kInl9/pEr1ZEFD9+ZOUicf52tvpTbhMS93aWXriu2IQYTTw==} + engines: {node: '>=14'} + + which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + + which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + winston-transport@4.7.0: + resolution: {integrity: sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==} + engines: {node: '>= 12.0.0'} + + winston@3.13.0: + resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==} + engines: {node: '>= 12.0.0'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + + zip-stream@4.1.1: + resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} + engines: {node: '>= 10'} + + zod-validation-error@3.3.0: + resolution: {integrity: sha512-Syib9oumw1NTqEv4LT0e6U83Td9aVRk9iTXPUQr1otyV1PuXQKOvOwhMNqZIq5hluzHP2pMgnOmHEo7kPdI2mw==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.18.0 + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@anatine/zod-mock@3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8)': + dependencies: + '@faker-js/faker': 8.4.1 + randexp: 0.5.3 + zod: 3.23.8 + + '@apidevtools/json-schema-ref-parser@9.0.6': + dependencies: + '@jsdevtools/ono': 7.1.3 + call-me-maybe: 1.0.2 + js-yaml: 3.14.1 + + '@apidevtools/openapi-schemas@2.1.0': {} + + '@apidevtools/swagger-methods@3.0.2': {} + + '@apidevtools/swagger-parser@10.1.0(openapi-types@12.1.3)': + dependencies: + '@apidevtools/json-schema-ref-parser': 9.0.6 + '@apidevtools/openapi-schemas': 2.1.0 + '@apidevtools/swagger-methods': 3.0.2 + '@jsdevtools/ono': 7.1.3 + ajv: 8.16.0 + ajv-draft-04: 1.0.0(ajv@8.16.0) + call-me-maybe: 1.0.2 + openapi-types: 12.1.3 + + '@aws-crypto/crc32@3.0.0': + dependencies: + '@aws-crypto/util': 3.0.0 + '@aws-sdk/types': 3.609.0 + tslib: 1.14.1 + + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.609.0 + tslib: 2.6.3 + + '@aws-crypto/crc32c@3.0.0': + dependencies: + '@aws-crypto/util': 3.0.0 + '@aws-sdk/types': 3.609.0 + tslib: 1.14.1 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.609.0 + tslib: 2.6.3 + + '@aws-crypto/ie11-detection@3.0.0': + dependencies: + tslib: 1.14.1 + + '@aws-crypto/sha1-browser@3.0.0': + dependencies: + '@aws-crypto/ie11-detection': 3.0.0 + '@aws-crypto/supports-web-crypto': 3.0.0 + '@aws-crypto/util': 3.0.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-locate-window': 3.568.0 + '@aws-sdk/util-utf8-browser': 3.259.0 + tslib: 1.14.1 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-locate-window': 3.568.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.3 + + '@aws-crypto/sha256-browser@3.0.0': + dependencies: + '@aws-crypto/ie11-detection': 3.0.0 + '@aws-crypto/sha256-js': 3.0.0 + '@aws-crypto/supports-web-crypto': 3.0.0 + '@aws-crypto/util': 3.0.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-locate-window': 3.568.0 + '@aws-sdk/util-utf8-browser': 3.259.0 + tslib: 1.14.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-locate-window': 3.568.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.3 + + '@aws-crypto/sha256-js@3.0.0': + dependencies: + '@aws-crypto/util': 3.0.0 + '@aws-sdk/types': 3.609.0 + tslib: 1.14.1 + + '@aws-crypto/sha256-js@4.0.0': + dependencies: + '@aws-crypto/util': 4.0.0 + '@aws-sdk/types': 3.609.0 + tslib: 1.14.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.609.0 + tslib: 2.6.3 + + '@aws-crypto/supports-web-crypto@3.0.0': + dependencies: + tslib: 1.14.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.6.3 + + '@aws-crypto/util@3.0.0': + dependencies: + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-utf8-browser': 3.259.0 + tslib: 1.14.1 + + '@aws-crypto/util@4.0.0': + dependencies: + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-utf8-browser': 3.259.0 + tslib: 1.14.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.609.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.3 + + '@aws-sdk/client-cognito-identity@3.609.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/core': 3.609.0 + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/middleware-host-header': 3.609.0 + '@aws-sdk/middleware-logger': 3.609.0 + '@aws-sdk/middleware-recursion-detection': 3.609.0 + '@aws-sdk/middleware-user-agent': 3.609.0 + '@aws-sdk/region-config-resolver': 3.609.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-endpoints': 3.609.0 + '@aws-sdk/util-user-agent-browser': 3.609.0 + '@aws-sdk/util-user-agent-node': 3.609.0 + '@smithy/config-resolver': 3.0.5 + '@smithy/core': 2.4.0 + '@smithy/fetch-http-handler': 3.2.4 + '@smithy/hash-node': 3.0.3 + '@smithy/invalid-dependency': 3.0.3 + '@smithy/middleware-content-length': 3.0.5 + '@smithy/middleware-endpoint': 3.1.0 + '@smithy/middleware-retry': 3.0.15 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.4 + '@smithy/node-http-handler': 3.1.4 + '@smithy/protocol-http': 4.1.0 + '@smithy/smithy-client': 3.2.0 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.15 + '@smithy/util-defaults-mode-node': 3.0.15 + '@smithy/util-endpoints': 2.0.5 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.3 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.3 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-dynamodb@3.602.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/core': 3.598.0 + '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/middleware-endpoint-discovery': 3.598.0 + '@aws-sdk/middleware-host-header': 3.598.0 + '@aws-sdk/middleware-logger': 3.598.0 + '@aws-sdk/middleware-recursion-detection': 3.598.0 + '@aws-sdk/middleware-user-agent': 3.598.0 + '@aws-sdk/region-config-resolver': 3.598.0 + '@aws-sdk/types': 3.598.0 + '@aws-sdk/util-endpoints': 3.598.0 + '@aws-sdk/util-user-agent-browser': 3.598.0 + '@aws-sdk/util-user-agent-node': 3.598.0 + '@smithy/config-resolver': 3.0.5 + '@smithy/core': 2.4.0 + '@smithy/fetch-http-handler': 3.2.4 + '@smithy/hash-node': 3.0.3 + '@smithy/invalid-dependency': 3.0.3 + '@smithy/middleware-content-length': 3.0.5 + '@smithy/middleware-endpoint': 3.1.0 + '@smithy/middleware-retry': 3.0.15 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.4 + '@smithy/node-http-handler': 3.1.4 + '@smithy/protocol-http': 4.1.0 + '@smithy/smithy-client': 3.2.0 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.15 + '@smithy/util-defaults-mode-node': 3.0.15 + '@smithy/util-endpoints': 2.0.5 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.3 + '@smithy/util-utf8': 3.0.0 '@smithy/util-waiter': 3.1.2 tslib: 2.6.3 uuid: 9.0.1 transitivePeerDependencies: - aws-crt - /@aws-sdk/client-dynamodb@3.648.0: - resolution: {integrity: sha512-61yU6wQRlwOhD0mfJS/N8SYmv9hxkVYGKsXqSJ5PNNnySutoNof7cmX8cTuijpTQqLL9sKPfvPMlJCv7/M1AiA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-dynamodb@3.637.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.637.0(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/client-sts': 3.637.0 + '@aws-sdk/core': 3.635.0 + '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/middleware-endpoint-discovery': 3.620.0 + '@aws-sdk/middleware-host-header': 3.620.0 + '@aws-sdk/middleware-logger': 3.609.0 + '@aws-sdk/middleware-recursion-detection': 3.620.0 + '@aws-sdk/middleware-user-agent': 3.637.0 + '@aws-sdk/region-config-resolver': 3.614.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-endpoints': 3.637.0 + '@aws-sdk/util-user-agent-browser': 3.609.0 + '@aws-sdk/util-user-agent-node': 3.614.0 + '@smithy/config-resolver': 3.0.5 + '@smithy/core': 2.4.0 + '@smithy/fetch-http-handler': 3.2.4 + '@smithy/hash-node': 3.0.3 + '@smithy/invalid-dependency': 3.0.3 + '@smithy/middleware-content-length': 3.0.5 + '@smithy/middleware-endpoint': 3.1.0 + '@smithy/middleware-retry': 3.0.15 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.4 + '@smithy/node-http-handler': 3.1.4 + '@smithy/protocol-http': 4.1.0 + '@smithy/smithy-client': 3.2.0 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.15 + '@smithy/util-defaults-mode-node': 3.0.15 + '@smithy/util-endpoints': 2.0.5 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.3 + '@smithy/util-utf8': 3.0.0 + '@smithy/util-waiter': 3.1.2 + tslib: 2.6.3 + uuid: 9.0.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-dynamodb@3.648.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/client-sts': 3.645.0 '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0) '@aws-sdk/middleware-endpoint-discovery': 3.620.0 '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 @@ -3048,11 +7906,8 @@ packages: uuid: 9.0.1 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-kms@3.600.0: - resolution: {integrity: sha512-m1o8aiVrVjExw6O+8JszXV3hr8sCyXKOLq1WCwWJqYF6Uf4vCf8iTYISQB3skbKUnBJm4SxVA82iViGAtWB7JA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-kms@3.600.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3097,11 +7952,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-s3@3.387.0: - resolution: {integrity: sha512-TX42MDfXnIy/U6f8XjCTR1Ezg1125Sv5k9kdKZJ0kkKcb/81N0+RfTqsR+Kpn/AbLjtvSUWrFIdc1o//GsfZAQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/client-s3@3.387.0': dependencies: '@aws-crypto/sha1-browser': 3.0.0 '@aws-crypto/sha256-browser': 3.0.0 @@ -3160,11 +8012,8 @@ packages: transitivePeerDependencies: - '@aws-sdk/signature-v4-crt' - aws-crt - dev: false - /@aws-sdk/client-s3@3.600.0: - resolution: {integrity: sha512-iYoKbJTputbf+ubkX6gSK/y/4uJEBRaXZ18jykLdBQ8UJuGrk2gqvV8h7OlGAhToCeysmmMqM0vDWyLt6lP8nw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-s3@3.600.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 @@ -3226,18 +8075,15 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sesv2@3.620.1: - resolution: {integrity: sha512-pu/CbRQuxCA0EmDqyAktc77pjbmeWuQao6aLDBuXLDbccwKgRECLCTscqUcnqtfFhPkiOAim9LdczqEXLNIpcA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sesv2@3.620.1': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.620.1) '@aws-sdk/client-sts': 3.620.1 '@aws-sdk/core': 3.620.1 - '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -3276,9 +8122,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sqs@3.600.0: - resolution: {integrity: sha512-GgjEiWbGbiHGU3yZcCr1hXfaq/B/3ncYclqLEbbxrWkQOdri3qfl278h+Qn0/DQ8On0kj1UUxld87TVIkYfG8w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sqs@3.600.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3325,11 +8169,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0): - resolution: {integrity: sha512-7+I8RWURGfzvChyNQSyj5/tKrqRbzRl7H+BnTOf/4Vsw1nFOi5ROhlhD4X/Y0QCTacxnaoNcIrqnY7uGGvVRzw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3374,19 +8215,14 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sts' - aws-crt - dev: false - /@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-0bNPAyPdkWkS9EGB2A9BZDkBNrnVCBzk5lYRezoT4K3/gi9w1DTYH5tuRdwaTZdxW19U1mq7CV0YJJARKO1L9Q==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 + '@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -3425,66 +8261,13 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1): - resolution: {integrity: sha512-gm69ttbkr7Kbg/Zzr3SczyLWkLgmK3bEZtkvbM/40ZW5ItYhDzJE48Ovs2lyA64h2YsOftDqqwcbJirAAdTgSg==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.620.1 + '@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sts': 3.620.1 '@aws-sdk/core': 3.620.1 - '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0) - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.620.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.614.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - - /@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-gm69ttbkr7Kbg/Zzr3SczyLWkLgmK3bEZtkvbM/40ZW5ItYhDzJE48Ovs2lyA64h2YsOftDqqwcbJirAAdTgSg==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.620.1 - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.645.0 - '@aws-sdk/core': 3.620.1 - '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -3523,17 +8306,13 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0): - resolution: {integrity: sha512-27bHALN6Qb6m6KZmPvRieJ/QRlj1lyac/GT2Rn5kJpre8Mpp+yxrtvp3h9PjNBty4lCeFEENfY4dGNSozBuBcw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.637.0 + '@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sts': 3.637.0 '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0)(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -3572,66 +8351,13 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-X9ULtdk3cO+1ysurEkJ1MSnu6U00qodXx+IVual+1jXX4RYY1WmQmfo7uDKf6FFkz7wW1DAqU+GJIBNQr0YH8A==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.645.0 - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.645.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.645.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - - /@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-X9ULtdk3cO+1ysurEkJ1MSnu6U00qodXx+IVual+1jXX4RYY1WmQmfo7uDKf6FFkz7wW1DAqU+GJIBNQr0YH8A==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.645.0 + '@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sts': 3.645.0 '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -3670,9 +8396,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso@3.387.0: - resolution: {integrity: sha512-E7uKSvbA0XMKSN5KLInf52hmMpe9/OKo6N9OPffGXdn3fNEQlvyQq3meUkqG7Is0ldgsQMz5EUBNtNybXzr3tQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/client-sso@3.387.0': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 @@ -3709,11 +8433,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sso@3.598.0: - resolution: {integrity: sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso@3.598.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3755,11 +8476,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sso@3.609.0: - resolution: {integrity: sha512-gqXGFDkIpKHCKAbeJK4aIDt3tiwJ26Rf5Tqw9JS6BYXsdMeOB8FTzqD9R+Yc1epHd8s5L94sdqXT5PapgxFZrg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso@3.609.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3802,9 +8520,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso@3.620.1: - resolution: {integrity: sha512-4Ox0BSs+atrAhLvjNHN2uiYvSTdpMv//IS4l4XRoQG0cJKIPLs3OU3PL5H0X1NfZehz9/8FTWl5Lv81uw4j1eA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso@3.620.1': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3847,9 +8563,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso@3.637.0: - resolution: {integrity: sha512-+KjLvgX5yJYROWo3TQuwBJlHCY0zz9PsLuEolmXQn0BVK1L/m9GteZHtd+rEdAoDGBpE0Xqjy1oz5+SmtsaRUw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso@3.637.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3892,9 +8606,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso@3.645.0: - resolution: {integrity: sha512-2rc8TjnsNddOeKQ/pfNN7deNvGLXAeKeYtHtGDAiM2qfTKxd2sNcAsZ+JCDLyshuD4xLM5fpUyR0X8As9EAouQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso@3.645.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3937,9 +8649,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sts@3.387.0: - resolution: {integrity: sha512-RPOME0gpAViheH6xHyMg/XkE1G/fs6dgKK/NqlBZDjwMsSTPc8CmItEC6FOsCaLJktif0tD/u9m2uaQ4Lb1nVw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/client-sts@3.387.0': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 @@ -3980,11 +8690,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sts@3.600.0: - resolution: {integrity: sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sts@3.600.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -4028,17 +8735,14 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sts@3.609.0: - resolution: {integrity: sha512-A0B3sDKFoFlGo8RYRjDBWHXpbgirer2bZBkCIzhSPHc1vOFHt/m2NcUoE2xnBKXJFrptL1xDkvo1P+XYp/BfcQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sts@3.609.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -4048,28 +8752,28 @@ packages: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.4 - '@smithy/core': 2.2.4 - '@smithy/fetch-http-handler': 3.2.0 + '@smithy/config-resolver': 3.0.5 + '@smithy/core': 2.4.0 + '@smithy/fetch-http-handler': 3.2.4 '@smithy/hash-node': 3.0.3 '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.3 - '@smithy/middleware-endpoint': 3.0.4 - '@smithy/middleware-retry': 3.0.7 + '@smithy/middleware-content-length': 3.0.5 + '@smithy/middleware-endpoint': 3.1.0 + '@smithy/middleware-retry': 3.0.15 '@smithy/middleware-serde': 3.0.3 '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.3 - '@smithy/node-http-handler': 3.1.1 - '@smithy/protocol-http': 4.0.3 - '@smithy/smithy-client': 3.1.5 + '@smithy/node-config-provider': 3.1.4 + '@smithy/node-http-handler': 3.1.4 + '@smithy/protocol-http': 4.1.0 + '@smithy/smithy-client': 3.2.0 '@smithy/types': 3.3.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.7 - '@smithy/util-defaults-mode-node': 3.0.7 - '@smithy/util-endpoints': 2.0.4 + '@smithy/util-defaults-mode-browser': 3.0.15 + '@smithy/util-defaults-mode-node': 3.0.15 + '@smithy/util-endpoints': 2.0.5 '@smithy/util-middleware': 3.0.3 '@smithy/util-retry': 3.0.3 '@smithy/util-utf8': 3.0.0 @@ -4077,15 +8781,13 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sts@3.620.1: - resolution: {integrity: sha512-d+ECGFDg0IsDdmfKU2O0VeMYKZcmbfBaA9HkZnZ39wu1BlXGI73xJe8cfmzbobvu+Ly+bAfHdLCpgIY+pD4D7g==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sts@3.620.1': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.620.1) '@aws-sdk/core': 3.620.1 - '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -4124,15 +8826,13 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sts@3.637.0: - resolution: {integrity: sha512-xUi7x4qDubtA8QREtlblPuAcn91GS/09YVEY/RwU7xCY0aqGuFwgszAANlha4OUIqva8oVj2WO4gJuG+iaSnhw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sts@3.637.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.637.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0)(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -4171,15 +8871,13 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sts@3.645.0: - resolution: {integrity: sha512-6azXYtvtnAsPf2ShN9vKynIYVcJOpo6IoVmoMAVgNaBJyllP+s/RORzranYZzckqfmrudSxtct4rVapjLWuAMg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sts@3.645.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -4218,9 +8916,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/core@3.598.0: - resolution: {integrity: sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==} - engines: {node: '>=16.0.0'} + '@aws-sdk/core@3.598.0': dependencies: '@smithy/core': 2.4.0 '@smithy/protocol-http': 4.1.0 @@ -4229,11 +8925,8 @@ packages: '@smithy/types': 3.3.0 fast-xml-parser: 4.2.5 tslib: 2.6.3 - dev: false - /@aws-sdk/core@3.609.0: - resolution: {integrity: sha512-ptqw+DTxLr01+pKjDUuo53SEDzI+7nFM3WfQaEo0yhDg8vWw8PER4sWj1Ysx67ksctnZesPUjqxd5SHbtdBxiA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/core@3.609.0': dependencies: '@smithy/core': 2.4.0 '@smithy/protocol-http': 4.1.0 @@ -4243,9 +8936,7 @@ packages: fast-xml-parser: 4.2.5 tslib: 2.6.3 - /@aws-sdk/core@3.620.1: - resolution: {integrity: sha512-6Ejce93dDlDnovl6oYtxj3I/SJMOQoFdmmtM4+4W/cgMWH+l00T5aszVxDLjjPfu3Ryt7dNhrXaYeK2Ue1ZBmg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/core@3.620.1': dependencies: '@smithy/core': 2.4.0 '@smithy/node-config-provider': 3.1.4 @@ -4257,9 +8948,7 @@ packages: fast-xml-parser: 4.2.5 tslib: 2.6.3 - /@aws-sdk/core@3.635.0: - resolution: {integrity: sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/core@3.635.0': dependencies: '@smithy/core': 2.4.0 '@smithy/node-config-provider': 3.1.4 @@ -4272,9 +8961,7 @@ packages: fast-xml-parser: 4.4.1 tslib: 2.6.3 - /@aws-sdk/credential-provider-cognito-identity@3.609.0: - resolution: {integrity: sha512-BqrpAXRr64dQ/uZsRB2wViGKTkVRlfp8Q+Zd7Bc8Ikk+YXjPtl+IyWXKtdKQ3LBO255KwAcPmra5oFC+2R1GOQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-cognito-identity@3.609.0': dependencies: '@aws-sdk/client-cognito-identity': 3.609.0 '@aws-sdk/types': 3.609.0 @@ -4283,49 +8970,36 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/credential-provider-env@3.387.0: - resolution: {integrity: sha512-PVqNk7XPIYe5CMYNvELkcALtkl/pIM8/uPtqEtTg+mgnZBeL4fAmgXZiZMahQo1DxP5t/JaK384f6JG+A0qDjA==} - engines: {node: '>=14.0.0'} + '@aws-sdk/credential-provider-env@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/property-provider': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-env@3.598.0: - resolution: {integrity: sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-env@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-env@3.609.0: - resolution: {integrity: sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-env@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-env@3.620.1: - resolution: {integrity: sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-env@3.620.1': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-http@3.598.0: - resolution: {integrity: sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-http@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/fetch-http-handler': 3.2.4 @@ -4336,11 +9010,8 @@ packages: '@smithy/types': 3.3.0 '@smithy/util-stream': 3.1.3 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-http@3.609.0: - resolution: {integrity: sha512-GQQfB9Mk4XUZwaPsk4V3w8MqleS6ApkZKVQn3vTLAKa8Y7B2Imcpe5zWbKYjDd8MPpMWjHcBGFTVlDRFP4zwSQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-http@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/fetch-http-handler': 3.2.4 @@ -4352,9 +9023,7 @@ packages: '@smithy/util-stream': 3.1.3 tslib: 2.6.3 - /@aws-sdk/credential-provider-http@3.620.0: - resolution: {integrity: sha512-BI2BdrSKDmB/2ouB/NJR0PT0x/+5fmoF6XOE78hFBb4F5w/yynGgcJY936dF+oREfpME6ehjB2b0okGg78Scpw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-http@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/fetch-http-handler': 3.2.4 @@ -4366,9 +9035,7 @@ packages: '@smithy/util-stream': 3.1.3 tslib: 2.6.3 - /@aws-sdk/credential-provider-http@3.635.0: - resolution: {integrity: sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-http@3.635.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/fetch-http-handler': 3.2.4 @@ -4380,9 +9047,7 @@ packages: '@smithy/util-stream': 3.1.3 tslib: 2.6.3 - /@aws-sdk/credential-provider-ini@3.387.0: - resolution: {integrity: sha512-DS2Jg5E4Hd9fhJqTVNBG3SEwLwcyguPDcXSVCDz5pEHlYFM1U4x9b7aAbutzZujTH99MZ6Gua8kAotB/qjEjtw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/credential-provider-ini@3.387.0': dependencies: '@aws-sdk/credential-provider-env': 3.387.0 '@aws-sdk/credential-provider-process': 3.387.0 @@ -4396,13 +9061,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0): - resolution: {integrity: sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.598.0 + '@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)': dependencies: '@aws-sdk/client-sts': 3.600.0 '@aws-sdk/credential-provider-env': 3.598.0 @@ -4419,41 +9079,14 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - - /@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-hwaBfXuBTv6/eAdEsDfGcteYUW6Km7lvvubbxEdxIuJNF3vswR7RMGIXaEC37hhPkTTgd3H0TONammhwZIfkog==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 - dependencies: - '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/credential-provider-env': 3.609.0 - '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - /@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-hwaBfXuBTv6/eAdEsDfGcteYUW6Km7lvvubbxEdxIuJNF3vswR7RMGIXaEC37hhPkTTgd3H0TONammhwZIfkog==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 + '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/credential-provider-env': 3.609.0 '@aws-sdk/credential-provider-http': 3.609.0 '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 @@ -4464,20 +9097,15 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - /@aws-sdk/credential-provider-ini@3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-m9jwigMPRlRRhoPxCQZMOwQUd6imEJbksF6tSMYNae76DIvrCi4z2Jhp6RJ9Mij8cnewUZCAmvu2FlK9+n9M7A==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.620.1 + '@aws-sdk/credential-provider-ini@3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1)': dependencies: - '@aws-sdk/client-sts': 3.645.0 + '@aws-sdk/client-sts': 3.620.1 '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.620.0 '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-sso': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)) + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.620.1) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 '@smithy/property-provider': 3.1.3 @@ -4488,17 +9116,13 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-ini@3.637.0(@aws-sdk/client-sso-oidc@3.637.0)(@aws-sdk/client-sts@3.637.0): - resolution: {integrity: sha512-h+PFCWfZ0Q3Dx84SppET/TFpcQHmxFW8/oV9ArEvMilw4EBN+IlxgbL0CnHwjHW64szcmrM0mbebjEfHf4FXmw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.637.0 + '@aws-sdk/credential-provider-ini@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/client-sts': 3.637.0 '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.635.0 '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0) + '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 @@ -4510,39 +9134,13 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-ini@3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-LlZW0qwUwNlTaAIDCNpLbPsyXvS42pRIwF92fgtCQedmdnpN3XRUC6hcwSYI7Xru3GGKp3RnceOvsdOaRJORsw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.645.0 - dependencies: - '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/credential-provider-env': 3.620.1 - '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0) - '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - - /@aws-sdk/credential-provider-ini@3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-LlZW0qwUwNlTaAIDCNpLbPsyXvS42pRIwF92fgtCQedmdnpN3XRUC6hcwSYI7Xru3GGKp3RnceOvsdOaRJORsw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.645.0 + '@aws-sdk/credential-provider-ini@3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0)': dependencies: '@aws-sdk/client-sts': 3.645.0 '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.635.0 '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 @@ -4554,9 +9152,7 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-node@3.387.0: - resolution: {integrity: sha512-NviQ0EqigPWwX4avKheRzE2R4YPzO6qzdyxKZUookr+uTWYroQ4ePZbHK1/BD8LlqKKBlttX/d3ENXjynU4clA==} - engines: {node: '>=14.0.0'} + '@aws-sdk/credential-provider-node@3.387.0': dependencies: '@aws-sdk/credential-provider-env': 3.387.0 '@aws-sdk/credential-provider-ini': 3.387.0 @@ -4571,11 +9167,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0): - resolution: {integrity: sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)': dependencies: '@aws-sdk/credential-provider-env': 3.598.0 '@aws-sdk/credential-provider-http': 3.598.0 @@ -4593,38 +9186,14 @@ packages: - '@aws-sdk/client-sso-oidc' - '@aws-sdk/client-sts' - aws-crt - dev: false - - /@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-4J8/JRuqfxJDGD9jTHVCBxCvYt7/Vgj2Stlhj930mrjFPO/yRw8ilAAZxBWe0JHPX3QwepCmh4ErZe53F5ysxQ==} - engines: {node: '>=16.0.0'} - dependencies: - '@aws-sdk/credential-provider-env': 3.609.0 - '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - '@aws-sdk/client-sts' - - aws-crt - /@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-4J8/JRuqfxJDGD9jTHVCBxCvYt7/Vgj2Stlhj930mrjFPO/yRw8ilAAZxBWe0JHPX3QwepCmh4ErZe53F5ysxQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/credential-provider-env': 3.609.0 '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 @@ -4636,18 +9205,15 @@ packages: - '@aws-sdk/client-sso-oidc' - '@aws-sdk/client-sts' - aws-crt - dev: false - /@aws-sdk/credential-provider-node@3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-KaprIJW2azM+oTIHi7S1ayJ3oQqoFwpMBWFpZM1nvSzaPucrZIUmX2m4uVrMM4LfXsfUsgMkrme2rBI1fGAjCg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1)': dependencies: '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.620.0 - '@aws-sdk/credential-provider-ini': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-ini': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1) '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-sso': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)) + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.620.1) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 '@smithy/property-provider': 3.1.3 @@ -4659,15 +9225,13 @@ packages: - '@aws-sdk/client-sts' - aws-crt - /@aws-sdk/credential-provider-node@3.637.0(@aws-sdk/client-sso-oidc@3.637.0)(@aws-sdk/client-sts@3.637.0): - resolution: {integrity: sha512-yoEhoxJJfs7sPVQ6Is939BDQJZpZCoUgKr/ySse4YKOZ24t4VqgHA6+wV7rYh+7IW24Rd91UTvEzSuHYTlxlNA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-ini': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0)(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-ini': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0) + '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 @@ -4680,36 +9244,13 @@ packages: - '@aws-sdk/client-sts' - aws-crt - /@aws-sdk/credential-provider-node@3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-eGFFuNvLeXjCJf5OCIuSEflxUowmK+bCS+lK4M8ofsYOEGAivdx7C0UPxNjHpvM8wKd8vpMl5phTeS9BWX5jMQ==} - engines: {node: '>=16.0.0'} - dependencies: - '@aws-sdk/credential-provider-env': 3.620.1 - '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-ini': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0) - '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - '@aws-sdk/client-sts' - - aws-crt - - /@aws-sdk/credential-provider-node@3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-eGFFuNvLeXjCJf5OCIuSEflxUowmK+bCS+lK4M8ofsYOEGAivdx7C0UPxNjHpvM8wKd8vpMl5phTeS9BWX5jMQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0)': dependencies: '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-ini': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-ini': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0) '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 @@ -4722,31 +9263,23 @@ packages: - '@aws-sdk/client-sts' - aws-crt - /@aws-sdk/credential-provider-process@3.387.0: - resolution: {integrity: sha512-tQScLHmDlqkQN+mqw4s3cxepEUeHYDhFl5eH+J8puvPqWjXMYpCEdY79SAtWs6SZd4CWiZ0VLeYU6xQBZengbQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/credential-provider-process@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/property-provider': 2.2.0 '@smithy/shared-ini-file-loader': 2.4.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-process@3.598.0: - resolution: {integrity: sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-process@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-process@3.609.0: - resolution: {integrity: sha512-Ux35nGOSJKZWUIM3Ny0ROZ8cqPRUEkh+tR3X2o9ydEbFiLq3eMMyEnHJqx4EeUjLRchidlm4CCid9GxMe5/gdw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-process@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 @@ -4754,9 +9287,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-process@3.620.1: - resolution: {integrity: sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-process@3.620.1': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 @@ -4764,9 +9295,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-sso@3.387.0: - resolution: {integrity: sha512-sQgrEyTSrwLe8zgjP9VEUDz3dtGXSCc4k00bCwODbzdOWCA1nz9oF2tFmgjFsb1Q80pae01Pe50Esix5z2eHsQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/credential-provider-sso@3.387.0': dependencies: '@aws-sdk/client-sso': 3.387.0 '@aws-sdk/token-providers': 3.387.0 @@ -4777,11 +9306,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/credential-provider-sso@3.598.0(@aws-sdk/client-sso-oidc@3.600.0): - resolution: {integrity: sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)': dependencies: '@aws-sdk/client-sso': 3.598.0 '@aws-sdk/token-providers': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) @@ -4793,29 +9319,11 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - - /@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.609.0): - resolution: {integrity: sha512-oQPGDKMMIxjvTcm86g07RPYeC7mCNk+29dPpY15ZAPRpAF7F0tircsC3wT9fHzNaKShEyK5LuI5Kg/uxsdy+Iw==} - engines: {node: '>=16.0.0'} - dependencies: - '@aws-sdk/client-sso': 3.609.0 - '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - /@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.645.0): - resolution: {integrity: sha512-oQPGDKMMIxjvTcm86g07RPYeC7mCNk+29dPpY15ZAPRpAF7F0tircsC3wT9fHzNaKShEyK5LuI5Kg/uxsdy+Iw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: '@aws-sdk/client-sso': 3.609.0 - '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 @@ -4824,14 +9332,11 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - /@aws-sdk/credential-provider-sso@3.620.1(@aws-sdk/client-sso-oidc@3.620.1): - resolution: {integrity: sha512-cFU8e6ctdkWR8BRCnHFzs37N+ilbHf1OT2EeMjt1ZDE9FgTD5L5BTgVWDxnPmyQnEoBs1p4PyNPHkpHY5EmswQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))': dependencies: '@aws-sdk/client-sso': 3.620.1 - '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.620.1) + '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 @@ -4841,12 +9346,10 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-sso@3.637.0(@aws-sdk/client-sso-oidc@3.637.0): - resolution: {integrity: sha512-Mvz+h+e62/tl+dVikLafhv+qkZJ9RUb8l2YN/LeKMWkxQylPT83CPk9aimVhCV89zth1zpREArl97+3xsfgQvA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))': dependencies: '@aws-sdk/client-sso': 3.637.0 - '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.637.0) + '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 @@ -4856,12 +9359,10 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-sso@3.645.0(@aws-sdk/client-sso-oidc@3.645.0): - resolution: {integrity: sha512-d6XuChAl5NCsCrUexc6AFb4efPmb9+66iwPylKG+iMTMYgO1ackfy1Q2/f35jdn0jolkPkzKsVyfzsEVoID6ew==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))': dependencies: '@aws-sdk/client-sso': 3.645.0 - '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 @@ -4871,34 +9372,22 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-web-identity@3.387.0: - resolution: {integrity: sha512-6ueMPl+J3KWv6ZaAWF4Z138QCuBVFZRVAgwbtP3BNqWrrs4Q6TPksOQJ79lRDMpv0EUoyVl04B6lldNlhN8RdA==} - engines: {node: '>=14.0.0'} + '@aws-sdk/credential-provider-web-identity@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/property-provider': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-web-identity@3.598.0(@aws-sdk/client-sts@3.600.0): - resolution: {integrity: sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.598.0 + '@aws-sdk/credential-provider-web-identity@3.598.0(@aws-sdk/client-sts@3.600.0)': dependencies: '@aws-sdk/client-sts': 3.600.0 '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 + '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/types': 3.609.0 @@ -4906,35 +9395,15 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 - dependencies: - '@aws-sdk/client-sts': 3.645.0 - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - - /@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.621.0 + '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.620.1)': dependencies: - '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/client-sts': 3.620.1 '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.637.0): - resolution: {integrity: sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.621.0 + '@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/client-sts': 3.637.0 '@aws-sdk/types': 3.609.0 @@ -4942,11 +9411,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.621.0 + '@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.645.0)': dependencies: '@aws-sdk/client-sts': 3.645.0 '@aws-sdk/types': 3.609.0 @@ -4954,9 +9419,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-providers@3.609.0(@aws-sdk/client-sso-oidc@3.645.0): - resolution: {integrity: sha512-bJKMY4QwRVderh8R2s9kukoZhuNZew/xzwPa9DRRFVOIsznsS0faAdmAAFrKb8e06YyQq6DiZP0BfFyVHAXE2A==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: '@aws-sdk/client-cognito-identity': 3.609.0 '@aws-sdk/client-sso': 3.609.0 @@ -4964,10 +9427,10 @@ packages: '@aws-sdk/credential-provider-cognito-identity': 3.609.0 '@aws-sdk/credential-provider-env': 3.609.0 '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.1.3 @@ -4977,18 +9440,13 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - /@aws-sdk/endpoint-cache@3.572.0: - resolution: {integrity: sha512-CzuRWMj/xtN9p9eP915nlPmlyniTzke732Ow/M60++gGgB3W+RtZyFftw3TEx+NzNhd1tH54dEcGiWdiNaBz3Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/endpoint-cache@3.572.0': dependencies: mnemonist: 0.38.3 tslib: 2.6.3 - /@aws-sdk/middleware-bucket-endpoint@3.387.0: - resolution: {integrity: sha512-o7Dsq0YTUHFcKXD6+30/fXv/Wzdxqz9WonhCu3ZFPwTDLZgOM4QDDKW8EcC1SplKP1IUyaEli8Affodag9T1cQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-bucket-endpoint@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@aws-sdk/util-arn-parser': 3.310.0 @@ -4996,11 +9454,8 @@ packages: '@smithy/types': 2.12.0 '@smithy/util-config-provider': 2.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-bucket-endpoint@3.598.0: - resolution: {integrity: sha512-PM7BcFfGUSkmkT6+LU9TyJiB4S8yI7dfuKQDwK5ZR3P7MKaK4Uj4yyDiv0oe5xvkF6+O2+rShj+eh8YuWkOZ/Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-bucket-endpoint@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@aws-sdk/util-arn-parser': 3.568.0 @@ -5009,11 +9464,8 @@ packages: '@smithy/types': 3.3.0 '@smithy/util-config-provider': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-endpoint-discovery@3.598.0: - resolution: {integrity: sha512-TaFo3rfapVP0FiddH2zDyA5R5XNk2M+zMeUZaBRveYamSQ11F+fMGcedBgbOsv7yNESvaZvjlcw2K+cx3jOchA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-endpoint-discovery@3.598.0': dependencies: '@aws-sdk/endpoint-cache': 3.572.0 '@aws-sdk/types': 3.598.0 @@ -5021,11 +9473,8 @@ packages: '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-endpoint-discovery@3.620.0: - resolution: {integrity: sha512-T6kuydHBF4BPP5CVH53Fze7c2b9rqxWP88XrGtmNMXXdY4sXur1v/itGdS2l3gqRjxKo0LsmjmuQm9zL4vGneQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-endpoint-discovery@3.620.0': dependencies: '@aws-sdk/endpoint-cache': 3.572.0 '@aws-sdk/types': 3.609.0 @@ -5034,29 +9483,21 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-expect-continue@3.387.0: - resolution: {integrity: sha512-w415a4tjQc6a7isq0AEDWFBC0HWUCHXEDjDl94UACxfMmS9bVabuf04t9CQ+nBBVs6HdiNdfdc/pBR2pRwx2Yg==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-expect-continue@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/protocol-http': 2.0.5 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-expect-continue@3.598.0: - resolution: {integrity: sha512-ZuHW18kaeHR8TQyhEOYMr8VwiIh0bMvF7J1OTqXHxDteQIavJWA3CbfZ9sgS4XGtrBZDyHJhjZKeCfLhN2rq3w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-expect-continue@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/protocol-http': 4.0.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-flexible-checksums@3.387.0: - resolution: {integrity: sha512-QlH97rrKlcMyLG+2ps7+DtBHfPyRIpi7sD3y0iko2u3PGXk+PoLPK8wWyGql9sFopOYTl6/Jh2Rb1b6z6NbjEA==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-flexible-checksums@3.387.0': dependencies: '@aws-crypto/crc32': 3.0.0 '@aws-crypto/crc32c': 3.0.0 @@ -5066,11 +9507,8 @@ packages: '@smithy/types': 2.12.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-flexible-checksums@3.598.0: - resolution: {integrity: sha512-xukAzds0GQXvMEY9G6qt+CzwVzTx8NyKKh04O2Q+nOch6QQ8Rs+2kTRy3Z4wQmXq2pK9hlOWb5nXA7HWpmz6Ng==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-flexible-checksums@3.598.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 @@ -5080,142 +9518,102 @@ packages: '@smithy/types': 3.3.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-host-header@3.387.0: - resolution: {integrity: sha512-EWm9PXSr8dSp7hnRth1U7OfelXQp9dLf1yS1kUL+UhppYDJpjhdP7ql3NI4xJKw8e76sP2FuJYEuzWnJHuWoyQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-host-header@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/protocol-http': 2.0.5 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-host-header@3.598.0: - resolution: {integrity: sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-host-header@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-host-header@3.609.0: - resolution: {integrity: sha512-iTKfo158lc4jLDfYeZmYMIBHsn8m6zX+XB6birCSNZ/rrlzAkPbGE43CNdKfvjyWdqgLMRXF+B+OcZRvqhMXPQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-host-header@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-host-header@3.620.0: - resolution: {integrity: sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-host-header@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-location-constraint@3.387.0: - resolution: {integrity: sha512-Ipdry2V58CpDcWD0ZTz6yFtpTASEBxbuWdqUUYW7pOkZ/5GPGH8NhBky7M38iGqAO6FNysvWEVCUpIqNGkI1lw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-location-constraint@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-location-constraint@3.598.0: - resolution: {integrity: sha512-8oybQxN3F1ISOMULk7JKJz5DuAm5hCUcxMW9noWShbxTJuStNvuHf/WLUzXrf8oSITyYzIHPtf8VPlKR7I3orQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-location-constraint@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-logger@3.387.0: - resolution: {integrity: sha512-FjAvJr1XyaInT81RxUwgifnbXoFJrRBFc64XeFJgFanGIQCWLYxRrK2HV9eBpao/AycbmuoHgLd/f0sa4hZFoQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-logger@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-logger@3.598.0: - resolution: {integrity: sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-logger@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-logger@3.609.0: - resolution: {integrity: sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-logger@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-recursion-detection@3.387.0: - resolution: {integrity: sha512-ZF45T785ru8OwvYZw6awD9Z76OwSMM1eZzj2eY+FDz1cHfkpLjxEiti2iIH1FxbyK7n9ZqDUx29lVlCv238YyQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-recursion-detection@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/protocol-http': 2.0.5 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-recursion-detection@3.598.0: - resolution: {integrity: sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-recursion-detection@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-recursion-detection@3.609.0: - resolution: {integrity: sha512-6sewsYB7/o/nbUfA99Aa/LokM+a/u4Wpm/X2o0RxOsDtSB795ObebLJe2BxY5UssbGaWkn7LswyfvrdZNXNj1w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-recursion-detection@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-recursion-detection@3.620.0: - resolution: {integrity: sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-recursion-detection@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-sdk-s3@3.387.0: - resolution: {integrity: sha512-OIUBDzGhglI6KjXVwPLh7hRbrfCpSTwWRkbXbLrPgZZuzWMoJJ3q59RVkpLnAV9Mdkg6+YA6JTw4k4hcmJblVw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-sdk-s3@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@aws-sdk/util-arn-parser': 3.310.0 '@smithy/protocol-http': 2.0.5 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-sdk-s3@3.598.0: - resolution: {integrity: sha512-5AGtLAh9wyK6ANPYfaKTqJY1IFJyePIxsEbxa7zS6REheAqyVmgJFaGu3oQ5XlxfGr5Uq59tFTRkyx26G1HkHA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-sdk-s3@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@aws-sdk/util-arn-parser': 3.568.0 @@ -5226,11 +9624,8 @@ packages: '@smithy/types': 3.3.0 '@smithy/util-config-provider': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-sdk-s3@3.622.0: - resolution: {integrity: sha512-tX9wZ2ALx5Ez4bkY+SvSj6DpNZ6TmY4zlsVsdgV95LZFLjNwqnZkKkS+uKnsIyLBiBp6g92JVQwnUEIp7ov2Zw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-sdk-s3@3.622.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-arn-parser': 3.568.0 @@ -5243,11 +9638,8 @@ packages: '@smithy/util-stream': 3.1.3 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-sdk-sqs@3.598.0: - resolution: {integrity: sha512-BVHR5cKwxXTovezHHPzP7iSNZQdMp+Pn9l2zVfFzryE2Enahkg5oxgUcLD2jeFx14QxS1k7czIEIvKh991CWsg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-sdk-sqs@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/smithy-client': 3.1.5 @@ -5255,21 +9647,15 @@ packages: '@smithy/util-hex-encoding': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-sdk-sts@3.387.0: - resolution: {integrity: sha512-7ZzRKOJ4V/JDQmKz9z+FjZqw59mrMATEMLR6ff0H0JHMX0Uk5IX8TQB058ss+ar14qeJ4UcteYzCqHNI0O1BHw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-sdk-sts@3.387.0': dependencies: '@aws-sdk/middleware-signing': 3.387.0 '@aws-sdk/types': 3.387.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-signing@3.387.0: - resolution: {integrity: sha512-oJXlE0MES8gxNLo137PPNNiOICQGOaETTvq3kBSJgb/gtEAxQajMIlaNT7s1wsjOAruFHt4975nCXuY4lpx7GQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-signing@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/property-provider': 2.2.0 @@ -5278,11 +9664,8 @@ packages: '@smithy/types': 2.12.0 '@smithy/util-middleware': 2.2.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-signing@3.598.0: - resolution: {integrity: sha512-XKb05DYx/aBPqz6iCapsCbIl8aD8EihTuPCs51p75QsVfbQoVr4TlFfIl5AooMSITzojdAQqxt021YtvxjtxIQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-signing@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.3 @@ -5291,51 +9674,36 @@ packages: '@smithy/types': 3.3.0 '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-ssec@3.387.0: - resolution: {integrity: sha512-Jtie1gqqcs7ZuYDlz/kuI3CKCXoCL5Ov/Gj5X8/XmwrQJEpuB6z0KY5H1qY0xo+jtAhC8nDPv0GnuLoOfn85hw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-ssec@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-ssec@3.598.0: - resolution: {integrity: sha512-f0p2xP8IC1uJ5e/tND1l81QxRtRFywEdnbtKCE0H6RSn4UIt2W3Dohe1qQDbnh27okF0PkNW6BJGdSAz3p7qbA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-ssec@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-user-agent@3.387.0: - resolution: {integrity: sha512-hTfFTwDtp86xS98BKa+RFuLfcvGftxwzrbZeisZV8hdb4ZhvNXjSxnvM3vetW0GUEnY9xHPSGyp2ERRTinPKFQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-user-agent@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@aws-sdk/util-endpoints': 3.387.0 '@smithy/protocol-http': 2.0.5 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-user-agent@3.598.0: - resolution: {integrity: sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@aws-sdk/util-endpoints': 3.598.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-user-agent@3.609.0: - resolution: {integrity: sha512-nbq7MXRmeXm4IDqh+sJRAxGPAq0OfGmGIwKvJcw66hLoG8CmhhVMZmIAEBDFr57S+YajGwnLLRt+eMI05MMeVA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.609.0 @@ -5343,9 +9711,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-user-agent@3.620.0: - resolution: {integrity: sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.614.0 @@ -5353,9 +9719,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-user-agent@3.637.0: - resolution: {integrity: sha512-EYo0NE9/da/OY8STDsK2LvM4kNa79DBsf4YVtaG4P5pZ615IeFsD8xOHZeuJmUrSMlVQ8ywPRX7WMucUybsKug==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.637.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.637.0 @@ -5363,9 +9727,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-user-agent@3.645.0: - resolution: {integrity: sha512-NpTAtqWK+49lRuxfz7st9for80r4NriCMK0RfdJSoPFVntjsSQiQ7+2nW2XL05uVY633e9DvCAw8YatX3zd1mw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.645.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.645.0 @@ -5373,9 +9735,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/region-config-resolver@3.598.0: - resolution: {integrity: sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/region-config-resolver@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/node-config-provider': 3.1.4 @@ -5383,11 +9743,8 @@ packages: '@smithy/util-config-provider': 3.0.0 '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - dev: false - /@aws-sdk/region-config-resolver@3.609.0: - resolution: {integrity: sha512-lMHBG8zg9GWYBc9/XVPKyuAUd7iKqfPP7z04zGta2kGNOKbUTeqmAdc1gJGku75p4kglIPlGBorOxti8DhRmKw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/region-config-resolver@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/node-config-provider': 3.1.4 @@ -5396,9 +9753,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@aws-sdk/region-config-resolver@3.614.0: - resolution: {integrity: sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==} - engines: {node: '>=16.0.0'} + '@aws-sdk/region-config-resolver@3.614.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/node-config-provider': 3.1.4 @@ -5407,9 +9762,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@aws-sdk/s3-request-presigner@3.623.0: - resolution: {integrity: sha512-xdY7x4GQ3jVhkge0I8P2V/18p2unP3AD0m1zvacgFmxZ8tptjVpEg2fwR39gKv3pfri0DdfiPDrVONsPC2KlLw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/s3-request-presigner@3.623.0': dependencies: '@aws-sdk/signature-v4-multi-region': 3.622.0 '@aws-sdk/types': 3.609.0 @@ -5419,27 +9772,16 @@ packages: '@smithy/smithy-client': 3.2.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/signature-v4-multi-region@3.387.0: - resolution: {integrity: sha512-SGuUbEFi8BXYVv4M7Hc0488I7uZbTVBW19j/B7bnyfbKFrndBXM366s/mChx4iELtESQ61AAstyafx5nGj5tIg==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@aws-sdk/signature-v4-crt': ^3.118.0 - peerDependenciesMeta: - '@aws-sdk/signature-v4-crt': - optional: true + '@aws-sdk/signature-v4-multi-region@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/protocol-http': 2.0.5 '@smithy/signature-v4': 2.3.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/signature-v4-multi-region@3.598.0: - resolution: {integrity: sha512-1r/EyTrO1gSa1FirnR8V7mabr7gk+l+HkyTI0fcTSr8ucB7gmYyW6WjkY8JCz13VYHFK62usCEDS7yoJoJOzTA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/signature-v4-multi-region@3.598.0': dependencies: '@aws-sdk/middleware-sdk-s3': 3.598.0 '@aws-sdk/types': 3.598.0 @@ -5447,11 +9789,8 @@ packages: '@smithy/signature-v4': 3.1.2 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/signature-v4-multi-region@3.622.0: - resolution: {integrity: sha512-K7ddofVNzwTFRjmLZLfs/v+hiE9m5LguajHk8WULxXQgkcDI3nPgOfmMMGuslYohaQhRwW+ic+dzYlateLUudQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/signature-v4-multi-region@3.622.0': dependencies: '@aws-sdk/middleware-sdk-s3': 3.622.0 '@aws-sdk/types': 3.609.0 @@ -5459,24 +9798,16 @@ packages: '@smithy/signature-v4': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/token-providers@3.387.0: - resolution: {integrity: sha512-W9lPW6zR8yrfvDDLJnKCvHs2KwmydSo+1bG5i6WzFnY3aeOgPBJO2eDIJajZG8Q/L++ZwDaNDLL+ROnIMcg6GA==} - engines: {node: '>=14.0.0'} + '@aws-sdk/token-providers@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/property-provider': 2.2.0 '@smithy/shared-ini-file-loader': 2.4.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/token-providers@3.598.0(@aws-sdk/client-sso-oidc@3.600.0): - resolution: {integrity: sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.598.0 + '@aws-sdk/token-providers@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)': dependencies: '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) '@aws-sdk/types': 3.598.0 @@ -5484,13 +9815,8 @@ packages: '@smithy/shared-ini-file-loader': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0): - resolution: {integrity: sha512-WvhW/7XSf+H7YmtiIigQxfDVZVZI7mbKikQ09YpzN7FeN3TmYib1+0tB+EE9TbICkwssjiFc71FEBEh4K9grKQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.609.0 + '@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 @@ -5499,38 +9825,16 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.645.0): - resolution: {integrity: sha512-WvhW/7XSf+H7YmtiIigQxfDVZVZI7mbKikQ09YpzN7FeN3TmYib1+0tB+EE9TbICkwssjiFc71FEBEh4K9grKQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.609.0 - dependencies: - '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - dev: false - - /@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.620.1): - resolution: {integrity: sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.614.0 + '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))': dependencies: - '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.620.1) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.637.0): - resolution: {integrity: sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.614.0 + '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.637.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 @@ -5539,285 +9843,179 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.645.0): - resolution: {integrity: sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.614.0 + '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))': dependencies: - '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/types@3.387.0: - resolution: {integrity: sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==} - engines: {node: '>=14.0.0'} + '@aws-sdk/types@3.387.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/types@3.598.0: - resolution: {integrity: sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/types@3.598.0': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/types@3.609.0: - resolution: {integrity: sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/types@3.609.0': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/util-arn-parser@3.310.0: - resolution: {integrity: sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==} - engines: {node: '>=14.0.0'} + '@aws-sdk/util-arn-parser@3.310.0': dependencies: tslib: 2.6.3 - dev: false - /@aws-sdk/util-arn-parser@3.568.0: - resolution: {integrity: sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-arn-parser@3.568.0': dependencies: tslib: 2.6.3 - dev: false - /@aws-sdk/util-dynamodb@3.602.0(@aws-sdk/client-dynamodb@3.602.0): - resolution: {integrity: sha512-0QsRLE4cK0h9jseCbaBZpcLzBcOgPMdsEy4wIYvcXivHIdgt/JQxs329NF7EfWQU8h3PU5hRy3tiTBLuB4TmgQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.602.0 + '@aws-sdk/util-dynamodb@3.602.0(@aws-sdk/client-dynamodb@3.602.0)': dependencies: '@aws-sdk/client-dynamodb': 3.602.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-dynamodb@3.637.0(@aws-sdk/client-dynamodb@3.637.0): - resolution: {integrity: sha512-C2q8HcGRiahtf46Mhaqydh1gofeksj7m74PJXHYKW+pKBMLPlpou1+w2o5QSpVEp0dSBtKw30eRVQzxhqg/ACA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.637.0 + '@aws-sdk/util-dynamodb@3.637.0(@aws-sdk/client-dynamodb@3.637.0)': dependencies: '@aws-sdk/client-dynamodb': 3.637.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-dynamodb@3.648.0(@aws-sdk/client-dynamodb@3.648.0): - resolution: {integrity: sha512-w8cF5Ap8AL6VvA8bIbDNnrfpVvN3klsZRQ/QLVAhW1k3R3t9L+eKzoS3bBTVeyBlIh/eyXnSkQ8eduehS82FMw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.648.0 + '@aws-sdk/util-dynamodb@3.648.0(@aws-sdk/client-dynamodb@3.648.0)': dependencies: '@aws-sdk/client-dynamodb': 3.648.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-dynamodb@3.658.1(@aws-sdk/client-dynamodb@3.637.0): - resolution: {integrity: sha512-lzlnis+35a2OhGZlVJvM3/30iIVoP2cIv5Bkw1F2nkM6Pr+1NOd3XvYhCY1Ud5zWtV6HUSptzessvUPqJTMfjQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.658.1 + '@aws-sdk/util-dynamodb@3.658.1(@aws-sdk/client-dynamodb@3.637.0)': dependencies: '@aws-sdk/client-dynamodb': 3.637.0 tslib: 2.6.3 - dev: true - /@aws-sdk/util-endpoints@3.387.0: - resolution: {integrity: sha512-g7kvuCXehGXHHBw9PkSQdwVyDFmNUZLmfrRmqMyrMDG9QLQrxr4pyWcSaYgTE16yUzhQQOR+QSey+BL6W9/N6g==} - engines: {node: '>=14.0.0'} + '@aws-sdk/util-endpoints@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-endpoints@3.598.0: - resolution: {integrity: sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/types': 3.3.0 '@smithy/util-endpoints': 2.0.5 tslib: 2.6.3 - dev: false - /@aws-sdk/util-endpoints@3.609.0: - resolution: {integrity: sha512-Rh+3V8dOvEeE1aQmUy904DYWtLUEJ7Vf5XBPlQ6At3pBhp+zpXbsnpZzVL33c8lW1xfj6YPwtO6gOeEsl1juCQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.3.0 '@smithy/util-endpoints': 2.0.5 tslib: 2.6.3 - /@aws-sdk/util-endpoints@3.614.0: - resolution: {integrity: sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.614.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.3.0 '@smithy/util-endpoints': 2.0.5 tslib: 2.6.3 - /@aws-sdk/util-endpoints@3.637.0: - resolution: {integrity: sha512-pAqOKUHeVWHEXXDIp/qoMk/6jyxIb6GGjnK1/f8dKHtKIEs4tKsnnL563gceEvdad53OPXIt86uoevCcCzmBnw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.637.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.3.0 '@smithy/util-endpoints': 2.0.5 tslib: 2.6.3 - /@aws-sdk/util-endpoints@3.645.0: - resolution: {integrity: sha512-Oe+xaU4ic4PB1k3pb5VTC1/MWES13IlgpaQw01bVHGfwP6Yv6zZOxizRzca2Y3E+AyR+nKD7vXtHRY+w3bi4bg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.645.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.3.0 '@smithy/util-endpoints': 2.0.5 tslib: 2.6.3 - /@aws-sdk/util-format-url@3.609.0: - resolution: {integrity: sha512-fuk29BI/oLQlJ7pfm6iJ4gkEpHdavffAALZwXh9eaY1vQ0ip0aKfRTiNudPoJjyyahnz5yJ1HkmlcDitlzsOrQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-format-url@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/querystring-builder': 3.0.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-locate-window@3.568.0: - resolution: {integrity: sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-locate-window@3.568.0': dependencies: tslib: 2.6.3 - /@aws-sdk/util-user-agent-browser@3.387.0: - resolution: {integrity: sha512-lpgSVvDqx+JjHZCTYs/yQSS7J71dPlJeAlvxc7bmx5m+vfwKe07HAnIs+929DngS0QbAp/VaXbTiMFsInLkO4Q==} + '@aws-sdk/util-user-agent-browser@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/types': 2.12.0 bowser: 2.11.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-user-agent-browser@3.598.0: - resolution: {integrity: sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==} + '@aws-sdk/util-user-agent-browser@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/types': 3.3.0 bowser: 2.11.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-user-agent-browser@3.609.0: - resolution: {integrity: sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==} + '@aws-sdk/util-user-agent-browser@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.3.0 bowser: 2.11.0 tslib: 2.6.3 - /@aws-sdk/util-user-agent-node@3.387.0: - resolution: {integrity: sha512-r9OVkcWpRYatjLhJacuHFgvO2T5s/Nu5DDbScMrkUD8b4aGIIqsrdZji0vZy9FCjsUFQMM92t9nt4SejrGjChA==} - engines: {node: '>=14.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true + '@aws-sdk/util-user-agent-node@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/node-config-provider': 2.3.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-user-agent-node@3.598.0: - resolution: {integrity: sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==} - engines: {node: '>=16.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true + '@aws-sdk/util-user-agent-node@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/node-config-provider': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-user-agent-node@3.609.0: - resolution: {integrity: sha512-DlZBwQ/HkZyf3pOWc7+wjJRk5R7x9YxHhs2szHwtv1IW30KMabjjjX0GMlGJ9LLkBHkbaaEY/w9Tkj12XRLhRg==} - engines: {node: '>=16.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true + '@aws-sdk/util-user-agent-node@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/node-config-provider': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/util-user-agent-node@3.614.0: - resolution: {integrity: sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==} - engines: {node: '>=16.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true + '@aws-sdk/util-user-agent-node@3.614.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/node-config-provider': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/util-utf8-browser@3.259.0: - resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} + '@aws-sdk/util-utf8-browser@3.259.0': dependencies: tslib: 2.6.3 - dev: false - /@aws-sdk/xml-builder@3.310.0: - resolution: {integrity: sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/xml-builder@3.310.0': dependencies: tslib: 2.6.3 - dev: false - /@aws-sdk/xml-builder@3.598.0: - resolution: {integrity: sha512-ZIa2RK7CHFTZ4gwK77WRtsZ6vF7xwRXxJ8KQIxK2duhoTVcn0xYxpFLdW9WZZZvdP9GIF3Loqvf8DRdeU5Jc7Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/xml-builder@3.598.0': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@babel/code-frame@7.24.7: - resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} - engines: {node: '>=6.9.0'} + '@babel/code-frame@7.24.7': dependencies: '@babel/highlight': 7.24.7 picocolors: 1.0.1 - /@babel/compat-data@7.24.7: - resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} - engines: {node: '>=6.9.0'} + '@babel/compat-data@7.24.7': {} - /@babel/core@7.24.7: - resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} - engines: {node: '>=6.9.0'} + '@babel/core@7.24.7': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.24.7 @@ -5837,18 +10035,14 @@ packages: transitivePeerDependencies: - supports-color - /@babel/generator@7.24.7: - resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} - engines: {node: '>=6.9.0'} + '@babel/generator@7.24.7': dependencies: '@babel/types': 7.24.7 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 - /@babel/helper-compilation-targets@7.24.7: - resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} - engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.24.7': dependencies: '@babel/compat-data': 7.24.7 '@babel/helper-validator-option': 7.24.7 @@ -5856,39 +10050,27 @@ packages: lru-cache: 5.1.1 semver: 6.3.1 - /@babel/helper-environment-visitor@7.24.7: - resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} - engines: {node: '>=6.9.0'} + '@babel/helper-environment-visitor@7.24.7': dependencies: '@babel/types': 7.24.7 - /@babel/helper-function-name@7.24.7: - resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} - engines: {node: '>=6.9.0'} + '@babel/helper-function-name@7.24.7': dependencies: '@babel/template': 7.24.7 '@babel/types': 7.24.7 - /@babel/helper-hoist-variables@7.24.7: - resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} - engines: {node: '>=6.9.0'} + '@babel/helper-hoist-variables@7.24.7': dependencies: '@babel/types': 7.24.7 - /@babel/helper-module-imports@7.24.7: - resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} - engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.24.7': dependencies: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 transitivePeerDependencies: - supports-color - /@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7): - resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-environment-visitor': 7.24.7 @@ -5899,67 +10081,46 @@ packages: transitivePeerDependencies: - supports-color - /@babel/helper-simple-access@7.24.7: - resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} - engines: {node: '>=6.9.0'} + '@babel/helper-simple-access@7.24.7': dependencies: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 transitivePeerDependencies: - supports-color - /@babel/helper-split-export-declaration@7.24.7: - resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} - engines: {node: '>=6.9.0'} + '@babel/helper-split-export-declaration@7.24.7': dependencies: '@babel/types': 7.24.7 - /@babel/helper-string-parser@7.24.7: - resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} - engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.24.7': {} - /@babel/helper-validator-identifier@7.24.7: - resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} - engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.24.7': {} - /@babel/helper-validator-option@7.24.7: - resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} - engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.24.7': {} - /@babel/helpers@7.24.7: - resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} - engines: {node: '>=6.9.0'} + '@babel/helpers@7.24.7': dependencies: '@babel/template': 7.24.7 '@babel/types': 7.24.7 - /@babel/highlight@7.24.7: - resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} - engines: {node: '>=6.9.0'} + '@babel/highlight@7.24.7': dependencies: '@babel/helper-validator-identifier': 7.24.7 chalk: 2.4.2 js-tokens: 4.0.0 picocolors: 1.0.1 - /@babel/parser@7.24.7: - resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} - engines: {node: '>=6.0.0'} - hasBin: true + '@babel/parser@7.24.7': dependencies: '@babel/types': 7.24.7 - /@babel/template@7.24.7: - resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} - engines: {node: '>=6.9.0'} + '@babel/template@7.24.7': dependencies: '@babel/code-frame': 7.24.7 '@babel/parser': 7.24.7 '@babel/types': 7.24.7 - /@babel/traverse@7.24.7: - resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} - engines: {node: '>=6.9.0'} + '@babel/traverse@7.24.7': dependencies: '@babel/code-frame': 7.24.7 '@babel/generator': 7.24.7 @@ -5974,488 +10135,181 @@ packages: transitivePeerDependencies: - supports-color - /@babel/types@7.24.7: - resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} - engines: {node: '>=6.9.0'} + '@babel/types@7.24.7': dependencies: '@babel/helper-string-parser': 7.24.7 '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 - /@balena/dockerignore@1.0.2: - resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} - dev: true + '@balena/dockerignore@1.0.2': {} - /@colors/colors@1.6.0: - resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} - engines: {node: '>=0.1.90'} - dev: false + '@colors/colors@1.6.0': {} - /@cspotcode/source-map-support@0.8.1: - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 - dev: true - /@dabh/diagnostics@2.0.3: - resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + '@dabh/diagnostics@2.0.3': dependencies: colorspace: 1.1.4 enabled: 2.0.0 kuler: 2.0.0 - dev: false - /@es-joy/jsdoccomment@0.36.1: - resolution: {integrity: sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==} - engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} + '@es-joy/jsdoccomment@0.36.1': dependencies: comment-parser: 1.3.1 esquery: 1.5.0 jsdoc-type-pratt-parser: 3.1.0 - dev: true - - /@esbuild/aix-ppc64@0.21.5: - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - requiresBuild: true - dev: true - optional: true - /@esbuild/aix-ppc64@0.23.1: - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - requiresBuild: true - dev: true + '@esbuild/aix-ppc64@0.21.5': optional: true - /@esbuild/android-arm64@0.21.5: - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/aix-ppc64@0.23.1': optional: true - /@esbuild/android-arm64@0.23.1: - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm64@0.21.5': optional: true - /@esbuild/android-arm@0.21.5: - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm64@0.23.1': optional: true - /@esbuild/android-arm@0.23.1: - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm@0.21.5': optional: true - /@esbuild/android-x64@0.21.5: - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm@0.23.1': optional: true - /@esbuild/android-x64@0.23.1: - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-x64@0.21.5': optional: true - - /@esbuild/darwin-arm64@0.21.5: - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + + '@esbuild/android-x64@0.23.1': optional: true - /@esbuild/darwin-arm64@0.23.1: - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-arm64@0.21.5': optional: true - /@esbuild/darwin-x64@0.21.5: - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-arm64@0.23.1': optional: true - /@esbuild/darwin-x64@0.23.1: - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-x64@0.21.5': optional: true - /@esbuild/freebsd-arm64@0.21.5: - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/darwin-x64@0.23.1': optional: true - /@esbuild/freebsd-arm64@0.23.1: - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-arm64@0.21.5': optional: true - /@esbuild/freebsd-x64@0.21.5: - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-arm64@0.23.1': optional: true - /@esbuild/freebsd-x64@0.23.1: - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-x64@0.21.5': optional: true - /@esbuild/linux-arm64@0.21.5: - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/freebsd-x64@0.23.1': optional: true - /@esbuild/linux-arm64@0.23.1: - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm64@0.21.5': optional: true - /@esbuild/linux-arm@0.21.5: - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm64@0.23.1': optional: true - /@esbuild/linux-arm@0.23.1: - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm@0.21.5': optional: true - /@esbuild/linux-ia32@0.21.5: - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm@0.23.1': optional: true - /@esbuild/linux-ia32@0.23.1: - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ia32@0.21.5': optional: true - /@esbuild/linux-loong64@0.21.5: - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ia32@0.23.1': optional: true - /@esbuild/linux-loong64@0.23.1: - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-loong64@0.21.5': optional: true - /@esbuild/linux-mips64el@0.21.5: - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-loong64@0.23.1': optional: true - /@esbuild/linux-mips64el@0.23.1: - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-mips64el@0.21.5': optional: true - /@esbuild/linux-ppc64@0.21.5: - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-mips64el@0.23.1': optional: true - /@esbuild/linux-ppc64@0.23.1: - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ppc64@0.21.5': optional: true - /@esbuild/linux-riscv64@0.21.5: - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ppc64@0.23.1': optional: true - /@esbuild/linux-riscv64@0.23.1: - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-riscv64@0.21.5': optional: true - /@esbuild/linux-s390x@0.21.5: - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-riscv64@0.23.1': optional: true - /@esbuild/linux-s390x@0.23.1: - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-s390x@0.21.5': optional: true - /@esbuild/linux-x64@0.21.5: - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-s390x@0.23.1': optional: true - /@esbuild/linux-x64@0.23.1: - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-x64@0.21.5': optional: true - /@esbuild/netbsd-x64@0.21.5: - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true + '@esbuild/linux-x64@0.23.1': optional: true - /@esbuild/netbsd-x64@0.23.1: - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true + '@esbuild/netbsd-x64@0.21.5': optional: true - /@esbuild/openbsd-arm64@0.23.1: - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - requiresBuild: true - dev: true + '@esbuild/netbsd-x64@0.23.1': optional: true - /@esbuild/openbsd-x64@0.21.5: - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true + '@esbuild/openbsd-arm64@0.23.1': optional: true - /@esbuild/openbsd-x64@0.23.1: - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true + '@esbuild/openbsd-x64@0.21.5': optional: true - /@esbuild/sunos-x64@0.21.5: - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true + '@esbuild/openbsd-x64@0.23.1': optional: true - /@esbuild/sunos-x64@0.23.1: - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true + '@esbuild/sunos-x64@0.21.5': optional: true - /@esbuild/win32-arm64@0.21.5: - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/sunos-x64@0.23.1': optional: true - /@esbuild/win32-arm64@0.23.1: - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-arm64@0.21.5': optional: true - /@esbuild/win32-ia32@0.21.5: - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-arm64@0.23.1': optional: true - /@esbuild/win32-ia32@0.23.1: - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-ia32@0.21.5': optional: true - /@esbuild/win32-x64@0.21.5: - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-ia32@0.23.1': optional: true - /@esbuild/win32-x64@0.23.1: - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-x64@0.21.5': optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@esbuild/win32-x64@0.23.1': + optional: true + + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': dependencies: eslint: 8.57.0 eslint-visitor-keys: 3.4.3 - dev: true - /@eslint-community/regexpp@4.11.0: - resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - dev: true + '@eslint-community/regexpp@4.11.0': {} - /@eslint/eslintrc@2.1.4: - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 debug: 4.3.5 @@ -6468,147 +10322,98 @@ packages: strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - dev: true - /@eslint/js@8.57.0: - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true + '@eslint/js@8.57.0': {} - /@ewoudenberg/difflib@0.1.0: - resolution: {integrity: sha512-OU5P5mJyD3OoWYMWY+yIgwvgNS9cFAU10f+DDuvtogcWQOoJIsQ4Hy2McSfUfhKjq8L0FuWVb4Rt7kgA+XK86A==} + '@ewoudenberg/difflib@0.1.0': dependencies: heap: 0.2.7 - dev: false - /@faker-js/faker@8.4.1: - resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} - dev: true + '@faker-js/faker@8.4.1': {} - /@humanwhocodes/config-array@0.11.14: - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead + '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 debug: 4.3.5 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - dev: true - /@humanwhocodes/module-importer@1.0.1: - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - dev: true + '@humanwhocodes/module-importer@1.0.1': {} - /@humanwhocodes/object-schema@2.0.3: - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead - dev: true + '@humanwhocodes/object-schema@2.0.3': {} - /@isaacs/cliui@8.0.2: - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 - string-width-cjs: /string-width@4.2.3 + string-width-cjs: string-width@4.2.3 strip-ansi: 7.1.0 - strip-ansi-cjs: /strip-ansi@6.0.1 + strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 - wrap-ansi-cjs: /wrap-ansi@7.0.0 - dev: true + wrap-ansi-cjs: wrap-ansi@7.0.0 - /@jest/schemas@29.6.3: - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.8 - dev: true - /@jridgewell/gen-mapping@0.3.5: - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} - engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.25 - /@jridgewell/resolve-uri@3.1.2: - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} + '@jridgewell/resolve-uri@3.1.2': {} - /@jridgewell/set-array@1.2.1: - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} + '@jridgewell/set-array@1.2.1': {} - /@jridgewell/sourcemap-codec@1.4.15: - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.4.15': {} - /@jridgewell/trace-mapping@0.3.25: - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - /@jridgewell/trace-mapping@0.3.9: - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - /@jsdevtools/ono@7.1.3: - resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + '@jsdevtools/ono@7.1.3': {} - /@liuli-util/fs-extra@0.1.0: - resolution: {integrity: sha512-eaAyDyMGT23QuRGbITVY3SOJff3G9ekAAyGqB9joAnTBmqvFN+9a1FazOdO70G6IUqgpKV451eBHYSRcOJ/FNQ==} + '@liuli-util/fs-extra@0.1.0': dependencies: '@types/fs-extra': 9.0.13 fs-extra: 10.1.0 - /@mongodb-js/saslprep@1.1.7: - resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} + '@mongodb-js/saslprep@1.1.7': dependencies: sparse-bitfield: 3.0.3 - dev: false - /@nodelib/fs.scandir@2.1.5: - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - dev: true - /@nodelib/fs.stat@2.0.5: - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - dev: true + '@nodelib/fs.stat@2.0.5': {} - /@nodelib/fs.walk@1.2.8: - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - dev: true - /@pagopa/eslint-config@3.0.0(typescript@5.4.5): - resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} + '@pagopa/eslint-config@3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5)': dependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5) '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-config-prettier: 8.10.0(eslint@8.57.0) eslint-plugin-extra-rules: 0.0.0-development eslint-plugin-fp: 2.3.0(eslint@8.57.0) - eslint-plugin-functional: 4.4.1(eslint@8.57.0)(typescript@5.4.5) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0)(eslint@8.57.0) + eslint-plugin-functional: 4.4.1(eslint@8.57.0)(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0) eslint-plugin-jsdoc: 39.9.1(eslint@8.57.0) eslint-plugin-prefer-arrow: 1.2.3(eslint@8.57.0) - eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.10.0)(eslint@8.57.0)(prettier@2.8.8) + eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8) eslint-plugin-react: 7.34.3(eslint@8.57.0) eslint-plugin-sonarjs: 0.13.0(eslint@8.57.0) prettier: 2.8.8 @@ -6618,59 +10423,38 @@ packages: - supports-color - tsutils - typescript - dev: true - /@pagopa/interop-outbound-models@1.0.4-b: - resolution: {integrity: sha512-c1kxjIBCU0gqV2s3bLIdor+fIfvGLZgML2Y26dTdY3ppoXOhJBVTWKVBu5mq8nmZvZj0KnmK7tjeD6NUJjlywg==} + '@pagopa/interop-outbound-models@1.0.4-b': dependencies: '@protobuf-ts/runtime': 2.9.4 ts-pattern: 5.2.0 zod: 3.23.8 - dev: false - /@pkgjs/parseargs@0.11.0: - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - requiresBuild: true - dev: true + '@pkgjs/parseargs@0.11.0': optional: true - /@protobuf-ts/plugin-framework@2.9.4: - resolution: {integrity: sha512-9nuX1kjdMliv+Pes8dQCKyVhjKgNNfwxVHg+tx3fLXSfZZRcUHMc1PMwB9/vTvc6gBKt9QGz5ERqSqZc0++E9A==} + '@protobuf-ts/plugin-framework@2.9.4': dependencies: '@protobuf-ts/runtime': 2.9.4 typescript: 3.9.10 - dev: false - /@protobuf-ts/plugin@2.9.4: - resolution: {integrity: sha512-Db5Laq5T3mc6ERZvhIhkj1rn57/p8gbWiCKxQWbZBBl20wMuqKoHbRw4tuD7FyXi+IkwTToaNVXymv5CY3E8Rw==} - hasBin: true + '@protobuf-ts/plugin@2.9.4': dependencies: '@protobuf-ts/plugin-framework': 2.9.4 '@protobuf-ts/protoc': 2.9.4 '@protobuf-ts/runtime': 2.9.4 '@protobuf-ts/runtime-rpc': 2.9.4 typescript: 3.9.10 - dev: false - /@protobuf-ts/protoc@2.9.4: - resolution: {integrity: sha512-hQX+nOhFtrA+YdAXsXEDrLoGJqXHpgv4+BueYF0S9hy/Jq0VRTVlJS1Etmf4qlMt/WdigEes5LOd/LDzui4GIQ==} - hasBin: true - dev: false + '@protobuf-ts/protoc@2.9.4': {} - /@protobuf-ts/runtime-rpc@2.9.4: - resolution: {integrity: sha512-y9L9JgnZxXFqH5vD4d7j9duWvIJ7AShyBRoNKJGhu9Q27qIbchfzli66H9RvrQNIFk5ER7z1Twe059WZGqERcA==} + '@protobuf-ts/runtime-rpc@2.9.4': dependencies: '@protobuf-ts/runtime': 2.9.4 - dev: false - /@protobuf-ts/runtime@2.9.4: - resolution: {integrity: sha512-vHRFWtJJB/SiogWDF0ypoKfRIZ41Kq+G9cEFj6Qm1eQaAhJ1LDFvgZ7Ja4tb3iLOQhz0PaoPnnOijF1qmEqTxg==} + '@protobuf-ts/runtime@2.9.4': {} - /@puppeteer/browsers@2.2.3: - resolution: {integrity: sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==} - engines: {node: '>=18'} - hasBin: true + '@puppeteer/browsers@2.2.3': dependencies: debug: 4.3.4 extract-zip: 2.0.1 @@ -6683,272 +10467,139 @@ packages: transitivePeerDependencies: - supports-color - /@redis/bloom@1.2.0(@redis/client@1.5.17): - resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/bloom@1.2.0(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@redis/client@1.5.17: - resolution: {integrity: sha512-IPvU9A31qRCZ7lds/x+ksuK/UMndd0EASveAvCvEtFFKIZjZ+m/a4a0L7S28KEWoR5ka8526hlSghDo4Hrc2Hg==} - engines: {node: '>=14'} + '@redis/client@1.5.17': dependencies: cluster-key-slot: 1.1.2 generic-pool: 3.9.0 yallist: 4.0.0 - dev: false - /@redis/graph@1.1.1(@redis/client@1.5.17): - resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/graph@1.1.1(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@redis/json@1.0.6(@redis/client@1.5.17): - resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/json@1.0.6(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@redis/search@1.1.6(@redis/client@1.5.17): - resolution: {integrity: sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/search@1.1.6(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@redis/time-series@1.0.5(@redis/client@1.5.17): - resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/time-series@1.0.5(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@rollup/rollup-android-arm-eabi@4.18.1: - resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@rollup/rollup-android-arm-eabi@4.18.1': optional: true - /@rollup/rollup-android-arm64@4.18.1: - resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@rollup/rollup-android-arm64@4.18.1': optional: true - /@rollup/rollup-darwin-arm64@4.18.1: - resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + '@rollup/rollup-darwin-arm64@4.18.1': optional: true - /@rollup/rollup-darwin-x64@4.18.1: - resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@rollup/rollup-darwin-x64@4.18.1': optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.18.1: - resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': optional: true - /@rollup/rollup-linux-arm-musleabihf@4.18.1: - resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm-musleabihf@4.18.1': optional: true - /@rollup/rollup-linux-arm64-gnu@4.18.1: - resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-arm64-musl@4.18.1: - resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm64-musl@4.18.1': optional: true - - /@rollup/rollup-linux-powerpc64le-gnu@4.18.1: - resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': optional: true - /@rollup/rollup-linux-riscv64-gnu@4.18.1: - resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-riscv64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-s390x-gnu@4.18.1: - resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-s390x-gnu@4.18.1': optional: true - /@rollup/rollup-linux-x64-gnu@4.18.1: - resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-x64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-x64-musl@4.18.1: - resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-x64-musl@4.18.1': optional: true - /@rollup/rollup-win32-arm64-msvc@4.18.1: - resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-arm64-msvc@4.18.1': optional: true - /@rollup/rollup-win32-ia32-msvc@4.18.1: - resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-ia32-msvc@4.18.1': optional: true - /@rollup/rollup-win32-x64-msvc@4.18.1: - resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-x64-msvc@4.18.1': optional: true - /@sinclair/typebox@0.27.8: - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - dev: true + '@sinclair/typebox@0.27.8': {} - /@sinonjs/commons@3.0.1: - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 - dev: true - /@sinonjs/fake-timers@10.3.0: - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@sinonjs/fake-timers@10.3.0': dependencies: '@sinonjs/commons': 3.0.1 - dev: true - /@sinonjs/fake-timers@11.3.1: - resolution: {integrity: sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==} + '@sinonjs/fake-timers@11.3.1': dependencies: '@sinonjs/commons': 3.0.1 - dev: true - /@sinonjs/samsam@8.0.2: - resolution: {integrity: sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==} + '@sinonjs/samsam@8.0.2': dependencies: '@sinonjs/commons': 3.0.1 lodash.get: 4.4.2 type-detect: 4.1.0 - dev: true - /@sinonjs/text-encoding@0.7.3: - resolution: {integrity: sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==} - dev: true + '@sinonjs/text-encoding@0.7.3': {} - /@smithy/abort-controller@2.2.0: - resolution: {integrity: sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==} - engines: {node: '>=14.0.0'} + '@smithy/abort-controller@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/abort-controller@3.1.1: - resolution: {integrity: sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==} - engines: {node: '>=16.0.0'} + '@smithy/abort-controller@3.1.1': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/chunked-blob-reader-native@2.2.0: - resolution: {integrity: sha512-VNB5+1oCgX3Fzs072yuRsUoC2N4Zg/LJ11DTxX3+Qu+Paa6AmbIF0E9sc2wthz9Psrk/zcOlTCyuposlIhPjZQ==} + '@smithy/chunked-blob-reader-native@2.2.0': dependencies: '@smithy/util-base64': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/chunked-blob-reader-native@3.0.0: - resolution: {integrity: sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg==} + '@smithy/chunked-blob-reader-native@3.0.0': dependencies: '@smithy/util-base64': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/chunked-blob-reader@2.2.0: - resolution: {integrity: sha512-3GJNvRwXBGdkDZZOGiziVYzDpn4j6zfyULHMDKAGIUo72yHALpE9CbhfQp/XcLNVoc1byfMpn6uW5H2BqPjgaQ==} + '@smithy/chunked-blob-reader@2.2.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/chunked-blob-reader@3.0.0: - resolution: {integrity: sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA==} + '@smithy/chunked-blob-reader@3.0.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/config-resolver@2.2.0: - resolution: {integrity: sha512-fsiMgd8toyUba6n1WRmr+qACzXltpdDkPTAaDqc8QqPBUzO+/JKwL6bUBseHVi8tu9l+3JOK+tSf7cay+4B3LA==} - engines: {node: '>=14.0.0'} + '@smithy/config-resolver@2.2.0': dependencies: '@smithy/node-config-provider': 2.3.0 '@smithy/types': 2.12.0 '@smithy/util-config-provider': 2.3.0 '@smithy/util-middleware': 2.2.0 tslib: 2.6.3 - dev: false - /@smithy/config-resolver@3.0.4: - resolution: {integrity: sha512-VwiOk7TwXoE7NlNguV/aPq1hFH72tqkHCw8eWXbr2xHspRyyv9DLpLXhq+Ieje+NwoqXrY0xyQjPXdOE6cGcHA==} - engines: {node: '>=16.0.0'} + '@smithy/config-resolver@3.0.4': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/types': 3.3.0 @@ -6956,9 +10607,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@smithy/config-resolver@3.0.5: - resolution: {integrity: sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==} - engines: {node: '>=16.0.0'} + '@smithy/config-resolver@3.0.5': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/types': 3.3.0 @@ -6966,9 +10615,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@smithy/core@2.2.4: - resolution: {integrity: sha512-qdY3LpMOUyLM/gfjjMQZui+UTNS7kBRDWlvyIhVOql5dn2J3isk9qUTBtQ1CbDH8MTugHis1zu3h4rH+Qmmh4g==} - engines: {node: '>=16.0.0'} + '@smithy/core@2.2.4': dependencies: '@smithy/middleware-endpoint': 3.0.4 '@smithy/middleware-retry': 3.0.7 @@ -6979,9 +10626,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@smithy/core@2.4.0: - resolution: {integrity: sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==} - engines: {node: '>=16.0.0'} + '@smithy/core@2.4.0': dependencies: '@smithy/middleware-endpoint': 3.1.0 '@smithy/middleware-retry': 3.0.15 @@ -6994,20 +10639,15 @@ packages: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/credential-provider-imds@2.3.0: - resolution: {integrity: sha512-BWB9mIukO1wjEOo1Ojgl6LrG4avcaC7T/ZP6ptmAaW4xluhSIPZhY+/PI5YKzlk+jsm+4sQZB45Bt1OfMeQa3w==} - engines: {node: '>=14.0.0'} + '@smithy/credential-provider-imds@2.3.0': dependencies: '@smithy/node-config-provider': 2.3.0 '@smithy/property-provider': 2.2.0 '@smithy/types': 2.12.0 '@smithy/url-parser': 2.2.0 tslib: 2.6.3 - dev: false - /@smithy/credential-provider-imds@3.1.3: - resolution: {integrity: sha512-U1Yrv6hx/mRK6k8AncuI6jLUx9rn0VVSd9NPEX6pyYFBfkSkChOc/n4zUb8alHUVg83TbI4OdZVo1X0Zfj3ijA==} - engines: {node: '>=16.0.0'} + '@smithy/credential-provider-imds@3.1.3': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/property-provider': 3.1.3 @@ -7015,9 +10655,7 @@ packages: '@smithy/url-parser': 3.0.3 tslib: 2.6.3 - /@smithy/credential-provider-imds@3.2.0: - resolution: {integrity: sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==} - engines: {node: '>=16.0.0'} + '@smithy/credential-provider-imds@3.2.0': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/property-provider': 3.1.3 @@ -7025,106 +10663,75 @@ packages: '@smithy/url-parser': 3.0.3 tslib: 2.6.3 - /@smithy/eventstream-codec@2.2.0: - resolution: {integrity: sha512-8janZoJw85nJmQZc4L8TuePp2pk1nxLgkxIR0TUjKJ5Dkj5oelB9WtiSSGXCQvNsJl0VSTvK/2ueMXxvpa9GVw==} + '@smithy/eventstream-codec@2.2.0': dependencies: '@aws-crypto/crc32': 3.0.0 '@smithy/types': 2.12.0 '@smithy/util-hex-encoding': 2.2.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-codec@3.1.2: - resolution: {integrity: sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==} + '@smithy/eventstream-codec@3.1.2': dependencies: '@aws-crypto/crc32': 5.2.0 '@smithy/types': 3.3.0 '@smithy/util-hex-encoding': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-browser@2.2.0: - resolution: {integrity: sha512-UaPf8jKbcP71BGiO0CdeLmlg+RhWnlN8ipsMSdwvqBFigl5nil3rHOI/5GE3tfiuX8LvY5Z9N0meuU7Rab7jWw==} - engines: {node: '>=14.0.0'} + '@smithy/eventstream-serde-browser@2.2.0': dependencies: '@smithy/eventstream-serde-universal': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-browser@3.0.4: - resolution: {integrity: sha512-Eo4anLZX6ltGJTZ5yJMc80gZPYYwBn44g0h7oFq6et+TYr5dUsTpIcDbz2evsOKIZhZ7zBoFWHtBXQ4QQeb5xA==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-browser@3.0.4': dependencies: '@smithy/eventstream-serde-universal': 3.0.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-config-resolver@2.2.0: - resolution: {integrity: sha512-RHhbTw/JW3+r8QQH7PrganjNCiuiEZmpi6fYUAetFfPLfZ6EkiA08uN3EFfcyKubXQxOwTeJRZSQmDDCdUshaA==} - engines: {node: '>=14.0.0'} + '@smithy/eventstream-serde-config-resolver@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-config-resolver@3.0.3: - resolution: {integrity: sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-config-resolver@3.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-node@2.2.0: - resolution: {integrity: sha512-zpQMtJVqCUMn+pCSFcl9K/RPNtQE0NuMh8sKpCdEHafhwRsjP50Oq/4kMmvxSRy6d8Jslqd8BLvDngrUtmN9iA==} - engines: {node: '>=14.0.0'} + '@smithy/eventstream-serde-node@2.2.0': dependencies: '@smithy/eventstream-serde-universal': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-node@3.0.4: - resolution: {integrity: sha512-mjlG0OzGAYuUpdUpflfb9zyLrBGgmQmrobNT8b42ZTsGv/J03+t24uhhtVEKG/b2jFtPIHF74Bq+VUtbzEKOKg==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-node@3.0.4': dependencies: '@smithy/eventstream-serde-universal': 3.0.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-universal@2.2.0: - resolution: {integrity: sha512-pvoe/vvJY0mOpuF84BEtyZoYfbehiFj8KKWk1ds2AT0mTLYFVs+7sBJZmioOFdBXKd48lfrx1vumdPdmGlCLxA==} - engines: {node: '>=14.0.0'} + '@smithy/eventstream-serde-universal@2.2.0': dependencies: '@smithy/eventstream-codec': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-universal@3.0.4: - resolution: {integrity: sha512-Od9dv8zh3PgOD7Vj4T3HSuox16n0VG8jJIM2gvKASL6aCtcS8CfHZDWe1Ik3ZXW6xBouU+45Q5wgoliWDZiJ0A==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-universal@3.0.4': dependencies: '@smithy/eventstream-codec': 3.1.2 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@smithy/fetch-http-handler@2.5.0: - resolution: {integrity: sha512-BOWEBeppWhLn/no/JxUL/ghTfANTjT7kg3Ww2rPqTUY9R4yHPXxJ9JhMe3Z03LN3aPwiwlpDIUcVw1xDyHqEhw==} + '@smithy/fetch-http-handler@2.5.0': dependencies: '@smithy/protocol-http': 3.3.0 '@smithy/querystring-builder': 2.2.0 '@smithy/types': 2.12.0 '@smithy/util-base64': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/fetch-http-handler@3.2.0: - resolution: {integrity: sha512-vFvDxMrc6sO5Atec8PaISckMcAwsCrRhYxwUylg97bRT2KZoumOF7qk5+6EVUtuM1IG9AJV5aqXnHln9ZdXHpg==} + '@smithy/fetch-http-handler@3.2.0': dependencies: '@smithy/protocol-http': 4.0.3 '@smithy/querystring-builder': 3.0.3 @@ -7132,8 +10739,7 @@ packages: '@smithy/util-base64': 3.0.0 tslib: 2.6.3 - /@smithy/fetch-http-handler@3.2.4: - resolution: {integrity: sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==} + '@smithy/fetch-http-handler@3.2.4': dependencies: '@smithy/protocol-http': 4.1.0 '@smithy/querystring-builder': 3.0.3 @@ -7141,130 +10747,95 @@ packages: '@smithy/util-base64': 3.0.0 tslib: 2.6.3 - /@smithy/hash-blob-browser@2.2.0: - resolution: {integrity: sha512-SGPoVH8mdXBqrkVCJ1Hd1X7vh1zDXojNN1yZyZTZsCno99hVue9+IYzWDjq/EQDDXxmITB0gBmuyPh8oAZSTcg==} + '@smithy/hash-blob-browser@2.2.0': dependencies: '@smithy/chunked-blob-reader': 2.2.0 '@smithy/chunked-blob-reader-native': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/hash-blob-browser@3.1.2: - resolution: {integrity: sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg==} + '@smithy/hash-blob-browser@3.1.2': dependencies: '@smithy/chunked-blob-reader': 3.0.0 '@smithy/chunked-blob-reader-native': 3.0.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@smithy/hash-node@2.2.0: - resolution: {integrity: sha512-zLWaC/5aWpMrHKpoDF6nqpNtBhlAYKF/7+9yMN7GpdR8CzohnWfGtMznPybnwSS8saaXBMxIGwJqR4HmRp6b3g==} - engines: {node: '>=14.0.0'} + '@smithy/hash-node@2.2.0': dependencies: '@smithy/types': 2.12.0 '@smithy/util-buffer-from': 2.2.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/hash-node@3.0.3: - resolution: {integrity: sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==} - engines: {node: '>=16.0.0'} + '@smithy/hash-node@3.0.3': dependencies: '@smithy/types': 3.3.0 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/hash-stream-node@2.2.0: - resolution: {integrity: sha512-aT+HCATOSRMGpPI7bi7NSsTNVZE/La9IaxLXWoVAYMxHT5hGO3ZOGEMZQg8A6nNL+pdFGtZQtND1eoY084HgHQ==} - engines: {node: '>=14.0.0'} + '@smithy/hash-stream-node@2.2.0': dependencies: '@smithy/types': 2.12.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/hash-stream-node@3.1.2: - resolution: {integrity: sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g==} - engines: {node: '>=16.0.0'} + '@smithy/hash-stream-node@3.1.2': dependencies: '@smithy/types': 3.3.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/invalid-dependency@2.2.0: - resolution: {integrity: sha512-nEDASdbKFKPXN2O6lOlTgrEEOO9NHIeO+HVvZnkqc8h5U9g3BIhWsvzFo+UcUbliMHvKNPD/zVxDrkP1Sbgp8Q==} + '@smithy/invalid-dependency@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/invalid-dependency@3.0.3: - resolution: {integrity: sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==} + '@smithy/invalid-dependency@3.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/is-array-buffer@2.2.0: - resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} - engines: {node: '>=14.0.0'} + '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.6.3 - /@smithy/is-array-buffer@3.0.0: - resolution: {integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==} - engines: {node: '>=16.0.0'} + '@smithy/is-array-buffer@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/md5-js@2.2.0: - resolution: {integrity: sha512-M26XTtt9IIusVMOWEAhIvFIr9jYj4ISPPGJROqw6vXngO3IYJCnVVSMFn4Tx1rUTG5BiKJNg9u2nxmBiZC5IlQ==} + '@smithy/md5-js@2.2.0': dependencies: '@smithy/types': 2.12.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/md5-js@3.0.3: - resolution: {integrity: sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==} + '@smithy/md5-js@3.0.3': dependencies: '@smithy/types': 3.3.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/middleware-content-length@2.2.0: - resolution: {integrity: sha512-5bl2LG1Ah/7E5cMSC+q+h3IpVHMeOkG0yLRyQT1p2aMJkSrZG7RlXHPuAgb7EyaFeidKEnnd/fNaLLaKlHGzDQ==} - engines: {node: '>=14.0.0'} + '@smithy/middleware-content-length@2.2.0': dependencies: '@smithy/protocol-http': 3.3.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/middleware-content-length@3.0.3: - resolution: {integrity: sha512-Dbz2bzexReYIQDWMr+gZhpwBetNXzbhnEMhYKA6urqmojO14CsXjnsoPYO8UL/xxcawn8ZsuVU61ElkLSltIUQ==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-content-length@3.0.3': dependencies: '@smithy/protocol-http': 4.0.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/middleware-content-length@3.0.5: - resolution: {integrity: sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-content-length@3.0.5': dependencies: '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/middleware-endpoint@2.5.1: - resolution: {integrity: sha512-1/8kFp6Fl4OsSIVTWHnNjLnTL8IqpIb/D3sTSczrKFnrE9VMNWxnrRKNvpUHOJ6zpGD5f62TPm7+17ilTJpiCQ==} - engines: {node: '>=14.0.0'} + '@smithy/middleware-endpoint@2.5.1': dependencies: '@smithy/middleware-serde': 2.3.0 '@smithy/node-config-provider': 2.3.0 @@ -7273,11 +10844,8 @@ packages: '@smithy/url-parser': 2.2.0 '@smithy/util-middleware': 2.2.0 tslib: 2.6.3 - dev: false - /@smithy/middleware-endpoint@3.0.4: - resolution: {integrity: sha512-whUJMEPwl3ANIbXjBXZVdJNgfV2ZU8ayln7xUM47rXL2txuenI7jQ/VFFwCzy5lCmXScjp6zYtptW5Evud8e9g==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-endpoint@3.0.4': dependencies: '@smithy/middleware-serde': 3.0.3 '@smithy/node-config-provider': 3.1.4 @@ -7287,9 +10855,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@smithy/middleware-endpoint@3.1.0: - resolution: {integrity: sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-endpoint@3.1.0': dependencies: '@smithy/middleware-serde': 3.0.3 '@smithy/node-config-provider': 3.1.4 @@ -7299,9 +10865,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@smithy/middleware-retry@2.3.1: - resolution: {integrity: sha512-P2bGufFpFdYcWvqpyqqmalRtwFUNUA8vHjJR5iGqbfR6mp65qKOLcUd6lTr4S9Gn/enynSrSf3p3FVgVAf6bXA==} - engines: {node: '>=14.0.0'} + '@smithy/middleware-retry@2.3.1': dependencies: '@smithy/node-config-provider': 2.3.0 '@smithy/protocol-http': 3.3.0 @@ -7312,11 +10876,8 @@ packages: '@smithy/util-retry': 2.2.0 tslib: 2.6.3 uuid: 9.0.1 - dev: false - /@smithy/middleware-retry@3.0.15: - resolution: {integrity: sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@3.0.15': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/protocol-http': 4.1.0 @@ -7328,9 +10889,7 @@ packages: tslib: 2.6.3 uuid: 9.0.1 - /@smithy/middleware-retry@3.0.7: - resolution: {integrity: sha512-f5q7Y09G+2h5ivkSx5CHvlAT4qRR3jBFEsfXyQ9nFNiWQlr8c48blnu5cmbTQ+p1xmIO14UXzKoF8d7Tm0Gsjw==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@3.0.7': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/protocol-http': 4.1.0 @@ -7342,78 +10901,56 @@ packages: tslib: 2.6.3 uuid: 9.0.1 - /@smithy/middleware-serde@2.3.0: - resolution: {integrity: sha512-sIADe7ojwqTyvEQBe1nc/GXB9wdHhi9UwyX0lTyttmUWDJLP655ZYE1WngnNyXREme8I27KCaUhyhZWRXL0q7Q==} - engines: {node: '>=14.0.0'} + '@smithy/middleware-serde@2.3.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/middleware-serde@3.0.3: - resolution: {integrity: sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-serde@3.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/middleware-stack@2.2.0: - resolution: {integrity: sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA==} - engines: {node: '>=14.0.0'} + '@smithy/middleware-stack@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/middleware-stack@3.0.3: - resolution: {integrity: sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-stack@3.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/node-config-provider@2.3.0: - resolution: {integrity: sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg==} - engines: {node: '>=14.0.0'} + '@smithy/node-config-provider@2.3.0': dependencies: '@smithy/property-provider': 2.2.0 '@smithy/shared-ini-file-loader': 2.4.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/node-config-provider@3.1.3: - resolution: {integrity: sha512-rxdpAZczzholz6CYZxtqDu/aKTxATD5DAUDVj7HoEulq+pDSQVWzbg0btZDlxeFfa6bb2b5tUvgdX5+k8jUqcg==} - engines: {node: '>=16.0.0'} + '@smithy/node-config-provider@3.1.3': dependencies: '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/node-config-provider@3.1.4: - resolution: {integrity: sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==} - engines: {node: '>=16.0.0'} + '@smithy/node-config-provider@3.1.4': dependencies: '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/node-http-handler@2.5.0: - resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==} - engines: {node: '>=14.0.0'} + '@smithy/node-http-handler@2.5.0': dependencies: '@smithy/abort-controller': 2.2.0 '@smithy/protocol-http': 3.3.0 '@smithy/querystring-builder': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/node-http-handler@3.1.1: - resolution: {integrity: sha512-L71NLyPeP450r2J/mfu1jMc//Z1YnqJt2eSNw7uhiItaONnBLDA68J5jgxq8+MBDsYnFwNAIc7dBG1ImiWBiwg==} - engines: {node: '>=16.0.0'} + '@smithy/node-http-handler@3.1.1': dependencies: '@smithy/abort-controller': 3.1.1 '@smithy/protocol-http': 4.1.0 @@ -7421,9 +10958,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/node-http-handler@3.1.4: - resolution: {integrity: sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==} - engines: {node: '>=16.0.0'} + '@smithy/node-http-handler@3.1.4': dependencies: '@smithy/abort-controller': 3.1.1 '@smithy/protocol-http': 4.1.0 @@ -7431,121 +10966,82 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/property-provider@2.2.0: - resolution: {integrity: sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==} - engines: {node: '>=14.0.0'} + '@smithy/property-provider@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/property-provider@3.1.3: - resolution: {integrity: sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==} - engines: {node: '>=16.0.0'} + '@smithy/property-provider@3.1.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/protocol-http@2.0.5: - resolution: {integrity: sha512-d2hhHj34mA2V86doiDfrsy2fNTnUOowGaf9hKb0hIPHqvcnShU4/OSc4Uf1FwHkAdYF3cFXTrj5VGUYbEuvMdw==} - engines: {node: '>=14.0.0'} + '@smithy/protocol-http@2.0.5': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/protocol-http@3.3.0: - resolution: {integrity: sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==} - engines: {node: '>=14.0.0'} + '@smithy/protocol-http@3.3.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/protocol-http@4.0.3: - resolution: {integrity: sha512-x5jmrCWwQlx+Zv4jAtc33ijJ+vqqYN+c/ZkrnpvEe/uDas7AT7A/4Rc2CdfxgWv4WFGmEqODIrrUToPN6DDkGw==} - engines: {node: '>=16.0.0'} + '@smithy/protocol-http@4.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/protocol-http@4.1.0: - resolution: {integrity: sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==} - engines: {node: '>=16.0.0'} + '@smithy/protocol-http@4.1.0': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/querystring-builder@2.2.0: - resolution: {integrity: sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==} - engines: {node: '>=14.0.0'} + '@smithy/querystring-builder@2.2.0': dependencies: '@smithy/types': 2.12.0 '@smithy/util-uri-escape': 2.2.0 tslib: 2.6.3 - dev: false - /@smithy/querystring-builder@3.0.3: - resolution: {integrity: sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==} - engines: {node: '>=16.0.0'} + '@smithy/querystring-builder@3.0.3': dependencies: '@smithy/types': 3.3.0 '@smithy/util-uri-escape': 3.0.0 tslib: 2.6.3 - /@smithy/querystring-parser@2.2.0: - resolution: {integrity: sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA==} - engines: {node: '>=14.0.0'} + '@smithy/querystring-parser@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/querystring-parser@3.0.3: - resolution: {integrity: sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==} - engines: {node: '>=16.0.0'} + '@smithy/querystring-parser@3.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/service-error-classification@2.1.5: - resolution: {integrity: sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ==} - engines: {node: '>=14.0.0'} + '@smithy/service-error-classification@2.1.5': dependencies: '@smithy/types': 2.12.0 - dev: false - /@smithy/service-error-classification@3.0.3: - resolution: {integrity: sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==} - engines: {node: '>=16.0.0'} + '@smithy/service-error-classification@3.0.3': dependencies: '@smithy/types': 3.3.0 - /@smithy/shared-ini-file-loader@2.4.0: - resolution: {integrity: sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA==} - engines: {node: '>=14.0.0'} + '@smithy/shared-ini-file-loader@2.4.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/shared-ini-file-loader@3.1.3: - resolution: {integrity: sha512-Z8Y3+08vgoDgl4HENqNnnzSISAaGrF2RoKupoC47u2wiMp+Z8P/8mDh1CL8+8ujfi2U5naNvopSBmP/BUj8b5w==} - engines: {node: '>=16.0.0'} + '@smithy/shared-ini-file-loader@3.1.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/shared-ini-file-loader@3.1.4: - resolution: {integrity: sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==} - engines: {node: '>=16.0.0'} + '@smithy/shared-ini-file-loader@3.1.4': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/signature-v4@2.3.0: - resolution: {integrity: sha512-ui/NlpILU+6HAQBfJX8BBsDXuKSNrjTSuOYArRblcrErwKFutjrCNb/OExfVRyj9+26F9J+ZmfWT+fKWuDrH3Q==} - engines: {node: '>=14.0.0'} + '@smithy/signature-v4@2.3.0': dependencies: '@smithy/is-array-buffer': 2.2.0 '@smithy/types': 2.12.0 @@ -7554,11 +11050,8 @@ packages: '@smithy/util-uri-escape': 2.2.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/signature-v4@3.1.2: - resolution: {integrity: sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==} - engines: {node: '>=16.0.0'} + '@smithy/signature-v4@3.1.2': dependencies: '@smithy/is-array-buffer': 3.0.0 '@smithy/types': 3.3.0 @@ -7568,9 +11061,7 @@ packages: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/signature-v4@4.1.0: - resolution: {integrity: sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==} - engines: {node: '>=16.0.0'} + '@smithy/signature-v4@4.1.0': dependencies: '@smithy/is-array-buffer': 3.0.0 '@smithy/protocol-http': 4.1.0 @@ -7581,9 +11072,7 @@ packages: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/smithy-client@2.5.1: - resolution: {integrity: sha512-jrbSQrYCho0yDaaf92qWgd+7nAeap5LtHTI51KXqmpIFCceKU3K9+vIVTUH72bOJngBMqa4kyu1VJhRcSrk/CQ==} - engines: {node: '>=14.0.0'} + '@smithy/smithy-client@2.5.1': dependencies: '@smithy/middleware-endpoint': 2.5.1 '@smithy/middleware-stack': 2.2.0 @@ -7591,11 +11080,8 @@ packages: '@smithy/types': 2.12.0 '@smithy/util-stream': 2.2.0 tslib: 2.6.3 - dev: false - /@smithy/smithy-client@3.1.5: - resolution: {integrity: sha512-x9bL9Mx2CT2P1OiUlHM+ZNpbVU6TgT32f9CmTRzqIHA7M4vYrROCWEoC3o4xHNJASoGd4Opos3cXYPgh+/m4Ww==} - engines: {node: '>=16.0.0'} + '@smithy/smithy-client@3.1.5': dependencies: '@smithy/middleware-endpoint': 3.0.4 '@smithy/middleware-stack': 3.0.3 @@ -7604,9 +11090,7 @@ packages: '@smithy/util-stream': 3.0.5 tslib: 2.6.3 - /@smithy/smithy-client@3.2.0: - resolution: {integrity: sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==} - engines: {node: '>=16.0.0'} + '@smithy/smithy-client@3.2.0': dependencies: '@smithy/middleware-endpoint': 3.1.0 '@smithy/middleware-stack': 3.0.3 @@ -7615,116 +11099,81 @@ packages: '@smithy/util-stream': 3.1.3 tslib: 2.6.3 - /@smithy/types@2.12.0: - resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==} - engines: {node: '>=14.0.0'} + '@smithy/types@2.12.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/types@3.3.0: - resolution: {integrity: sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==} - engines: {node: '>=16.0.0'} + '@smithy/types@3.3.0': dependencies: tslib: 2.6.3 - /@smithy/url-parser@2.2.0: - resolution: {integrity: sha512-hoA4zm61q1mNTpksiSWp2nEl1dt3j726HdRhiNgVJQMj7mLp7dprtF57mOB6JvEk/x9d2bsuL5hlqZbBuHQylQ==} + '@smithy/url-parser@2.2.0': dependencies: '@smithy/querystring-parser': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/url-parser@3.0.3: - resolution: {integrity: sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==} + '@smithy/url-parser@3.0.3': dependencies: '@smithy/querystring-parser': 3.0.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-base64@2.3.0: - resolution: {integrity: sha512-s3+eVwNeJuXUwuMbusncZNViuhv2LjVJ1nMwTqSA0XAC7gjKhqqxRdJPhR8+YrkoZ9IiIbFk/yK6ACe/xlF+hw==} - engines: {node: '>=14.0.0'} + '@smithy/util-base64@2.3.0': dependencies: '@smithy/util-buffer-from': 2.2.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/util-base64@3.0.0: - resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-base64@3.0.0': dependencies: '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/util-body-length-browser@2.2.0: - resolution: {integrity: sha512-dtpw9uQP7W+n3vOtx0CfBD5EWd7EPdIdsQnWTDoFf77e3VUf05uA7R7TGipIo8e4WL2kuPdnsr3hMQn9ziYj5w==} + '@smithy/util-body-length-browser@2.2.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/util-body-length-browser@3.0.0: - resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} + '@smithy/util-body-length-browser@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-body-length-node@2.3.0: - resolution: {integrity: sha512-ITWT1Wqjubf2CJthb0BuT9+bpzBfXeMokH/AAa5EJQgbv9aPMVfnM76iFIZVFf50hYXGbtiV71BHAthNWd6+dw==} - engines: {node: '>=14.0.0'} + '@smithy/util-body-length-node@2.3.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/util-body-length-node@3.0.0: - resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} - engines: {node: '>=16.0.0'} + '@smithy/util-body-length-node@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-buffer-from@2.2.0: - resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} - engines: {node: '>=14.0.0'} + '@smithy/util-buffer-from@2.2.0': dependencies: '@smithy/is-array-buffer': 2.2.0 tslib: 2.6.3 - /@smithy/util-buffer-from@3.0.0: - resolution: {integrity: sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==} - engines: {node: '>=16.0.0'} + '@smithy/util-buffer-from@3.0.0': dependencies: '@smithy/is-array-buffer': 3.0.0 tslib: 2.6.3 - /@smithy/util-config-provider@2.3.0: - resolution: {integrity: sha512-HZkzrRcuFN1k70RLqlNK4FnPXKOpkik1+4JaBoHNJn+RnJGYqaa3c5/+XtLOXhlKzlRgNvyaLieHTW2VwGN0VQ==} - engines: {node: '>=14.0.0'} + '@smithy/util-config-provider@2.3.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/util-config-provider@3.0.0: - resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-config-provider@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-defaults-mode-browser@2.2.1: - resolution: {integrity: sha512-RtKW+8j8skk17SYowucwRUjeh4mCtnm5odCL0Lm2NtHQBsYKrNW0od9Rhopu9wF1gHMfHeWF7i90NwBz/U22Kw==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@2.2.1': dependencies: '@smithy/property-provider': 2.2.0 '@smithy/smithy-client': 2.5.1 '@smithy/types': 2.12.0 bowser: 2.11.0 tslib: 2.6.3 - dev: false - /@smithy/util-defaults-mode-browser@3.0.15: - resolution: {integrity: sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@3.0.15': dependencies: '@smithy/property-provider': 3.1.3 '@smithy/smithy-client': 3.2.0 @@ -7732,9 +11181,7 @@ packages: bowser: 2.11.0 tslib: 2.6.3 - /@smithy/util-defaults-mode-browser@3.0.7: - resolution: {integrity: sha512-Q2txLyvQyGfmjsaDbVV7Sg8psefpFcrnlGapDzXGFRPFKRBeEg6OvFK8FljqjeHSaCZ6/UuzQExUPqBR/2qlDA==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@3.0.7': dependencies: '@smithy/property-provider': 3.1.3 '@smithy/smithy-client': 3.2.0 @@ -7742,9 +11189,7 @@ packages: bowser: 2.11.0 tslib: 2.6.3 - /@smithy/util-defaults-mode-node@2.3.1: - resolution: {integrity: sha512-vkMXHQ0BcLFysBMWgSBLSk3+leMpFSyyFj8zQtv5ZyUBx8/owVh1/pPEkzmW/DR/Gy/5c8vjLDD9gZjXNKbrpA==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@2.3.1': dependencies: '@smithy/config-resolver': 2.2.0 '@smithy/credential-provider-imds': 2.3.0 @@ -7753,11 +11198,8 @@ packages: '@smithy/smithy-client': 2.5.1 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/util-defaults-mode-node@3.0.15: - resolution: {integrity: sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@3.0.15': dependencies: '@smithy/config-resolver': 3.0.5 '@smithy/credential-provider-imds': 3.2.0 @@ -7767,9 +11209,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-defaults-mode-node@3.0.7: - resolution: {integrity: sha512-F4Qcj1fG6MGi2BSWCslfsMSwllws/WzYONBGtLybyY+halAcXdWhcew+mej8M5SKd5hqPYp4f7b+ABQEaeytgg==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@3.0.7': dependencies: '@smithy/config-resolver': 3.0.5 '@smithy/credential-provider-imds': 3.1.3 @@ -7779,70 +11219,49 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-endpoints@2.0.4: - resolution: {integrity: sha512-ZAtNf+vXAsgzgRutDDiklU09ZzZiiV/nATyqde4Um4priTmasDH+eLpp3tspL0hS2dEootyFMhu1Y6Y+tzpWBQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-endpoints@2.0.4': dependencies: '@smithy/node-config-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-endpoints@2.0.5: - resolution: {integrity: sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==} - engines: {node: '>=16.0.0'} + '@smithy/util-endpoints@2.0.5': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-hex-encoding@2.2.0: - resolution: {integrity: sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ==} - engines: {node: '>=14.0.0'} + '@smithy/util-hex-encoding@2.2.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/util-hex-encoding@3.0.0: - resolution: {integrity: sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-hex-encoding@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-middleware@2.2.0: - resolution: {integrity: sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw==} - engines: {node: '>=14.0.0'} + '@smithy/util-middleware@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/util-middleware@3.0.3: - resolution: {integrity: sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==} - engines: {node: '>=16.0.0'} + '@smithy/util-middleware@3.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-retry@2.2.0: - resolution: {integrity: sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g==} - engines: {node: '>= 14.0.0'} + '@smithy/util-retry@2.2.0': dependencies: '@smithy/service-error-classification': 2.1.5 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/util-retry@3.0.3: - resolution: {integrity: sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==} - engines: {node: '>=16.0.0'} + '@smithy/util-retry@3.0.3': dependencies: '@smithy/service-error-classification': 3.0.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-stream@2.2.0: - resolution: {integrity: sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA==} - engines: {node: '>=14.0.0'} + '@smithy/util-stream@2.2.0': dependencies: '@smithy/fetch-http-handler': 2.5.0 '@smithy/node-http-handler': 2.5.0 @@ -7852,11 +11271,8 @@ packages: '@smithy/util-hex-encoding': 2.2.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/util-stream@3.0.5: - resolution: {integrity: sha512-xC3L5PKMAT/Bh8fmHNXP9sdQ4+4aKVUU3EEJ2CF/lLk7R+wtMJM+v/1B4en7jO++Wa5spGzFDBCl0QxgbUc5Ug==} - engines: {node: '>=16.0.0'} + '@smithy/util-stream@3.0.5': dependencies: '@smithy/fetch-http-handler': 3.2.4 '@smithy/node-http-handler': 3.1.4 @@ -7867,9 +11283,7 @@ packages: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/util-stream@3.1.3: - resolution: {integrity: sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==} - engines: {node: '>=16.0.0'} + '@smithy/util-stream@3.1.3': dependencies: '@smithy/fetch-http-handler': 3.2.4 '@smithy/node-http-handler': 3.1.4 @@ -7880,312 +11294,205 @@ packages: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/util-uri-escape@2.2.0: - resolution: {integrity: sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==} - engines: {node: '>=14.0.0'} + '@smithy/util-uri-escape@2.2.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/util-uri-escape@3.0.0: - resolution: {integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==} - engines: {node: '>=16.0.0'} + '@smithy/util-uri-escape@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-utf8@2.3.0: - resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} - engines: {node: '>=14.0.0'} + '@smithy/util-utf8@2.3.0': dependencies: '@smithy/util-buffer-from': 2.2.0 tslib: 2.6.3 - /@smithy/util-utf8@3.0.0: - resolution: {integrity: sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==} - engines: {node: '>=16.0.0'} + '@smithy/util-utf8@3.0.0': dependencies: '@smithy/util-buffer-from': 3.0.0 tslib: 2.6.3 - /@smithy/util-waiter@2.2.0: - resolution: {integrity: sha512-IHk53BVw6MPMi2Gsn+hCng8rFA3ZmR3Rk7GllxDUW9qFJl/hiSvskn7XldkECapQVkIg/1dHpMAxI9xSTaLLSA==} - engines: {node: '>=14.0.0'} + '@smithy/util-waiter@2.2.0': dependencies: '@smithy/abort-controller': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/util-waiter@3.1.2: - resolution: {integrity: sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==} - engines: {node: '>=16.0.0'} + '@smithy/util-waiter@3.1.2': dependencies: '@smithy/abort-controller': 3.1.1 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@testcontainers/postgresql@10.9.0: - resolution: {integrity: sha512-Z3K/TFkl/PVE2v8A6yKqgF4pSFk9ilFG02yeGhPswUjmBlcig/rpVOjBQOkQ/yJCcQ/r2RrX3RR+7vr+UO4QlQ==} + '@testcontainers/postgresql@10.9.0': dependencies: testcontainers: 10.9.0 transitivePeerDependencies: - encoding - supports-color - dev: true - /@tootallnate/quickjs-emscripten@0.23.0: - resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + '@tootallnate/quickjs-emscripten@0.23.0': {} - /@tsconfig/node-lts@20.1.3: - resolution: {integrity: sha512-m3b7EP2U+h5tNSpaBMfcTuHmHn04wrgRPQQrfKt75YIPq6kPs2153/KfPHdqkEWGx5pEBvS6rnvToT+yTtC1iw==} - dev: true + '@tsconfig/node-lts@20.1.3': {} - /@tsconfig/node10@1.0.11: - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - dev: true + '@tsconfig/node10@1.0.11': {} - /@tsconfig/node12@1.0.11: - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - dev: true + '@tsconfig/node12@1.0.11': {} - /@tsconfig/node14@1.0.3: - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - dev: true + '@tsconfig/node14@1.0.3': {} - /@tsconfig/node16@1.0.4: - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - dev: true + '@tsconfig/node16@1.0.4': {} - /@tsconfig/strictest@2.0.5: - resolution: {integrity: sha512-ec4tjL2Rr0pkZ5hww65c+EEPYwxOi4Ryv+0MtjeaSQRJyq322Q27eOQiFbuNgw2hpL4hB1/W/HBGk3VKS43osg==} - dev: true + '@tsconfig/strictest@2.0.5': {} - /@types/adm-zip@0.5.5: - resolution: {integrity: sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==} + '@types/adm-zip@0.5.5': dependencies: '@types/node': 20.14.6 - dev: true - /@types/body-parser@1.19.5: - resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 '@types/node': 20.14.6 - /@types/buffers@0.1.31: - resolution: {integrity: sha512-wEZBb3o0Kh5RAj3V172vJCcxaCV8C2HJ7YLBBlG5Mwue0g4uRg5LWv8C6ap8MyFbXE6UbYEuvtHY7oTWAPeXEw==} + '@types/buffers@0.1.31': dependencies: '@types/node': 20.14.6 - dev: false - /@types/connect@3.4.38: - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/connect@3.4.38': dependencies: '@types/node': 20.14.6 - /@types/docker-modem@3.0.6: - resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==} + '@types/docker-modem@3.0.6': dependencies: '@types/node': 20.14.6 '@types/ssh2': 1.15.0 - dev: true - /@types/dockerode@3.3.29: - resolution: {integrity: sha512-5PRRq/yt5OT/Jf77ltIdz4EiR9+VLnPF+HpU4xGFwUqmV24Co2HKBNW3w+slqZ1CYchbcDeqJASHDYWzZCcMiQ==} + '@types/dockerode@3.3.29': dependencies: '@types/docker-modem': 3.0.6 '@types/node': 20.14.6 '@types/ssh2': 1.15.0 - dev: true - /@types/estree@1.0.5: - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: true + '@types/estree@1.0.5': {} - /@types/express-serve-static-core@4.19.5: - resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} + '@types/express-serve-static-core@4.19.5': dependencies: '@types/node': 20.14.6 '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 - /@types/express@4.17.21: - resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + '@types/express@4.17.21': dependencies: '@types/body-parser': 1.19.5 '@types/express-serve-static-core': 4.19.5 '@types/qs': 6.9.15 '@types/serve-static': 1.15.7 - /@types/fs-extra@9.0.13: - resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + '@types/fs-extra@9.0.13': dependencies: '@types/node': 20.14.6 - /@types/html2json@1.0.1: - resolution: {integrity: sha512-D+cq6HcgfMdrbIpXzxsmJ9mBz9VlyagqaIB9OZVHGrOeyWbwjTLWR2qUGh1w/VZLtG4wy8mp7v4EpJQ1dYqWzw==} - dev: true + '@types/html2json@1.0.1': {} - /@types/http-errors@2.0.4: - resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + '@types/http-errors@2.0.4': {} - /@types/json-diff@1.0.3: - resolution: {integrity: sha512-Qvxm8fpRMv/1zZR3sQWImeRK2mBYJji20xF51Fq9Gt//Ed18u0x6/FNLogLS1xhfUWTEmDyqveJqn95ltB6Kvw==} - dev: true + '@types/json-diff@1.0.3': {} - /@types/json-schema@7.0.15: - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - dev: true + '@types/json-schema@7.0.15': {} - /@types/json5@0.0.29: - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - dev: true + '@types/json5@0.0.29': {} - /@types/jsonwebtoken@9.0.6: - resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} + '@types/jsonwebtoken@9.0.6': dependencies: '@types/node': 20.14.6 - /@types/lodash.isequal@4.5.8: - resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} + '@types/lodash.isequal@4.5.8': dependencies: '@types/lodash': 4.17.6 - dev: true - /@types/lodash@4.14.196: - resolution: {integrity: sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==} - dev: true + '@types/lodash@4.14.196': {} - /@types/lodash@4.17.6: - resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} - dev: true + '@types/lodash@4.17.6': {} - /@types/mime@1.3.5: - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/mime@1.3.5': {} - /@types/multer@1.4.11: - resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==} + '@types/multer@1.4.11': dependencies: '@types/express': 4.17.21 - dev: true - /@types/node@18.19.39: - resolution: {integrity: sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==} + '@types/node@18.19.39': dependencies: undici-types: 5.26.5 - dev: true - /@types/node@20.14.6: - resolution: {integrity: sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==} + '@types/node@20.14.6': dependencies: undici-types: 5.26.5 - /@types/node@20.4.9: - resolution: {integrity: sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==} - dev: true + '@types/node@20.4.9': {} - /@types/nodemailer@6.4.15: - resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==} + '@types/nodemailer@6.4.15': dependencies: '@types/node': 20.14.6 - dev: true - /@types/nodemailer@6.4.9: - resolution: {integrity: sha512-XYG8Gv+sHjaOtUpiuytahMy2mM3rectgroNbs6R3djZEKmPNiIJwe9KqOJBGzKKnNZNKvnuvmugBgpq3w/S0ig==} + '@types/nodemailer@6.4.9': dependencies: '@types/node': 20.14.6 - dev: true - /@types/qs@6.9.15: - resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} + '@types/qs@6.9.15': {} - /@types/range-parser@1.2.7: - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/range-parser@1.2.7': {} - /@types/semver@7.5.8: - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - dev: true + '@types/semver@7.5.8': {} - /@types/send@0.17.4: - resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 '@types/node': 20.14.6 - /@types/serve-static@1.15.7: - resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 '@types/node': 20.14.6 '@types/send': 0.17.4 - /@types/sinon@10.0.20: - resolution: {integrity: sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==} + '@types/sinon@10.0.20': dependencies: '@types/sinonjs__fake-timers': 8.1.5 - dev: true - /@types/sinonjs__fake-timers@8.1.5: - resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} - dev: true + '@types/sinonjs__fake-timers@8.1.5': {} - /@types/ssh2-sftp-client@9.0.4: - resolution: {integrity: sha512-gnIn56MTB9W3A3hPL/1sHI23t8YwcE3eVYa1O2XjT9vaqimFdtNHxyQiy5Y78+ociQTKazMSD8YyMEO4QjNMrg==} + '@types/ssh2-sftp-client@9.0.4': dependencies: '@types/ssh2': 1.15.0 - dev: true - /@types/ssh2-streams@0.1.12: - resolution: {integrity: sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==} + '@types/ssh2-streams@0.1.12': dependencies: '@types/node': 20.14.6 - dev: true - /@types/ssh2@0.5.52: - resolution: {integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==} + '@types/ssh2@0.5.52': dependencies: '@types/node': 20.14.6 '@types/ssh2-streams': 0.1.12 - dev: true - /@types/ssh2@1.15.0: - resolution: {integrity: sha512-YcT8jP5F8NzWeevWvcyrrLB3zcneVjzYY9ZDSMAMboI+2zR1qYWFhwsyOFVzT7Jorn67vqxC0FRiw8YyG9P1ww==} + '@types/ssh2@1.15.0': dependencies: '@types/node': 18.19.39 - dev: true - /@types/triple-beam@1.3.5: - resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} - dev: false + '@types/triple-beam@1.3.5': {} - /@types/webidl-conversions@7.0.3: - resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} - dev: false + '@types/webidl-conversions@7.0.3': {} - /@types/whatwg-url@11.0.5: - resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + '@types/whatwg-url@11.0.5': dependencies: '@types/webidl-conversions': 7.0.3 - dev: false - /@types/yauzl@2.10.3: - resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - requiresBuild: true + '@types/yauzl@2.10.3': dependencies: '@types/node': 20.14.6 optional: true - /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.11.0 '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) @@ -8199,72 +11506,43 @@ packages: natural-compare-lite: 1.4.0 semver: 7.6.2 tsutils: 3.21.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) debug: 4.3.5 eslint: 8.57.0 + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/scope-manager@5.62.0: - resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/scope-manager@5.62.0': dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - dev: true - /@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5) debug: 4.3.5 eslint: 8.57.0 tsutils: 3.21.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/types@5.62.0: - resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true + '@typescript-eslint/types@5.62.0': {} - /@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.5): - resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.5)': dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 @@ -8273,16 +11551,12 @@ packages: is-glob: 4.0.3 semver: 7.6.2 tsutils: 3.21.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + '@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.5)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 @@ -8296,199 +11570,120 @@ packages: transitivePeerDependencies: - supports-color - typescript - dev: true - /@typescript-eslint/visitor-keys@5.62.0: - resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/visitor-keys@5.62.0': dependencies: '@typescript-eslint/types': 5.62.0 eslint-visitor-keys: 3.4.3 - dev: true - /@ungap/structured-clone@1.2.0: - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - dev: true + '@ungap/structured-clone@1.2.0': {} - /@vitest/expect@1.6.0: - resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} + '@vitest/expect@1.6.0': dependencies: '@vitest/spy': 1.6.0 '@vitest/utils': 1.6.0 chai: 4.4.1 - dev: true - /@vitest/runner@1.6.0: - resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} + '@vitest/runner@1.6.0': dependencies: '@vitest/utils': 1.6.0 p-limit: 5.0.0 pathe: 1.1.2 - dev: true - /@vitest/snapshot@1.6.0: - resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} + '@vitest/snapshot@1.6.0': dependencies: magic-string: 0.30.10 pathe: 1.1.2 pretty-format: 29.7.0 - dev: true - /@vitest/spy@1.6.0: - resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} + '@vitest/spy@1.6.0': dependencies: tinyspy: 2.2.1 - dev: true - /@vitest/utils@1.6.0: - resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} + '@vitest/utils@1.6.0': dependencies: diff-sequences: 29.6.3 estree-walker: 3.0.3 loupe: 2.3.7 pretty-format: 29.7.0 - dev: true - /@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8): - resolution: {integrity: sha512-aH4rOdb3AcezN7ws8vDgBfGboZMk2JGGzEq/DtW65MhnRxyTGRuLJRWVQ/2KxDgWvV2F5oTkAS+5pnjKbl0n+A==} - peerDependencies: - axios: ^0.x || ^1.0.0 - zod: ^3.x + '@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8)': dependencies: axios: 1.7.4 zod: 3.23.8 - /@zodios/express@10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8): - resolution: {integrity: sha512-FNgOq8mvwvWP5B2howMKGm6EPp6i/0XFAsQnX5Ov3MLbanzD1oE4WJtBkTL3cmJYvD0nyykbWSeHOh51bCmhUA==} - peerDependencies: - '@zodios/core': '>=10.4.4 <11.0.0' - express: 4.x - zod: ^3.x + '@zodios/express@10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8)': dependencies: '@zodios/core': 10.9.6(axios@1.7.4)(zod@3.23.8) express: 4.20.0 zod: 3.23.8 - dev: false - /accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} + accepts@1.3.8: dependencies: mime-types: 2.1.35 negotiator: 0.6.3 - dev: false - /acorn-jsx@2.0.1: - resolution: {integrity: sha512-rbNtu2WkMJAZNnw2rh35whZO2e2N8Q1Dp4PBf/pKJAals6uFbPvVgVcKZ8poUnrkF50thOea1ApmF8W56apnwA==} + acorn-jsx@2.0.1: dependencies: acorn: 2.7.0 - dev: true - /acorn-jsx@5.3.2(acorn@8.12.1): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-jsx@5.3.2(acorn@8.12.1): dependencies: acorn: 8.12.1 - dev: true - /acorn-walk@8.3.3: - resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} - engines: {node: '>=0.4.0'} + acorn-walk@8.3.3: dependencies: acorn: 8.12.1 - dev: true - /acorn@2.7.0: - resolution: {integrity: sha512-pXK8ez/pVjqFdAgBkF1YPVRacuLQ9EXBKaKWaeh58WNfMkCmZhOZzu+NtKSPD5PHmCCHheQ5cD29qM1K4QTxIg==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true + acorn@2.7.0: {} - /acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true + acorn@8.12.1: {} - /adm-zip@0.5.15: - resolution: {integrity: sha512-jYPWSeOA8EFoZnucrKCNihqBjoEGQSU4HKgHYQgKNEQ0pQF9a/DYuo/+fAxY76k4qe75LUlLWpAM1QWcBMTOKw==} - engines: {node: '>=12.0'} - dev: false + adm-zip@0.5.15: {} - /agent-base@7.1.1: - resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} - engines: {node: '>= 14'} + agent-base@7.1.1: dependencies: debug: 4.3.5 transitivePeerDependencies: - supports-color - /ajv-draft-04@1.0.0(ajv@8.16.0): - resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} - peerDependencies: - ajv: ^8.5.0 - peerDependenciesMeta: - ajv: - optional: true - dependencies: + ajv-draft-04@1.0.0(ajv@8.16.0): + optionalDependencies: ajv: 8.16.0 - /ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 - dev: true - /ajv@8.16.0: - resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + ajv@8.16.0: dependencies: fast-deep-equal: 3.1.3 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 uri-js: 4.4.1 - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} + ansi-regex@5.0.1: {} - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - dev: true + ansi-regex@6.0.1: {} - /ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - /ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - dev: true + ansi-styles@5.2.0: {} - /ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - dev: true + ansi-styles@6.2.1: {} - /append-field@1.0.0: - resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} - dev: false + append-field@1.0.0: {} - /archiver-utils@2.1.0: - resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} - engines: {node: '>= 6'} + archiver-utils@2.1.0: dependencies: glob: 7.2.3 graceful-fs: 4.2.11 @@ -8500,11 +11695,8 @@ packages: lodash.union: 4.6.0 normalize-path: 3.0.0 readable-stream: 2.3.8 - dev: true - /archiver-utils@3.0.4: - resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} - engines: {node: '>= 10'} + archiver-utils@3.0.4: dependencies: glob: 7.2.3 graceful-fs: 4.2.11 @@ -8516,11 +11708,8 @@ packages: lodash.union: 4.6.0 normalize-path: 3.0.0 readable-stream: 3.6.2 - dev: true - /archiver@5.3.2: - resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} - engines: {node: '>= 10'} + archiver@5.3.2: dependencies: archiver-utils: 2.1.0 async: 3.2.5 @@ -8529,35 +11718,23 @@ packages: readdir-glob: 1.1.3 tar-stream: 2.2.0 zip-stream: 4.1.1 - dev: true - /arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true + arg@4.1.3: {} - /argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + argparse@2.0.1: {} - /array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.1: dependencies: call-bind: 1.0.7 is-array-buffer: 3.0.4 - dev: true - /array-flatten@1.1.1: - resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - dev: false + array-flatten@1.1.1: {} - /array-includes@3.1.8: - resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} - engines: {node: '>= 0.4'} + array-includes@3.1.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -8565,16 +11742,10 @@ packages: es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 is-string: 1.0.7 - dev: true - /array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - dev: true + array-union@2.1.0: {} - /array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} - engines: {node: '>= 0.4'} + array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -8582,11 +11753,8 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.findlastindex@1.2.5: - resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} - engines: {node: '>= 0.4'} + array.prototype.findlastindex@1.2.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -8594,51 +11762,37 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} + array.prototype.flat@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} + array.prototype.flatmap@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.toreversed@1.1.2: - resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} + array.prototype.toreversed@1.1.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.tosorted@1.1.4: - resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} - engines: {node: '>= 0.4'} + array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-errors: 1.3.0 es-shim-unscopables: 1.0.2 - dev: true - /arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} + arraybuffer.prototype.slice@1.0.3: dependencies: array-buffer-byte-length: 1.0.1 call-bind: 1.0.7 @@ -8648,76 +11802,53 @@ packages: get-intrinsic: 1.2.4 is-array-buffer: 3.0.4 is-shared-array-buffer: 1.0.3 - dev: true - /asn1@0.2.6: - resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + asn1@0.2.6: dependencies: safer-buffer: 2.1.2 - /assert-options@0.8.1: - resolution: {integrity: sha512-5lNGRB5g5i2bGIzb+J1QQE1iKU/WEMVBReFIc5pPDWjcPj23otPL0eI6PB2v7QPi0qU6Mhym5D3y0ZiSIOf3GA==} - engines: {node: '>=10.0.0'} + assert-options@0.8.1: {} - /assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - dev: true + assertion-error@1.1.0: {} - /ast-types@0.13.4: - resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} - engines: {node: '>=4'} + ast-types@0.13.4: dependencies: tslib: 2.6.3 - /async-lock@1.4.1: - resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} - dev: true + async-lock@1.4.1: {} - /async@3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + async@3.2.5: {} - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + asynckit@0.4.0: {} - /available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 - dev: true - /aws-msk-iam-sasl-signer-js@1.0.0(@aws-sdk/client-sso-oidc@3.645.0): - resolution: {integrity: sha512-L0Jk0k2XNHMSGipJ8rRdTq51KrH/gwrfZ39iKY9BWHGOAv7EygsG4qJC7lIRsbu5/ZHB886Z3WsOsFxqR2R4XQ==} - engines: {node: '>=14.x'} + aws-msk-iam-sasl-signer-js@1.0.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)): dependencies: '@aws-crypto/sha256-js': 4.0.0 '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/credential-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/credential-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/util-format-url': 3.609.0 '@smithy/signature-v4': 2.3.0 '@types/buffers': 0.1.31 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - /aws-sdk-client-mock@4.0.1: - resolution: {integrity: sha512-yD2mmgy73Xce097G5hIpr1k7j50qzvJ49/+6osGZiCyk4m6cwhb+2x7kKFY1gEMwTzaS8+m8fXv9SB29SkRYyQ==} + aws-sdk-client-mock@4.0.1: dependencies: '@types/sinon': 10.0.20 sinon: 16.1.3 tslib: 2.6.3 - dev: true - /axios-logger@2.8.1: - resolution: {integrity: sha512-Bbl7XRR/Rkxg2Owv/kRgAZ/0qf8kMPLc08LtiUcGCWV5RmoI7vHr+eee6SUc8jRi2nE5KWShziCVh35C1SBEaw==} + axios-logger@2.8.1: dependencies: chalk: 4.1.2 dateformat: 3.0.3 - dev: false - /axios@1.7.4: - resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==} + axios@1.7.4: dependencies: follow-redirects: 1.15.6 form-data: 4.0.0 @@ -8725,69 +11856,48 @@ packages: transitivePeerDependencies: - debug - /b4a@1.6.6: - resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + b4a@1.6.6: {} - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true + balanced-match@1.0.2: {} - /bare-events@2.4.2: - resolution: {integrity: sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==} - requiresBuild: true + bare-events@2.4.2: optional: true - /bare-fs@2.3.1: - resolution: {integrity: sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==} - requiresBuild: true + bare-fs@2.3.1: dependencies: bare-events: 2.4.2 bare-path: 2.1.3 bare-stream: 2.1.3 optional: true - /bare-os@2.4.0: - resolution: {integrity: sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==} - requiresBuild: true + bare-os@2.4.0: optional: true - /bare-path@2.1.3: - resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==} - requiresBuild: true + bare-path@2.1.3: dependencies: bare-os: 2.4.0 optional: true - /bare-stream@2.1.3: - resolution: {integrity: sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==} - requiresBuild: true + bare-stream@2.1.3: dependencies: streamx: 2.18.0 optional: true - /base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + base64-js@1.5.1: {} - /basic-ftp@5.0.5: - resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} - engines: {node: '>=10.0.0'} + basic-ftp@5.0.5: {} - /bcrypt-pbkdf@1.0.2: - resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + bcrypt-pbkdf@1.0.2: dependencies: tweetnacl: 0.14.5 - /bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bl@4.1.0: dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true - /body-parser@1.20.3: - resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + body-parser@1.20.3: dependencies: bytes: 3.1.2 content-type: 1.0.5 @@ -8803,92 +11913,56 @@ packages: unpipe: 1.0.0 transitivePeerDependencies: - supports-color - dev: false - /bowser@2.11.0: - resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + bowser@2.11.0: {} - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - dev: true - /brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 - dev: true - /braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} + braces@3.0.3: dependencies: fill-range: 7.1.1 - dev: true - /browserslist@4.23.1: - resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true + browserslist@4.23.1: dependencies: caniuse-lite: 1.0.30001640 electron-to-chromium: 1.4.818 node-releases: 2.0.14 update-browserslist-db: 1.1.0(browserslist@4.23.1) - /bson@6.8.0: - resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} - engines: {node: '>=16.20.1'} - dev: false + bson@6.8.0: {} - /buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-crc32@0.2.13: {} - /buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer-equal-constant-time@1.0.1: {} - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: false + buffer-from@1.1.2: {} - /buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - /buildcheck@0.0.6: - resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==} - engines: {node: '>=10.0.0'} - requiresBuild: true + buildcheck@0.0.6: optional: true - /busboy@1.6.0: - resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} - engines: {node: '>=10.16.0'} + busboy@1.6.0: dependencies: streamsearch: 1.1.0 - dev: false - /byline@5.0.0: - resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} - engines: {node: '>=0.10.0'} - dev: true + byline@5.0.0: {} - /bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - dev: false + bytes@3.1.2: {} - /cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} + cac@6.7.14: {} - /call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} + call-bind@1.0.7: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 @@ -8896,19 +11970,13 @@ packages: get-intrinsic: 1.2.4 set-function-length: 1.2.2 - /call-me-maybe@1.0.2: - resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + call-me-maybe@1.0.2: {} - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} + callsites@3.1.0: {} - /caniuse-lite@1.0.30001640: - resolution: {integrity: sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==} + caniuse-lite@1.0.30001640: {} - /chai@4.4.1: - resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} - engines: {node: '>=4'} + chai@4.4.1: dependencies: assertion-error: 1.1.0 check-error: 1.0.3 @@ -8917,212 +11985,133 @@ packages: loupe: 2.3.7 pathval: 1.1.1 type-detect: 4.0.8 - dev: true - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - /check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + check-error@1.0.3: dependencies: get-func-name: 2.0.2 - dev: true - /chownr@1.1.4: - resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - dev: true + chownr@1.1.4: {} - /chromium-bidi@0.5.23(devtools-protocol@0.0.1299070): - resolution: {integrity: sha512-1o/gLU9wDqbN5nL2MtfjykjOuighGXc3/hnWueO1haiEoFgX8h5vbvcA4tgdQfjw1mkZ1OEF4x/+HVeqEX6NoA==} - peerDependencies: - devtools-protocol: '*' + chromium-bidi@0.5.23(devtools-protocol@0.0.1299070): dependencies: devtools-protocol: 0.0.1299070 mitt: 3.0.1 urlpattern-polyfill: 10.0.0 zod: 3.23.8 - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - /cluster-key-slot@1.1.2: - resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} - engines: {node: '>=0.10.0'} - dev: false + cluster-key-slot@1.1.2: {} - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + color-convert@1.9.3: dependencies: color-name: 1.1.3 - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + color-convert@2.0.1: dependencies: color-name: 1.1.4 - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.3: {} - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@1.1.4: {} - /color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + color-string@1.9.1: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - dev: false - /color@3.2.1: - resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + color@3.2.1: dependencies: color-convert: 1.9.3 color-string: 1.9.1 - dev: false - /colors@1.4.0: - resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} - engines: {node: '>=0.1.90'} - dev: false + colors@1.4.0: {} - /colorspace@1.1.4: - resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + colorspace@1.1.4: dependencies: color: 3.2.1 text-hex: 1.0.0 - dev: false - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - /comment-parser@1.3.1: - resolution: {integrity: sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==} - engines: {node: '>= 12.0.0'} - dev: true + comment-parser@1.3.1: {} - /compress-commons@4.1.2: - resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} - engines: {node: '>= 10'} + compress-commons@4.1.2: dependencies: buffer-crc32: 0.2.13 crc32-stream: 4.0.3 normalize-path: 3.0.0 readable-stream: 3.6.2 - dev: true - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: true + concat-map@0.0.1: {} - /concat-stream@1.6.2: - resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} - engines: {'0': node >= 0.8} + concat-stream@1.6.2: dependencies: buffer-from: 1.1.2 inherits: 2.0.4 readable-stream: 2.3.8 typedarray: 0.0.6 - dev: false - /concat-stream@2.0.0: - resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} - engines: {'0': node >= 6.0} + concat-stream@2.0.0: dependencies: buffer-from: 1.1.2 inherits: 2.0.4 readable-stream: 3.6.2 typedarray: 0.0.6 - dev: false - /confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - dev: true + confbox@0.1.7: {} - /connection-string@4.4.0: - resolution: {integrity: sha512-D4xsUjSoE8m/B5yMOvCIHY+2ME6FIZhCq0NzBBT57Q8BuL7ArFhBK04osOfReoW4KFr5ztzFwWRdmnv9rCvu2w==} - engines: {node: '>=14'} - dev: false + connection-string@4.4.0: {} - /console-assert@1.0.0: - resolution: {integrity: sha512-YtowQtZLdzPUlXL+kxMEBclXVOrWzR/+9TAUbIdgnjCkRW8+Dj0y4ajMJtOoQFXEubMONX0fkFS3SNLxx4FQRA==} - dev: true + console-assert@1.0.0: {} - /content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 - dev: false - /content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - dev: false + content-type@1.0.5: {} - /convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + convert-source-map@2.0.0: {} - /cookie-signature@1.0.6: - resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - dev: false + cookie-signature@1.0.6: {} - /cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} - dev: false + cookie@0.6.0: {} - /core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + core-util-is@1.0.3: {} - /cosmiconfig@9.0.0(typescript@5.4.5): - resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true + cosmiconfig@9.0.0(typescript@5.4.5): dependencies: env-paths: 2.2.1 import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 + optionalDependencies: typescript: 5.4.5 - /cpu-features@0.0.10: - resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==} - engines: {node: '>=10.0.0'} - requiresBuild: true + cpu-features@0.0.10: dependencies: buildcheck: 0.0.6 nan: 2.20.0 optional: true - /cpx2@7.0.1: - resolution: {integrity: sha512-ZgK/DRvPFM5ATZ5DQ5UzY6ajkBrI/p9Uc7VyLHc7b4OSFeBO4yOQz/GEmccc4Om6capGYlY4K1XX+BtYQiPPIA==} - engines: {node: '>=18', npm: '>=10'} - hasBin: true + cpx2@7.0.1: dependencies: debounce: 2.1.0 debug: 4.3.5 @@ -9139,253 +12128,136 @@ packages: subarg: 1.0.0 transitivePeerDependencies: - supports-color - dev: true - /crc-32@1.2.2: - resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} - engines: {node: '>=0.8'} - hasBin: true - dev: true + crc-32@1.2.2: {} - /crc32-stream@4.0.3: - resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} - engines: {node: '>= 10'} + crc32-stream@4.0.3: dependencies: crc-32: 1.2.2 readable-stream: 3.6.2 - dev: true - /create-eslint-index@1.0.0: - resolution: {integrity: sha512-nXvJjnfDytOOaPOonX0h0a1ggMoqrhdekGeZkD6hkcWYvlCWhU719tKFVh8eU04CnMwu3uwe1JjwuUF2C3k2qg==} - engines: {node: '>=4.0.0'} + create-eslint-index@1.0.0: dependencies: lodash.get: 4.4.2 - dev: true - /create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true + create-require@1.1.1: {} - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true - /csv-generate@4.4.1: - resolution: {integrity: sha512-O/einO0v4zPmXaOV+sYqGa02VkST4GP5GLpWBNHEouIU7pF3kpGf3D0kCCvX82ydIY4EKkOK+R8b1BYsRXravg==} - dev: false + csv-generate@4.4.1: {} - /csv-parse@5.5.6: - resolution: {integrity: sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A==} - dev: false + csv-parse@5.5.6: {} - /csv-stringify@6.5.1: - resolution: {integrity: sha512-+9lpZfwpLntpTIEpFbwQyWuW/hmI/eHuJZD1XzeZpfZTqkf1fyvBbBLXTJJMsBuuS11uTShMqPwzx4A6ffXgRQ==} - dev: false + csv-stringify@6.5.1: {} - /csv@6.3.2: - resolution: {integrity: sha512-fOm1LBmt4/kjC1RFanNtjSFVjvoh6MS5E/CuQrED5gCfvjHESZD97Fbjfz/W8ZN4wQAxFjzOonATE790UIuLTg==} - engines: {node: '>= 0.1.90'} + csv@6.3.2: dependencies: csv-generate: 4.4.1 csv-parse: 5.5.6 csv-stringify: 6.5.1 stream-transform: 3.3.2 - dev: false - /data-uri-to-buffer@6.0.2: - resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} - engines: {node: '>= 14'} + data-uri-to-buffer@6.0.2: {} - /data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} - engines: {node: '>= 0.4'} + data-view-buffer@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - dev: true - /data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} - engines: {node: '>= 0.4'} + data-view-byte-length@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - dev: true - /data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} - engines: {node: '>= 0.4'} + data-view-byte-offset@1.0.0: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - dev: true - /date-fns-tz@3.1.3(date-fns@3.6.0): - resolution: {integrity: sha512-ZfbMu+nbzW0mEzC8VZrLiSWvUIaI3aRHeq33mTe7Y38UctKukgqPR4nTDwcwS4d64Gf8GghnVsroBuMY3eiTeA==} - peerDependencies: - date-fns: ^3.0.0 + date-fns-tz@3.1.3(date-fns@3.6.0): dependencies: date-fns: 3.6.0 - dev: false - /date-fns@3.6.0: - resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + date-fns@3.6.0: {} - /dateformat@3.0.3: - resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} - dev: false + dateformat@3.0.3: {} - /debounce@2.1.0: - resolution: {integrity: sha512-OkL3+0pPWCqoBc/nhO9u6TIQNTK44fnBnzuVtJAbp13Naxw9R6u21x+8tVTka87AhDZ3htqZ2pSSsZl9fqL2Wg==} - engines: {node: '>=18'} - dev: true + debounce@2.1.0: {} - /debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@2.6.9: dependencies: ms: 2.0.0 - dev: false - /debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@3.2.7: dependencies: ms: 2.1.3 - dev: true - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.4: dependencies: ms: 2.1.2 - /debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.5: dependencies: ms: 2.1.2 - /deep-eql@4.1.4: - resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} - engines: {node: '>=6'} + deep-eql@4.1.4: dependencies: type-detect: 4.0.8 - dev: true - /deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - dev: true + deep-is@0.1.4: {} - /deepmerge-ts@4.3.0: - resolution: {integrity: sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw==} - engines: {node: '>=12.4.0'} - dev: true + deepmerge-ts@4.3.0: {} - /define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} + define-data-property@1.1.4: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 gopd: 1.0.1 - /define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} + define-properties@1.2.1: dependencies: define-data-property: 1.1.4 has-property-descriptors: 1.0.2 object-keys: 1.1.1 - dev: true - /degenerator@5.0.1: - resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} - engines: {node: '>= 14'} + degenerator@5.0.1: dependencies: ast-types: 0.13.4 escodegen: 2.1.0 esprima: 4.0.1 - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} + delayed-stream@1.0.0: {} - /depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - dev: false + depd@2.0.0: {} - /depseek@0.4.1: - resolution: {integrity: sha512-YYfPPajzH9s2qnEva411VJzCMWtArBTfluI9USiKQ+T6xBWFh3C7yPxhaa1KVgJa17v9aRKc+LcRhgxS5/9mOA==} - dev: true + depseek@0.4.1: {} - /destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dev: false + destroy@1.2.0: {} - /devtools-protocol@0.0.1299070: - resolution: {integrity: sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==} + devtools-protocol@0.0.1299070: {} - /diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true + diff-sequences@29.6.3: {} - /diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: true + diff@4.0.2: {} - /diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} - engines: {node: '>=0.3.1'} - dev: true + diff@5.2.0: {} - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 - dev: true - /docker-compose@0.24.8: - resolution: {integrity: sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==} - engines: {node: '>= 6.0.0'} + docker-compose@0.24.8: dependencies: yaml: 2.4.5 - dev: true - /docker-modem@3.0.8: - resolution: {integrity: sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==} - engines: {node: '>= 8.0'} + docker-modem@3.0.8: dependencies: debug: 4.3.5 readable-stream: 3.6.2 @@ -9393,117 +12265,70 @@ packages: ssh2: 1.15.0 transitivePeerDependencies: - supports-color - dev: true - /dockerode@3.3.5: - resolution: {integrity: sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==} - engines: {node: '>= 8.0'} + dockerode@3.3.5: dependencies: '@balena/dockerignore': 1.0.2 docker-modem: 3.0.8 tar-fs: 2.0.1 transitivePeerDependencies: - supports-color - dev: true - /doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} + doctrine@2.1.0: dependencies: esutils: 2.0.3 - dev: true - /doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} + doctrine@3.0.0: dependencies: esutils: 2.0.3 - dev: true - /dotenv-flow@4.1.0: - resolution: {integrity: sha512-0cwP9jpQBQfyHwvE0cRhraZMkdV45TQedA8AAUZMsFzvmLcQyc1HPv+oX0OOYwLFjIlvgVepQ+WuQHbqDaHJZg==} - engines: {node: '>= 12.0.0'} + dotenv-flow@4.1.0: dependencies: dotenv: 16.4.5 - /dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} + dotenv@16.4.5: {} - /drange@1.1.1: - resolution: {integrity: sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==} - engines: {node: '>=4'} - dev: true + drange@1.1.1: {} - /dreamopt@0.8.0: - resolution: {integrity: sha512-vyJTp8+mC+G+5dfgsY+r3ckxlz+QMX40VjPQsZc5gxVAxLmi64TBoVkP54A/pRAXMXsbu2GMMBrZPxNv23waMg==} - engines: {node: '>=0.4.0'} + dreamopt@0.8.0: dependencies: wordwrap: 1.0.0 - dev: false - /duplexer@0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - dev: true + duplexer@0.1.2: {} - /eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true + eastasianwidth@0.2.0: {} - /ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer: 5.2.1 - /ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - dev: false + ee-first@1.1.1: {} - /electron-to-chromium@1.4.818: - resolution: {integrity: sha512-eGvIk2V0dGImV9gWLq8fDfTTsCAeMDwZqEPMr+jMInxZdnp9Us8UpovYpRCf9NQ7VOFgrN2doNSgvISbsbNpxA==} + electron-to-chromium@1.4.818: {} - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@8.0.0: {} - /emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - dev: true + emoji-regex@9.2.2: {} - /enabled@2.0.0: - resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - dev: false + enabled@2.0.0: {} - /encodeurl@1.0.2: - resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} - engines: {node: '>= 0.8'} - dev: false + encodeurl@1.0.2: {} - /encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - dev: false + encodeurl@2.0.0: {} - /end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + end-of-stream@1.4.4: dependencies: once: 1.4.0 - /env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} + env-paths@2.2.1: {} - /err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - dev: false + err-code@2.0.3: {} - /error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 - /es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} - engines: {node: '>= 0.4'} + es-abstract@1.23.3: dependencies: array-buffer-byte-length: 1.0.1 arraybuffer.prototype.slice: 1.0.3 @@ -9551,21 +12376,14 @@ packages: typed-array-length: 1.0.6 unbox-primitive: 1.0.2 which-typed-array: 1.1.15 - dev: true - /es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} + es-define-property@1.0.0: dependencies: get-intrinsic: 1.2.4 - /es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} + es-errors@1.3.0: {} - /es-iterator-helpers@1.0.19: - resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} - engines: {node: '>= 0.4'} + es-iterator-helpers@1.0.19: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -9581,44 +12399,28 @@ packages: internal-slot: 1.0.7 iterator.prototype: 1.1.2 safe-array-concat: 1.1.2 - dev: true - /es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} + es-object-atoms@1.0.0: dependencies: es-errors: 1.3.0 - dev: true - /es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} - engines: {node: '>= 0.4'} + es-set-tostringtag@2.0.3: dependencies: get-intrinsic: 1.2.4 has-tostringtag: 1.0.2 hasown: 2.0.2 - dev: true - /es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + es-shim-unscopables@1.0.2: dependencies: hasown: 2.0.2 - dev: true - /es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} + es-to-primitive@1.2.1: dependencies: is-callable: 1.2.7 is-date-object: 1.0.5 is-symbol: 1.0.4 - dev: true - /esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 '@esbuild/android-arm': 0.21.5 @@ -9643,13 +12445,8 @@ packages: '@esbuild/win32-arm64': 0.21.5 '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - dev: true - /esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} - engines: {node: '>=18'} - hasBin: true - requiresBuild: true + esbuild@0.23.1: optionalDependencies: '@esbuild/aix-ppc64': 0.23.1 '@esbuild/android-arm': 0.23.1 @@ -9675,29 +12472,16 @@ packages: '@esbuild/win32-arm64': 0.23.1 '@esbuild/win32-ia32': 0.23.1 '@esbuild/win32-x64': 0.23.1 - dev: true - /escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} + escalade@3.1.2: {} - /escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - dev: false + escape-html@1.0.3: {} - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} + escape-string-regexp@1.0.5: {} - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: true + escape-string-regexp@4.0.0: {} - /escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true + escodegen@2.1.0: dependencies: esprima: 4.0.1 estraverse: 5.3.0 @@ -9705,118 +12489,62 @@ packages: optionalDependencies: source-map: 0.6.1 - /eslint-ast-utils@1.1.0: - resolution: {integrity: sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA==} - engines: {node: '>=4'} + eslint-ast-utils@1.1.0: dependencies: lodash.get: 4.4.2 lodash.zip: 4.2.0 - dev: true - /eslint-config-prettier@8.10.0(eslint@8.57.0): - resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' + eslint-config-prettier@8.10.0(eslint@8.57.0): dependencies: eslint: 8.57.0 - dev: true - /eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 is-core-module: 2.14.0 resolve: 1.22.8 transitivePeerDependencies: - supports-color - dev: true - /eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): - resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true + eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - dev: true - /eslint-plugin-extra-rules@0.0.0-development: - resolution: {integrity: sha512-Lib5tzYuLE8IneAYm8LY5oFhAaQ40IgO8BemKZGBpmZgQwgG7zzKLHs+pvUcgn5cjdoPdbZMcr2vTYmuss2l/g==} - engines: {node: '> 0.10.*'} + eslint-plugin-extra-rules@0.0.0-development: dependencies: console-assert: 1.0.0 espree: 3.0.0-alpha-1 quote: 0.4.0 - dev: true - /eslint-plugin-fp@2.3.0(eslint@8.57.0): - resolution: {integrity: sha512-3n2oHibwoIxAht9/+ZaTldhI6brXORgl8oNXqZd+d9xuAQt2SBJ2/aml0oQRMWvXrgsz2WG6wfC++NjzSG3prA==} - engines: {node: '>=4.0.0'} - peerDependencies: - eslint: '>=3' + eslint-plugin-fp@2.3.0(eslint@8.57.0): dependencies: create-eslint-index: 1.0.0 eslint: 8.57.0 eslint-ast-utils: 1.1.0 lodash: 4.17.21 req-all: 0.1.0 - dev: true - /eslint-plugin-functional@4.4.1(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-YhSfHS52Si62Sn126g9wGx+XnWMoWhwEt6ctVXfcJj+xMUiggjOqUVMca7fuLNzX8jYiNBIeU1Y0teHGePZ3NA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^8.0.0 - tsutils: ^3.0.0 - typescript: ^3.4.1 || ^4.0.0 - peerDependenciesMeta: - tsutils: - optional: true - typescript: - optional: true + eslint-plugin-functional@4.4.1(eslint@8.57.0)(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5): dependencies: '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5) deepmerge-ts: 4.3.0 escape-string-regexp: 4.0.0 eslint: 8.57.0 semver: 7.6.2 + optionalDependencies: + tsutils: 3.21.0(typescript@5.4.5) typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0)(eslint@8.57.0): - resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true + eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0): dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 @@ -9825,7 +12553,7 @@ packages: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.14.0 is-glob: 4.0.3 @@ -9835,17 +12563,14 @@ packages: object.values: 1.2.0 semver: 6.3.1 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - dev: true - /eslint-plugin-jsdoc@39.9.1(eslint@8.57.0): - resolution: {integrity: sha512-Rq2QY6BZP2meNIs48aZ3GlIlJgBqFCmR55+UBvaDkA3ZNQ0SvQXOs2QKkubakEijV8UbIVbVZKsOVN8G3MuqZw==} - engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint-plugin-jsdoc@39.9.1(eslint@8.57.0): dependencies: '@es-joy/jsdoccomment': 0.36.1 comment-parser: 1.3.1 @@ -9857,38 +12582,20 @@ packages: spdx-expression-parse: 3.0.1 transitivePeerDependencies: - supports-color - dev: true - /eslint-plugin-prefer-arrow@1.2.3(eslint@8.57.0): - resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==} - peerDependencies: - eslint: '>=2.0.0' + eslint-plugin-prefer-arrow@1.2.3(eslint@8.57.0): dependencies: eslint: 8.57.0 - dev: true - - /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0)(eslint@8.57.0)(prettier@2.8.8): - resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - eslint: '>=7.28.0' - eslint-config-prettier: '*' - prettier: '>=2.0.0' - peerDependenciesMeta: - eslint-config-prettier: - optional: true + + eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8): dependencies: eslint: 8.57.0 - eslint-config-prettier: 8.10.0(eslint@8.57.0) prettier: 2.8.8 prettier-linter-helpers: 1.0.0 - dev: true + optionalDependencies: + eslint-config-prettier: 8.10.0(eslint@8.57.0) - /eslint-plugin-react@7.34.3(eslint@8.57.0): - resolution: {integrity: sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + eslint-plugin-react@7.34.3(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -9909,43 +12616,24 @@ packages: resolve: 2.0.0-next.5 semver: 6.3.1 string.prototype.matchall: 4.0.11 - dev: true - /eslint-plugin-sonarjs@0.13.0(eslint@8.57.0): - resolution: {integrity: sha512-t3m7ta0EspzDxSOZh3cEOJIJVZgN/TlJYaBGnQlK6W/PZNbWep8q4RQskkJkA7/zwNpX0BaoEOSUUrqaADVoqA==} - engines: {node: '>=12'} - peerDependencies: - eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint-plugin-sonarjs@0.13.0(eslint@8.57.0): dependencies: eslint: 8.57.0 - dev: true - /eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} + eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 estraverse: 4.3.0 - dev: true - /eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@7.2.2: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 - dev: true - /eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true + eslint-visitor-keys@3.4.3: {} - /eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true + eslint@8.57.0: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/regexpp': 4.11.0 @@ -9987,75 +12675,43 @@ packages: text-table: 0.2.0 transitivePeerDependencies: - supports-color - dev: true - /espree@3.0.0-alpha-1: - resolution: {integrity: sha512-HIv6P6qCt3ciLWri1KrO7EPigKPetBZwfCf0o9TuAxRBEPoUUisCepsZqvM76xRfQf2sheO4BC5R/w3UKhwx4w==} - engines: {node: '>=0.10.0'} - hasBin: true + espree@3.0.0-alpha-1: dependencies: acorn: 2.7.0 acorn-jsx: 2.0.1 - dev: true - /espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + espree@9.6.1: dependencies: acorn: 8.12.1 acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 3.4.3 - dev: true - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true + esprima@4.0.1: {} - /esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} - engines: {node: '>=0.10'} + esquery@1.5.0: dependencies: estraverse: 5.3.0 - dev: true - /esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 - dev: true - /estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - dev: true + estraverse@4.3.0: {} - /estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} + estraverse@5.3.0: {} - /estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.5 - dev: true - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} + esutils@2.0.3: {} - /etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - dev: false + etag@1.8.1: {} - /eval-estree-expression@2.0.0: - resolution: {integrity: sha512-e1VweC8biANiuLBY1kZSva3PpaiqaL79S9RR9ql9ngidCYM6tsY20xrUBEJsN63A1yVSmO92chPaBpIGBmGsPg==} + eval-estree-expression@2.0.0: {} - /execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} + execa@8.0.1: dependencies: cross-spawn: 7.0.3 get-stream: 8.0.1 @@ -10066,11 +12722,8 @@ packages: onetime: 6.0.0 signal-exit: 4.1.0 strip-final-newline: 3.0.0 - dev: true - /express@4.20.0: - resolution: {integrity: sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==} - engines: {node: '>= 0.10.0'} + express@4.20.0: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 @@ -10105,12 +12758,8 @@ packages: vary: 1.1.2 transitivePeerDependencies: - supports-color - dev: false - /extract-zip@2.0.1: - resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} - engines: {node: '>= 10.17.0'} - hasBin: true + extract-zip@2.0.1: dependencies: debug: 4.3.5 get-stream: 5.2.0 @@ -10120,79 +12769,51 @@ packages: transitivePeerDependencies: - supports-color - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-deep-equal@3.1.3: {} - /fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - dev: true + fast-diff@1.3.0: {} - /fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-fifo@1.3.2: {} - /fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.7 - dev: true - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true + fast-json-stable-stringify@2.1.0: {} - /fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - dev: true + fast-levenshtein@2.0.6: {} - /fast-xml-parser@4.2.5: - resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==} - hasBin: true + fast-xml-parser@4.2.5: dependencies: strnum: 1.0.5 - /fast-xml-parser@4.4.1: - resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} - hasBin: true + fast-xml-parser@4.4.1: dependencies: strnum: 1.0.5 - /fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.17.1: dependencies: reusify: 1.0.4 - dev: true - /fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fd-slicer@1.1.0: dependencies: pend: 1.2.0 - /fecha@4.2.3: - resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} - dev: false + fecha@4.2.3: {} - /file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 - dev: true - /fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - dev: true - /finalhandler@1.2.0: - resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} - engines: {node: '>= 0.8'} + finalhandler@1.2.0: dependencies: debug: 2.6.9 encodeurl: 1.0.2 @@ -10203,147 +12824,84 @@ packages: unpipe: 1.0.0 transitivePeerDependencies: - supports-color - dev: false - /find-index@0.1.1: - resolution: {integrity: sha512-uJ5vWrfBKMcE6y2Z8834dwEZj9mNGxYa3t3I53OwFeuZ8D9oc2E5zcsrkuhX6h4iYrjhiv0T3szQmxlAV9uxDg==} - dev: true + find-index@0.1.1: {} - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} + find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true - /flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@3.2.0: dependencies: flatted: 3.3.1 keyv: 4.5.4 rimraf: 3.0.2 - dev: true - /flatted@3.3.1: - resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - dev: true + flatted@3.3.1: {} - /fn.name@1.1.0: - resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - dev: false + fn.name@1.1.0: {} - /follow-redirects@1.15.6: - resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true + follow-redirects@1.15.6: {} - /for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-each@0.3.3: dependencies: is-callable: 1.2.7 - dev: true - /foreground-child@3.2.1: - resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} - engines: {node: '>=14'} + foreground-child@3.2.1: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 - dev: true - /form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} + form-data@4.0.0: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - /forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - dev: false + forwarded@0.2.0: {} - /fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - dev: false + fresh@0.5.2: {} - /fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - dev: true + fs-constants@1.0.0: {} - /fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 - /fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} - engines: {node: '>=14.14'} + fs-extra@11.2.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true + fs.realpath@1.0.0: {} - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true + fsevents@2.3.3: optional: true - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-bind@1.1.2: {} - /function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} + function.prototype.name@1.1.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 functions-have-names: 1.2.3 - dev: true - /functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - dev: true + functions-have-names@1.2.3: {} - /generic-pool@3.9.0: - resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} - engines: {node: '>= 4'} - dev: false + generic-pool@3.9.0: {} - /gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} + gensync@1.0.0-beta.2: {} - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} + get-caller-file@2.0.5: {} - /get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - dev: true + get-func-name@2.0.2: {} - /get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} + get-intrinsic@1.2.4: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 @@ -10351,40 +12909,25 @@ packages: has-symbols: 1.0.3 hasown: 2.0.2 - /get-port@5.1.1: - resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} - engines: {node: '>=8'} - dev: true + get-port@5.1.1: {} - /get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} + get-stream@5.2.0: dependencies: pump: 3.0.0 - /get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - dev: true + get-stream@8.0.1: {} - /get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} + get-symbol-description@1.0.2: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 - dev: true - /get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + get-tsconfig@4.8.1: dependencies: resolve-pkg-maps: 1.0.0 - dev: true - /get-uri@6.0.3: - resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} - engines: {node: '>= 14'} + get-uri@6.0.3: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 @@ -10393,31 +12936,19 @@ packages: transitivePeerDependencies: - supports-color - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - dev: true - /glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 - dev: true - /glob2base@0.0.12: - resolution: {integrity: sha512-ZyqlgowMbfj2NPjxaZZ/EtsXlOch28FRXgMd64vqZWk1bT9+wvSRLYD1om9M7QfQru51zJPAT17qXm4/zd+9QA==} - engines: {node: '>= 0.10'} + glob2base@0.0.12: dependencies: find-index: 0.1.1 - dev: true - /glob@10.4.3: - resolution: {integrity: sha512-Q38SGlYRpVtDBPSWEylRyctn7uDeTp4NQERTLiCT1FqA9JXPYWqAVmQU6qh4r/zMM5ehxTcbaO8EjhWnvEhmyg==} - engines: {node: '>=18'} - hasBin: true + glob@10.4.3: dependencies: foreground-child: 3.2.1 jackspeak: 3.4.1 @@ -10425,11 +12956,8 @@ packages: minipass: 7.1.2 package-json-from-dist: 1.0.0 path-scurry: 1.11.1 - dev: true - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + glob@7.2.3: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -10437,30 +12965,19 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - dev: true - /globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} + globals@11.12.0: {} - /globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} + globals@13.24.0: dependencies: type-fest: 0.20.2 - dev: true - /globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} + globalthis@1.0.4: dependencies: define-properties: 1.2.1 gopd: 1.0.1 - dev: true - /globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} + globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 @@ -10468,35 +12985,24 @@ packages: ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 - dev: true - /globby@13.2.2: - resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + globby@13.2.2: dependencies: dir-glob: 3.0.1 fast-glob: 3.3.2 ignore: 5.3.1 merge2: 1.4.1 slash: 4.0.0 - dev: true - /gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + gopd@1.0.1: dependencies: get-intrinsic: 1.2.4 - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graceful-fs@4.2.11: {} - /graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - dev: true + graphemer@1.4.0: {} - /handlebars@4.7.7: - resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==} - engines: {node: '>=0.4.7'} - hasBin: true + handlebars@4.7.7: dependencies: minimist: 1.2.8 neo-async: 2.6.2 @@ -10504,12 +13010,8 @@ packages: wordwrap: 1.0.0 optionalDependencies: uglify-js: 3.18.0 - dev: true - /handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} - engines: {node: '>=0.4.7'} - hasBin: true + handlebars@4.7.8: dependencies: minimist: 1.2.8 neo-async: 2.6.2 @@ -10518,453 +13020,265 @@ packages: optionalDependencies: uglify-js: 3.18.0 - /has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - dev: true + has-bigints@1.0.2: {} - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} + has-flag@3.0.0: {} - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + has-flag@4.0.0: {} - /has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.0 - /has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} + has-proto@1.0.3: {} - /has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} + has-symbols@1.0.3: {} - /has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: dependencies: has-symbols: 1.0.3 - dev: true - /hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + hasown@2.0.2: dependencies: function-bind: 1.1.2 - /heap@0.2.7: - resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==} - dev: false + heap@0.2.7: {} - /html2json@1.0.2: - resolution: {integrity: sha512-tCdVt82U+/D1GCXFIoN5VfCzx767065EZJ5B8nStQUGSXU9PQ4L/0kFvF2of3Qsoe9HGaJni+lxOkqakfw0zpA==} - dev: false + html2json@1.0.2: {} - /http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} + http-errors@2.0.0: dependencies: depd: 2.0.0 inherits: 2.0.4 setprototypeof: 1.2.0 statuses: 2.0.1 toidentifier: 1.0.1 - dev: false - /http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 debug: 4.3.5 transitivePeerDependencies: - supports-color - /https-proxy-agent@7.0.5: - resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} - engines: {node: '>= 14'} + https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.1 debug: 4.3.5 transitivePeerDependencies: - supports-color - /human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - dev: true + human-signals@5.0.0: {} - /iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - dev: false - /ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ieee754@1.2.1: {} - /ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} - dev: true + ignore@5.3.1: {} - /import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - dev: true + imurmurhash@0.1.4: {} - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - dev: true - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inherits@2.0.4: {} - /internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} + internal-slot@1.0.7: dependencies: es-errors: 1.3.0 hasown: 2.0.2 side-channel: 1.0.6 - dev: true - /ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} - engines: {node: '>= 12'} + ip-address@9.0.5: dependencies: jsbn: 1.1.0 sprintf-js: 1.1.3 - /ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - dev: false + ipaddr.js@1.9.1: {} - /is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - dev: true - /is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.2.1: {} - /is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - dev: false + is-arrayish@0.3.2: {} - /is-async-function@2.0.0: - resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} - engines: {node: '>= 0.4'} + is-async-function@2.0.0: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-bigint@1.0.4: dependencies: has-bigints: 1.0.2 - dev: true - /is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} + is-boolean-object@1.1.2: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - dev: true - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - dev: true + is-callable@1.2.7: {} - /is-core-module@2.14.0: - resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} - engines: {node: '>= 0.4'} + is-core-module@2.14.0: dependencies: hasown: 2.0.2 - dev: true - /is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} - engines: {node: '>= 0.4'} + is-data-view@1.0.1: dependencies: is-typed-array: 1.1.13 - dev: true - /is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} + is-date-object@1.0.5: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - dev: true + is-extglob@2.1.1: {} - /is-finalizationregistry@1.0.2: - resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + is-finalizationregistry@1.0.2: dependencies: call-bind: 1.0.7 - dev: true - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} + is-fullwidth-code-point@3.0.0: {} - /is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} + is-generator-function@1.0.10: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - dev: true - /is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - dev: true + is-map@2.0.3: {} - /is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - dev: true + is-negative-zero@2.0.3: {} - /is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} + is-number-object@1.0.7: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - dev: true + is-number@7.0.0: {} - /is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - dev: true + is-path-inside@3.0.3: {} - /is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} + is-regex@1.1.4: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - dev: true - /is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - dev: true + is-set@2.0.3: {} - /is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} - engines: {node: '>= 0.4'} + is-shared-array-buffer@1.0.3: dependencies: call-bind: 1.0.7 - dev: true - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: false + is-stream@2.0.1: {} - /is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + is-stream@3.0.0: {} - /is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} + is-string@1.0.7: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} + is-symbol@1.0.4: dependencies: has-symbols: 1.0.3 - dev: true - /is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} + is-typed-array@1.1.13: dependencies: which-typed-array: 1.1.15 - dev: true - /is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - dev: true + is-weakmap@2.0.2: {} - /is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-weakref@1.0.2: dependencies: call-bind: 1.0.7 - dev: true - - /is-weakset@2.0.3: - resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} - engines: {node: '>= 0.4'} + + is-weakset@2.0.3: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - dev: true - /isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@1.0.0: {} - /isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - dev: true + isarray@2.0.5: {} - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true + isexe@2.0.0: {} - /iterator.prototype@1.1.2: - resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + iterator.prototype@1.1.2: dependencies: define-properties: 1.2.1 get-intrinsic: 1.2.4 has-symbols: 1.0.3 reflect.getprototypeof: 1.0.6 set-function-name: 2.0.2 - dev: true - /jackspeak@3.4.1: - resolution: {integrity: sha512-U23pQPDnmYybVkYjObcuYMk43VRlMLLqLI+RdZy8s8WV8WsxO9SnqSroKaluuvcNOdCAlauKszDwd+umbot5Mg==} - engines: {node: '>=18'} + jackspeak@3.4.1: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - dev: true - /jose@4.15.9: - resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} - dev: false + jose@4.15.9: {} - /jose@5.9.4: - resolution: {integrity: sha512-WBBl6au1qg6OHj67yCffCgFR3BADJBXN8MdRvCgJDuMv3driV2nHr7jdGvaKX9IolosAsn+M0XRArqLXUhyJHQ==} - dev: false + jose@5.9.4: {} - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@4.0.0: {} - /js-tokens@9.0.0: - resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} - dev: true + js-tokens@9.0.0: {} - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true + js-yaml@3.14.1: dependencies: argparse: 1.0.10 esprima: 4.0.1 - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - /jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + jsbn@1.1.0: {} - /jsdoc-type-pratt-parser@3.1.0: - resolution: {integrity: sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==} - engines: {node: '>=12.0.0'} - dev: true + jsdoc-type-pratt-parser@3.1.0: {} - /jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true + jsesc@2.5.2: {} - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true + json-buffer@3.0.1: {} - /json-diff@1.0.6: - resolution: {integrity: sha512-tcFIPRdlc35YkYdGxcamJjllUhXWv4n2rK9oJ2RsAzV4FBkuV4ojKEDgcZ+kpKxDmJKv+PFK65+1tVVOnSeEqA==} - hasBin: true + json-diff@1.0.6: dependencies: '@ewoudenberg/difflib': 0.1.0 colors: 1.4.0 dreamopt: 0.8.0 - dev: false - /json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-parse-even-better-errors@2.3.1: {} - /json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true + json-schema-traverse@0.4.1: {} - /json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-traverse@1.0.0: {} - /json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - dev: true + json-stable-stringify-without-jsonify@1.0.1: {} - /json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true + json5@1.0.2: dependencies: minimist: 1.2.8 - dev: true - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true + json5@2.2.3: {} - /jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonfile@6.1.0: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 - /jsonwebtoken@9.0.2: - resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} - engines: {node: '>=12', npm: '>=6'} + jsonwebtoken@9.0.2: dependencies: jws: 3.2.2 lodash.includes: 4.3.0 @@ -10977,30 +13291,22 @@ packages: ms: 2.1.3 semver: 7.6.2 - /jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 array.prototype.flat: 1.3.2 object.assign: 4.1.5 object.values: 1.2.0 - dev: true - /just-extend@6.2.0: - resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==} - dev: true + just-extend@6.2.0: {} - /jwa@1.4.1: - resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + jwa@1.4.1: dependencies: buffer-equal-constant-time: 1.0.1 ecdsa-sig-formatter: 1.0.11 safe-buffer: 5.2.1 - /jwks-rsa@3.1.0: - resolution: {integrity: sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==} - engines: {node: '>=14'} + jwks-rsa@3.1.0: dependencies: '@types/express': 4.17.21 '@types/jsonwebtoken': 9.0.6 @@ -11010,133 +13316,79 @@ packages: lru-memoizer: 2.3.0 transitivePeerDependencies: - supports-color - dev: false - /jws@3.2.2: - resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + jws@3.2.2: dependencies: jwa: 1.4.1 safe-buffer: 5.2.1 - /kafkajs@2.2.4: - resolution: {integrity: sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==} - engines: {node: '>=14.0.0'} + kafkajs@2.2.4: {} - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 - dev: true - /kuler@2.0.0: - resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} - dev: false + kuler@2.0.0: {} - /lazystream@1.0.1: - resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} - engines: {node: '>= 0.6.3'} + lazystream@1.0.1: dependencies: readable-stream: 2.3.8 - dev: true - /levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 - dev: true - /limiter@1.1.5: - resolution: {integrity: sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==} - dev: false + limiter@1.1.5: {} - /lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lines-and-columns@1.2.4: {} - /local-pkg@0.5.0: - resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} - engines: {node: '>=14'} + local-pkg@0.5.0: dependencies: mlly: 1.7.1 pkg-types: 1.1.3 - dev: true - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 - dev: true - /lodash.clonedeep@4.5.0: - resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - dev: false + lodash.clonedeep@4.5.0: {} - /lodash.defaults@4.2.0: - resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} - dev: true + lodash.defaults@4.2.0: {} - /lodash.difference@4.5.0: - resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} - dev: true + lodash.difference@4.5.0: {} - /lodash.flatten@4.4.0: - resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} - dev: true + lodash.flatten@4.4.0: {} - /lodash.get@4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} - dev: true + lodash.get@4.4.2: {} - /lodash.includes@4.3.0: - resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + lodash.includes@4.3.0: {} - /lodash.isboolean@3.0.3: - resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + lodash.isboolean@3.0.3: {} - /lodash.isempty@4.4.0: - resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==} - dev: false + lodash.isempty@4.4.0: {} - /lodash.isequal@4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - dev: false + lodash.isequal@4.5.0: {} - /lodash.isinteger@4.0.4: - resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + lodash.isinteger@4.0.4: {} - /lodash.isnumber@3.0.3: - resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + lodash.isnumber@3.0.3: {} - /lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + lodash.isplainobject@4.0.6: {} - /lodash.isstring@4.0.1: - resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + lodash.isstring@4.0.1: {} - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: true + lodash.merge@4.6.2: {} - /lodash.once@4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + lodash.once@4.1.1: {} - /lodash.union@4.6.0: - resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} - dev: true + lodash.union@4.6.0: {} - /lodash.zip@4.2.0: - resolution: {integrity: sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==} - dev: true + lodash.zip@4.2.0: {} - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true + lodash@4.17.21: {} - /logform@2.6.0: - resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} - engines: {node: '>= 12.0.0'} + logform@2.6.0: dependencies: '@colors/colors': 1.6.0 '@types/triple-beam': 1.3.5 @@ -11144,245 +13396,129 @@ packages: ms: 2.1.3 safe-stable-stringify: 2.4.3 triple-beam: 1.4.1 - dev: false - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 - dev: true - /loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + loupe@2.3.7: dependencies: get-func-name: 2.0.2 - dev: true - /lru-cache@10.4.0: - resolution: {integrity: sha512-bfJaPTuEiTYBu+ulDaeQ0F+uLmlfFkMgXj4cbwfuMSjgObGMzb55FMMbDvbRU0fAHZ4sLGkz2mKwcMg8Dvm8Ww==} - engines: {node: '>=18'} - dev: true + lru-cache@10.4.0: {} - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} + lru-cache@6.0.0: dependencies: yallist: 4.0.0 - /lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} + lru-cache@7.18.3: {} - /lru-memoizer@2.3.0: - resolution: {integrity: sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==} + lru-memoizer@2.3.0: dependencies: lodash.clonedeep: 4.5.0 lru-cache: 6.0.0 - dev: false - /magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - /make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true + make-error@1.3.6: {} - /media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - dev: false + media-typer@0.3.0: {} - /memory-pager@1.5.0: - resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - dev: false + memory-pager@1.5.0: {} - /meow@12.1.1: - resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} - engines: {node: '>=16.10'} - dev: true + meow@12.1.1: {} - /merge-descriptors@1.0.3: - resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} - dev: false + merge-descriptors@1.0.3: {} - /merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true + merge-stream@2.0.0: {} - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - dev: true + merge2@1.4.1: {} - /methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - dev: false + methods@1.1.2: {} - /micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} - engines: {node: '>=8.6'} + micromatch@4.0.7: dependencies: braces: 3.0.3 picomatch: 2.3.1 - dev: true - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} + mime-db@1.52.0: {} - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 - /mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - dev: false + mime@1.6.0: {} - /mime@4.0.4: - resolution: {integrity: sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==} - engines: {node: '>=16'} - hasBin: true - dev: false + mime@4.0.4: {} - /mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - dev: true + mimic-fn@4.0.0: {} - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 - dev: true - /minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} + minimatch@5.1.6: dependencies: brace-expansion: 2.0.1 - dev: true - /minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 - dev: true - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minimist@1.2.8: {} - /minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - dev: true + minipass@7.1.2: {} - /mitt@3.0.1: - resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + mitt@3.0.1: {} - /mkdirp-classic@0.5.3: - resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - dev: true + mkdirp-classic@0.5.3: {} - /mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true + mkdirp@0.5.6: dependencies: minimist: 1.2.8 - dev: false - /mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - dev: true + mkdirp@1.0.4: {} - /mkdirp@3.0.1: - resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} - engines: {node: '>=10'} - hasBin: true - dev: true + mkdirp@3.0.1: {} - /mlly@1.7.1: - resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + mlly@1.7.1: dependencies: acorn: 8.12.1 pathe: 1.1.2 pkg-types: 1.1.3 ufo: 1.5.3 - dev: true - /mnemonist@0.38.3: - resolution: {integrity: sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==} + mnemonist@0.38.3: dependencies: obliterator: 1.6.1 - /mongodb-connection-string-url@3.0.1: - resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + mongodb-connection-string-url@3.0.1: dependencies: '@types/whatwg-url': 11.0.5 whatwg-url: 13.0.0 - dev: false - /mongodb@6.7.0: - resolution: {integrity: sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==} - engines: {node: '>=16.20.1'} - peerDependencies: - '@aws-sdk/credential-providers': ^3.188.0 - '@mongodb-js/zstd': ^1.1.0 - gcp-metadata: ^5.2.0 - kerberos: ^2.0.1 - mongodb-client-encryption: '>=6.0.0 <7' - snappy: ^7.2.2 - socks: ^2.7.1 - peerDependenciesMeta: - '@aws-sdk/credential-providers': - optional: true - '@mongodb-js/zstd': - optional: true - gcp-metadata: - optional: true - kerberos: - optional: true - mongodb-client-encryption: - optional: true - snappy: - optional: true - socks: - optional: true + mongodb@6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3): dependencies: '@mongodb-js/saslprep': 1.1.7 bson: 6.8.0 mongodb-connection-string-url: 3.0.1 - dev: false + optionalDependencies: + '@aws-sdk/credential-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) + socks: 2.8.3 - /ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - dev: false + ms@2.0.0: {} - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.2: {} - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + ms@2.1.3: {} - /multer@1.4.5-lts.1: - resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==} - engines: {node: '>= 6.0.0'} + multer@1.4.5-lts.1: dependencies: append-field: 1.0.0 busboy: 1.6.0 @@ -11391,189 +13527,111 @@ packages: object-assign: 4.1.1 type-is: 1.6.18 xtend: 4.0.2 - dev: false - /nan@2.20.0: - resolution: {integrity: sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==} - requiresBuild: true + nan@2.20.0: optional: true - /nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true + nanoid@3.3.7: {} - /natural-compare-lite@1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - dev: true + natural-compare-lite@1.4.0: {} - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true + natural-compare@1.4.0: {} - /negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - dev: false + negotiator@0.6.3: {} - /neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + neo-async@2.6.2: {} - /netmask@2.0.2: - resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} - engines: {node: '>= 0.4.0'} + netmask@2.0.2: {} - /nise@5.1.9: - resolution: {integrity: sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==} + nise@5.1.9: dependencies: '@sinonjs/commons': 3.0.1 '@sinonjs/fake-timers': 11.3.1 '@sinonjs/text-encoding': 0.7.3 just-extend: 6.2.0 path-to-regexp: 6.3.0 - dev: true - /node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - dev: true - /node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + node-releases@2.0.14: {} - /nodemailer@6.9.14: - resolution: {integrity: sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==} - engines: {node: '>=6.0.0'} - dev: false + nodemailer@6.9.14: {} - /nodemailer@6.9.9: - resolution: {integrity: sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==} - engines: {node: '>=6.0.0'} - dev: false + nodemailer@6.9.9: {} - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: true + normalize-path@3.0.0: {} - /npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + npm-run-path@5.3.0: dependencies: path-key: 4.0.0 - dev: true - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} + object-assign@4.1.1: {} - /object-inspect@1.13.2: - resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} - engines: {node: '>= 0.4'} + object-inspect@1.13.2: {} - /object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - dev: true + object-keys@1.1.1: {} - /object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} + object.assign@4.1.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 - dev: true - /object.entries@1.1.8: - resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} - engines: {node: '>= 0.4'} + object.entries@1.1.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: true - /object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} + object.fromentries@2.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - dev: true - /object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} - engines: {node: '>= 0.4'} + object.groupby@1.0.3: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 - dev: true - /object.hasown@1.1.4: - resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} - engines: {node: '>= 0.4'} + object.hasown@1.1.4: dependencies: define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - dev: true - /object.values@1.2.0: - resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} - engines: {node: '>= 0.4'} + object.values@1.2.0: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: true - /obliterator@1.6.1: - resolution: {integrity: sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==} + obliterator@1.6.1: {} - /on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 - dev: false - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + once@1.4.0: dependencies: wrappy: 1.0.2 - /one-time@1.0.0: - resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + one-time@1.0.0: dependencies: fn.name: 1.1.0 - dev: false - /onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} + onetime@6.0.0: dependencies: mimic-fn: 4.0.0 - dev: true - /openapi-types@12.1.3: - resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + openapi-types@12.1.3: {} - /openapi-zod-client@1.18.1: - resolution: {integrity: sha512-L0GzU/7Sx9ugbWWoQwOJdKtyxr8ZnjxIK2RJP63//OkmKws2w7c5HSgS2bdNxPVCIp/eJuYk+CtaKfvCoJ08Yw==} - hasBin: true + openapi-zod-client@1.18.1: dependencies: '@apidevtools/swagger-parser': 10.1.0(openapi-types@12.1.3) '@liuli-util/fs-extra': 0.1.0 @@ -11595,14 +13653,11 @@ packages: - supports-color - xstate - /openapi3-ts@3.1.0: - resolution: {integrity: sha512-1qKTvCCVoV0rkwUh1zq5o8QyghmwYPuhdvtjv1rFjuOnJToXhQyF8eGjNETQ8QmGjr9Jz/tkAKLITIl2s7dw3A==} + openapi3-ts@3.1.0: dependencies: yaml: 2.4.5 - /optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} + optionator@0.9.4: dependencies: deep-is: 0.1.4 fast-levenshtein: 2.0.6 @@ -11610,37 +13665,22 @@ packages: prelude-ls: 1.2.1 type-check: 0.4.0 word-wrap: 1.2.5 - dev: true - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - dev: true - /p-limit@5.0.0: - resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} - engines: {node: '>=18'} + p-limit@5.0.0: dependencies: yocto-queue: 1.1.1 - dev: true - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + p-locate@5.0.0: dependencies: p-limit: 3.1.0 - dev: true - /p-map@6.0.0: - resolution: {integrity: sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==} - engines: {node: '>=16'} - dev: true + p-map@6.0.0: {} - /pac-proxy-agent@7.0.2: - resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==} - engines: {node: '>= 14'} + pac-proxy-agent@7.0.2: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.1 @@ -11653,48 +13693,27 @@ packages: transitivePeerDependencies: - supports-color - /pac-resolver@7.0.1: - resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} - engines: {node: '>= 14'} + pac-resolver@7.0.1: dependencies: degenerator: 5.0.1 netmask: 2.0.2 - /package-json-from-dist@1.0.0: - resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} - dev: true + package-json-from-dist@1.0.0: {} - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} + parent-module@1.0.1: dependencies: callsites: 3.1.0 - /parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} + parse-json@5.2.0: dependencies: '@babel/code-frame': 7.24.7 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - /parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - dev: false + parseurl@1.3.3: {} - /pastable@2.2.1: - resolution: {integrity: sha512-K4ClMxRKpgN4sXj6VIPPrvor/TMp2yPNCGtfhvV106C73SwefQ3FuegURsH7AQHpqu0WwbvKXRl1HQxF6qax9w==} - engines: {node: '>=14.x'} - peerDependencies: - react: '>=17' - xstate: '>=4.32.1' - peerDependenciesMeta: - react: - optional: true - xstate: - optional: true + pastable@2.2.1: dependencies: '@babel/core': 7.24.7 ts-toolbelt: 9.6.0 @@ -11702,88 +13721,47 @@ packages: transitivePeerDependencies: - supports-color - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: true + path-exists@4.0.0: {} - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: true + path-is-absolute@1.0.1: {} - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - dev: true + path-key@3.1.1: {} - /path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - dev: true + path-key@4.0.0: {} - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true + path-parse@1.0.7: {} - /path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} + path-scurry@1.11.1: dependencies: lru-cache: 10.4.0 minipass: 7.1.2 - dev: true - /path-to-regexp@0.1.10: - resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} - dev: false + path-to-regexp@0.1.10: {} - /path-to-regexp@6.3.0: - resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} - dev: true + path-to-regexp@6.3.0: {} - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - dev: true + path-type@4.0.0: {} - /pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - dev: true + pathe@1.1.2: {} - /pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - dev: true + pathval@1.1.1: {} - /pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + pend@1.2.0: {} - /pg-cloudflare@1.1.1: - resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} - requiresBuild: true + pg-cloudflare@1.1.1: optional: true - /pg-connection-string@2.6.4: - resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} + pg-connection-string@2.6.4: {} - /pg-int8@1.0.1: - resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} - engines: {node: '>=4.0.0'} + pg-int8@1.0.1: {} - /pg-minify@1.6.4: - resolution: {integrity: sha512-cf6hBt1YqzqPX0OznXKSv4U7e4o7eUU4zp2zQsbJ+4OCNNr7EnnAVWkIz4k0dv6UN4ouS1ZL4WlXxCrZHHl69g==} - engines: {node: '>=14.0.0'} + pg-minify@1.6.4: {} - /pg-pool@3.6.2(pg@8.11.5): - resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==} - peerDependencies: - pg: '>=8.0' + pg-pool@3.6.2(pg@8.11.5): dependencies: pg: 8.11.5 - /pg-promise@11.8.0: - resolution: {integrity: sha512-w9hTFpkM4FByJTJ7KCWLtZSOtQa2BKC+XIV8+3ZvDlfYfBYdz8V4V+BttnqhUPY/d12Itug7Bft4XdILihsY+w==} - engines: {node: '>=14.0'} + pg-promise@11.8.0: dependencies: assert-options: 0.8.1 pg: 8.11.5 @@ -11792,12 +13770,9 @@ packages: transitivePeerDependencies: - pg-native - /pg-protocol@1.6.1: - resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + pg-protocol@1.6.1: {} - /pg-types@2.2.0: - resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} - engines: {node: '>=4'} + pg-types@2.2.0: dependencies: pg-int8: 1.0.1 postgres-array: 2.0.0 @@ -11805,14 +13780,7 @@ packages: postgres-date: 1.0.7 postgres-interval: 1.2.0 - /pg@8.11.5: - resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==} - engines: {node: '>= 8.0.0'} - peerDependencies: - pg-native: '>=3.0.1' - peerDependenciesMeta: - pg-native: - optional: true + pg@8.11.5: dependencies: pg-connection-string: 2.6.4 pg-pool: 3.6.2(pg@8.11.5) @@ -11822,134 +13790,83 @@ packages: optionalDependencies: pg-cloudflare: 1.1.1 - /pgpass@1.0.5: - resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + pgpass@1.0.5: dependencies: split2: 4.2.0 - /picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.0.1: {} - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - dev: true + picomatch@2.3.1: {} - /pkg-types@1.1.3: - resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + pkg-types@1.1.3: dependencies: confbox: 0.1.7 mlly: 1.7.1 pathe: 1.1.2 - dev: true - /possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - dev: true + possible-typed-array-names@1.0.0: {} - /postcss@8.4.39: - resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} - engines: {node: ^10 || ^12 || >=14} + postcss@8.4.39: dependencies: nanoid: 3.3.7 picocolors: 1.0.1 source-map-js: 1.2.0 - dev: true - /postgres-array@2.0.0: - resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} - engines: {node: '>=4'} + postgres-array@2.0.0: {} - /postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} - engines: {node: '>=0.10.0'} + postgres-bytea@1.0.0: {} - /postgres-date@1.0.7: - resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} - engines: {node: '>=0.10.0'} + postgres-date@1.0.7: {} - /postgres-interval@1.2.0: - resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} - engines: {node: '>=0.10.0'} + postgres-interval@1.2.0: dependencies: xtend: 4.0.2 - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - dev: true + prelude-ls@1.2.1: {} - /prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} - engines: {node: '>=6.0.0'} + prettier-linter-helpers@1.0.0: dependencies: fast-diff: 1.3.0 - dev: true - /prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true + prettier@2.8.8: {} - /pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-format@29.7.0: dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.3.1 - dev: true - /process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process-nextick-args@2.0.1: {} - /progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} + progress@2.0.3: {} - /promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} + promise-retry@2.0.1: dependencies: err-code: 2.0.3 retry: 0.12.0 - dev: false - /prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 - dev: true - /proper-lockfile@4.1.2: - resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + proper-lockfile@4.1.2: dependencies: graceful-fs: 4.2.11 retry: 0.12.0 signal-exit: 3.0.7 - dev: true - /properties-reader@2.3.0: - resolution: {integrity: sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==} - engines: {node: '>=14'} + properties-reader@2.3.0: dependencies: mkdirp: 1.0.4 - dev: true - /proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 - dev: false - /proxy-agent@6.4.0: - resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==} - engines: {node: '>= 14'} + proxy-agent@6.4.0: dependencies: agent-base: 7.1.1 debug: 4.3.5 @@ -11962,22 +13879,16 @@ packages: transitivePeerDependencies: - supports-color - /proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@1.1.0: {} - /pump@3.0.0: - resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + pump@3.0.0: dependencies: end-of-stream: 1.4.4 once: 1.4.0 - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + punycode@2.3.1: {} - /puppeteer-core@22.11.2: - resolution: {integrity: sha512-vQo+YDuePyvj+92Z9cdtxi/HalKf+k/R4tE80nGtQqJRNqU81eHaHkbVfnLszdaLlvwFF5tipnnSCzqWlEddtw==} - engines: {node: '>=18'} + puppeteer-core@22.11.2: dependencies: '@puppeteer/browsers': 2.2.3 chromium-bidi: 0.5.23(devtools-protocol@0.0.1299070) @@ -11989,11 +13900,7 @@ packages: - supports-color - utf-8-validate - /puppeteer@22.11.2(typescript@5.4.5): - resolution: {integrity: sha512-8fjdQSgW0sq7471ftca24J7sXK+jXZ7OW7Gx+NEBFNyXrcTiBfukEI46gNq6hiMhbLEDT30NeylK/1ZoPdlKSA==} - engines: {node: '>=18'} - hasBin: true - requiresBuild: true + puppeteer@22.11.2(typescript@5.4.5): dependencies: '@puppeteer/browsers': 2.2.3 cosmiconfig: 9.0.0(typescript@5.4.5) @@ -12005,75 +13912,45 @@ packages: - typescript - utf-8-validate - /qs@6.11.0: - resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} - engines: {node: '>=0.6'} + qs@6.11.0: dependencies: side-channel: 1.0.6 - dev: false - /qs@6.12.3: - resolution: {integrity: sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==} - engines: {node: '>=0.6'} + qs@6.12.3: dependencies: side-channel: 1.0.6 - dev: false - /qs@6.13.0: - resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} - engines: {node: '>=0.6'} + qs@6.13.0: dependencies: side-channel: 1.0.6 - dev: false - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true + queue-microtask@1.2.3: {} - /queue-tick@1.0.1: - resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + queue-tick@1.0.1: {} - /quote@0.4.0: - resolution: {integrity: sha512-KHp3y3xDjuBhRx+tYKOgzPnVHMRlgpn2rU450GcU4PL24r1H6ls/hfPrxDwX2pvYMlwODHI2l8WwgoV69x5rUQ==} - dev: true + quote@0.4.0: {} - /randexp@0.5.3: - resolution: {integrity: sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==} - engines: {node: '>=4'} + randexp@0.5.3: dependencies: drange: 1.1.1 ret: 0.2.2 - dev: true - /range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - dev: false + range-parser@1.2.1: {} - /rate-limiter-flexible@5.0.3: - resolution: {integrity: sha512-lWx2y8NBVlTOLPyqs+6y7dxfEpT6YFqKy3MzWbCy95sTTOhOuxufP2QvRyOHpfXpB9OUJPbVLybw3z3AVAS5fA==} - dev: false + rate-limiter-flexible@5.0.3: {} - /raw-body@2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} + raw-body@2.5.2: dependencies: bytes: 3.1.2 http-errors: 2.0.0 iconv-lite: 0.4.24 unpipe: 1.0.0 - dev: false - /react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - dev: true + react-is@16.13.1: {} - /react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - dev: true + react-is@18.3.1: {} - /readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -12083,22 +13960,17 @@ packages: string_decoder: 1.1.1 util-deprecate: 1.0.2 - /readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - /readdir-glob@1.1.3: - resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + readdir-glob@1.1.3: dependencies: minimatch: 5.1.6 - dev: true - /redis@4.6.15: - resolution: {integrity: sha512-2NtuOpMW3tnYzBw6S8mbXSX7RPzvVFCA2wFJq9oErushO2UeBkxObk+uvo7gv7n0rhWeOj/IzrHO8TjcFlRSOg==} + redis@4.6.15: dependencies: '@redis/bloom': 1.2.0(@redis/client@1.5.17) '@redis/client': 1.5.17 @@ -12106,11 +13978,8 @@ packages: '@redis/json': 1.0.6(@redis/client@1.5.17) '@redis/search': 1.1.6(@redis/client@1.5.17) '@redis/time-series': 1.0.5(@redis/client@1.5.17) - dev: false - /reflect.getprototypeof@1.0.6: - resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} - engines: {node: '>= 0.4'} + reflect.getprototypeof@1.0.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -12119,83 +13988,47 @@ packages: get-intrinsic: 1.2.4 globalthis: 1.0.4 which-builtin-type: 1.1.3 - dev: true - /regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} + regexp.prototype.flags@1.5.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-errors: 1.3.0 set-function-name: 2.0.2 - dev: true - /req-all@0.1.0: - resolution: {integrity: sha512-ZdvPr8uXy9ujX3KujwE2P1HWkMYgogIhqeAeyb47MqWjSfyxERSm0TNbN/IapCCmWDufXab04AYrRgObaJCJ6Q==} - engines: {node: '>=4'} - dev: true + req-all@0.1.0: {} - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} + require-directory@2.1.1: {} - /require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} + require-from-string@2.0.2: {} - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} + resolve-from@4.0.0: {} - /resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - dev: true + resolve-pkg-maps@1.0.0: {} - /resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true + resolve@1.22.8: dependencies: is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true - /resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true + resolve@2.0.0-next.5: dependencies: is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true - /ret@0.2.2: - resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} - engines: {node: '>=4'} - dev: true + ret@0.2.2: {} - /retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} + retry@0.12.0: {} - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true + reusify@1.0.4: {} - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true + rimraf@3.0.2: dependencies: glob: 7.2.3 - dev: true - /rollup@4.18.1: - resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true + rollup@4.18.1: dependencies: '@types/estree': 1.0.5 optionalDependencies: @@ -12216,66 +14049,41 @@ packages: '@rollup/rollup-win32-ia32-msvc': 4.18.1 '@rollup/rollup-win32-x64-msvc': 4.18.1 fsevents: 2.3.3 - dev: true - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - dev: true - /safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} - engines: {node: '>=0.4'} + safe-array-concat@1.1.2: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 has-symbols: 1.0.3 isarray: 2.0.5 - dev: true - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.1.2: {} - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-buffer@5.2.1: {} - /safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} + safe-regex-test@1.0.3: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-regex: 1.1.4 - dev: true - /safe-stable-stringify@2.4.3: - resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} - engines: {node: '>=10'} - dev: false + safe-stable-stringify@2.4.3: {} - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + safer-buffer@2.1.2: {} - /semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true + semver@6.3.1: {} - /semver@7.6.0: - resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} - engines: {node: '>=10'} - hasBin: true + semver@7.6.0: dependencies: lru-cache: 6.0.0 - /semver@7.6.2: - resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} - engines: {node: '>=10'} - hasBin: true + semver@7.6.2: {} - /send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} - engines: {node: '>= 0.8.0'} + send@0.18.0: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -12292,11 +14100,8 @@ packages: statuses: 2.0.1 transitivePeerDependencies: - supports-color - dev: false - /send@0.19.0: - resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} - engines: {node: '>= 0.8.0'} + send@0.19.0: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -12313,11 +14118,8 @@ packages: statuses: 2.0.1 transitivePeerDependencies: - supports-color - dev: false - /serve-static@1.16.0: - resolution: {integrity: sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==} - engines: {node: '>= 0.8.0'} + serve-static@1.16.0: dependencies: encodeurl: 1.0.2 escape-html: 1.0.3 @@ -12325,11 +14127,8 @@ packages: send: 0.18.0 transitivePeerDependencies: - supports-color - dev: false - /set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 @@ -12338,66 +14137,41 @@ packages: gopd: 1.0.1 has-property-descriptors: 1.0.2 - /set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} + set-function-name@2.0.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 - dev: true - /setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - dev: false + setprototypeof@1.2.0: {} - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - dev: true - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - dev: true + shebang-regex@3.0.0: {} - /shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - dev: true + shell-quote@1.8.1: {} - /side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} + side-channel@1.0.6: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 object-inspect: 1.13.2 - /siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - dev: true + siginfo@2.0.0: {} - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true + signal-exit@3.0.7: {} - /signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - dev: true + signal-exit@4.1.0: {} - /simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 - dev: false - /sinon@16.1.3: - resolution: {integrity: sha512-mjnWWeyxcAf9nC0bXcPmiDut+oE8HYridTNzBbF98AYVLmWwGRp2ISEpyhYflG1ifILT+eNn3BmKUJPxjXUPlA==} + sinon@16.1.3: dependencies: '@sinonjs/commons': 3.0.1 '@sinonjs/fake-timers': 10.3.0 @@ -12405,25 +14179,14 @@ packages: diff: 5.2.0 nise: 5.1.9 supports-color: 7.2.0 - dev: true - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - dev: true + slash@3.0.0: {} - /slash@4.0.0: - resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} - engines: {node: '>=12'} - dev: true + slash@4.0.0: {} - /smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + smart-buffer@4.2.0: {} - /socks-proxy-agent@8.0.4: - resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==} - engines: {node: '>= 14'} + socks-proxy-agent@8.0.4: dependencies: agent-base: 7.1.1 debug: 4.3.5 @@ -12431,81 +14194,50 @@ packages: transitivePeerDependencies: - supports-color - /socks@2.8.3: - resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + socks@2.8.3: dependencies: ip-address: 9.0.5 smart-buffer: 4.2.0 - /source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} - engines: {node: '>=0.10.0'} - dev: true + source-map-js@1.2.0: {} - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} + source-map@0.6.1: {} - /sparse-bitfield@3.0.3: - resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + sparse-bitfield@3.0.3: dependencies: memory-pager: 1.5.0 - dev: false - /spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - dev: true + spdx-exceptions@2.5.0: {} - /spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.18 - dev: true - /spdx-license-ids@3.0.18: - resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} - dev: true + spdx-license-ids@3.0.18: {} - /spex@3.3.0: - resolution: {integrity: sha512-VNiXjFp6R4ldPbVRYbpxlD35yRHceecVXlct1J4/X80KuuPnW2AXMq3sGwhnJOhKkUsOxAT6nRGfGE5pocVw5w==} - engines: {node: '>=10.0.0'} + spex@3.3.0: {} - /split-ca@1.0.1: - resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} - dev: true + split-ca@1.0.1: {} - /split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} + split2@4.2.0: {} - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + sprintf-js@1.0.3: {} - /sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + sprintf-js@1.1.3: {} - /ssh-remote-port-forward@1.0.4: - resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} + ssh-remote-port-forward@1.0.4: dependencies: '@types/ssh2': 0.5.52 ssh2: 1.15.0 - dev: true - /ssh2-sftp-client@9.1.0: - resolution: {integrity: sha512-Hzdr9OE6GxZjcmyM9tgBSIFVyrHAp9c6U2Y4yBkmYOHoQvZ7pIm27dmltvcmRfxcWiIcg8HBvG5iAikDf+ZuzQ==} - engines: {node: '>=10.24.1'} + ssh2-sftp-client@9.1.0: dependencies: concat-stream: 2.0.0 promise-retry: 2.0.1 ssh2: 1.15.0 - dev: false - /ssh2@1.15.0: - resolution: {integrity: sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==} - engines: {node: '>=10.16.0'} - requiresBuild: true + ssh2@1.15.0: dependencies: asn1: 0.2.6 bcrypt-pbkdf: 1.0.2 @@ -12513,34 +14245,19 @@ packages: cpu-features: 0.0.10 nan: 2.20.0 - /stack-trace@0.0.10: - resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} - dev: false + stack-trace@0.0.10: {} - /stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - dev: true + stackback@0.0.2: {} - /statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - dev: false + statuses@2.0.1: {} - /std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - dev: true + std-env@3.7.0: {} - /stream-transform@3.3.2: - resolution: {integrity: sha512-v64PUnPy9Qw94NGuaEMo+9RHQe4jTBYf+NkTtqkCgeuiNo8NlL0LtLR7fkKWNVFtp3RhIm5Dlxkgm5uz7TDimQ==} - dev: false + stream-transform@3.3.2: {} - /streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} - engines: {node: '>=10.0.0'} - dev: false + streamsearch@1.1.0: {} - /streamx@2.18.0: - resolution: {integrity: sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==} + streamx@2.18.0: dependencies: fast-fifo: 1.3.2 queue-tick: 1.0.1 @@ -12548,26 +14265,19 @@ packages: optionalDependencies: bare-events: 2.4.2 - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - /string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} + string-width@5.1.2: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 - dev: true - /string.prototype.matchall@4.0.11: - resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} - engines: {node: '>= 0.4'} + string.prototype.matchall@4.0.11: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -12581,122 +14291,81 @@ packages: regexp.prototype.flags: 1.5.2 set-function-name: 2.0.2 side-channel: 1.0.6 - dev: true - /string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} - engines: {node: '>= 0.4'} + string.prototype.trim@1.2.9: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - dev: true - /string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + string.prototype.trimend@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: true - /string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} + string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: true - /string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 - /string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - /strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} + strip-ansi@7.1.0: dependencies: ansi-regex: 6.0.1 - dev: true - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - dev: true + strip-bom@3.0.0: {} - /strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - dev: true + strip-final-newline@3.0.0: {} - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - dev: true + strip-json-comments@3.1.1: {} - /strip-literal@2.1.0: - resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + strip-literal@2.1.0: dependencies: js-tokens: 9.0.0 - dev: true - /strnum@1.0.5: - resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + strnum@1.0.5: {} - /subarg@1.0.0: - resolution: {integrity: sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==} + subarg@1.0.0: dependencies: minimist: 1.2.8 - dev: true - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - dev: true + supports-preserve-symlinks-flag@1.0.0: {} - /tanu@0.1.13: - resolution: {integrity: sha512-UbRmX7ccZ4wMVOY/Uw+7ji4VOkEYSYJG1+I4qzbnn4qh/jtvVbrm6BFnF12NQQ4+jGv21wKmjb1iFyUSVnBWcQ==} + tanu@0.1.13: dependencies: tslib: 2.6.3 typescript: 4.9.5 - /tar-fs@2.0.1: - resolution: {integrity: sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==} + tar-fs@2.0.1: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 pump: 3.0.0 tar-stream: 2.2.0 - dev: true - /tar-fs@3.0.5: - resolution: {integrity: sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==} + tar-fs@3.0.5: dependencies: pump: 3.0.0 tar-stream: 3.1.7 @@ -12704,36 +14373,29 @@ packages: bare-fs: 2.3.1 bare-path: 2.1.3 - /tar-fs@3.0.6: - resolution: {integrity: sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==} + tar-fs@3.0.6: dependencies: pump: 3.0.0 tar-stream: 3.1.7 optionalDependencies: bare-fs: 2.3.1 bare-path: 2.1.3 - dev: true - /tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} + tar-stream@2.2.0: dependencies: bl: 4.1.0 end-of-stream: 1.4.4 fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true - /tar-stream@3.1.7: - resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + tar-stream@3.1.7: dependencies: b4a: 1.6.6 fast-fifo: 1.3.2 streamx: 2.18.0 - /testcontainers@10.9.0: - resolution: {integrity: sha512-LN+cKAOd61Up9SVMJW+3VFVGeVQG8JBqZhEQo2U0HBfIsAynyAXcsLBSo+KZrOfy9SBz7pGHctWN/KabLDbNFA==} + testcontainers@10.9.0: dependencies: '@balena/dockerignore': 1.0.2 '@types/dockerode': 3.3.29 @@ -12753,88 +14415,42 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: true - /text-decoder@1.1.1: - resolution: {integrity: sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==} + text-decoder@1.1.1: dependencies: b4a: 1.6.6 - /text-hex@1.0.0: - resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} - dev: false + text-hex@1.0.0: {} - /text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - dev: true + text-table@0.2.0: {} - /through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + through@2.3.8: {} - /tinybench@2.8.0: - resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} - dev: true + tinybench@2.8.0: {} - /tinypool@0.8.4: - resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} - engines: {node: '>=14.0.0'} - dev: true + tinypool@0.8.4: {} - /tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - dev: true + tinyspy@2.2.1: {} - /tmp@0.2.3: - resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} - engines: {node: '>=14.14'} - dev: true + tmp@0.2.3: {} - /to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} + to-fast-properties@2.0.0: {} - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - dev: true - /toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - dev: false + toidentifier@1.0.1: {} - /tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: true + tr46@0.0.3: {} - /tr46@4.1.1: - resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} - engines: {node: '>=14'} + tr46@4.1.1: dependencies: punycode: 2.3.1 - dev: false - /triple-beam@1.4.1: - resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} - engines: {node: '>= 14.0.0'} - dev: false + triple-beam@1.4.1: {} - /ts-node@10.9.2(@types/node@20.14.6)(typescript@5.4.5): - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - 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 + ts-node@10.9.2(@types/node@20.14.6)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -12851,18 +14467,12 @@ packages: typescript: 5.4.5 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - dev: true - /ts-pattern@5.2.0: - resolution: {integrity: sha512-aGaSpOlDcns7ZoeG/OMftWyQG1KqPVhgplhJxNCvyIXqWrumM5uIoOSarw/hmmi/T1PnuQ/uD8NaFHvLpHicDg==} + ts-pattern@5.2.0: {} - /ts-toolbelt@9.6.0: - resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} + ts-toolbelt@9.6.0: {} - /tsc-esm-fix@2.20.27: - resolution: {integrity: sha512-bfoSY29XN4yRvXgfxc4rtKQPe9Xx02BahWSZ3D4GgBXIWSE+TJ/BXGSrpUIBkrsKIUQv2zA3qiwJVFnUV59Xdw==} - engines: {node: '>=16.0.0'} - hasBin: true + tsc-esm-fix@2.20.27: dependencies: depseek: 0.4.1 fs-extra: 11.2.0 @@ -12870,95 +14480,49 @@ packages: json5: 2.2.3 meow: 12.1.1 tslib: 2.6.3 - dev: true - /tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 json5: 1.0.2 minimist: 1.2.8 strip-bom: 3.0.0 - dev: true - /tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@1.14.1: {} - /tslib@2.6.3: - resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + tslib@2.6.3: {} - /tsutils@3.21.0(typescript@5.4.5): - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - 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' + tsutils@3.21.0(typescript@5.4.5): dependencies: tslib: 1.14.1 typescript: 5.4.5 - dev: true - /tsx@4.19.1: - resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} - engines: {node: '>=18.0.0'} - hasBin: true + tsx@4.19.1: dependencies: esbuild: 0.23.1 get-tsconfig: 4.8.1 optionalDependencies: fsevents: 2.3.3 - dev: true - /turbo-darwin-64@2.0.4: - resolution: {integrity: sha512-x9mvmh4wudBstML8Z8IOmokLWglIhSfhQwnh2gBCSqabgVBKYvzl8Y+i+UCNPxheCGTgtsPepTcIaKBIyFIcvw==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + turbo-darwin-64@2.0.4: optional: true - /turbo-darwin-arm64@2.0.4: - resolution: {integrity: sha512-/B1Ih8zPRGVw5vw4SlclOf3C/woJ/2T6ieH6u54KT4wypoaVyaiyMqBcziIXycdObIYr7jQ+raHO7q3mhay9/A==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + turbo-darwin-arm64@2.0.4: optional: true - /turbo-linux-64@2.0.4: - resolution: {integrity: sha512-6aG670e5zOWu6RczEYcB81nEl8EhiGJEvWhUrnAfNEUIMBEH1pR5SsMmG2ol5/m3PgiRM12r13dSqTxCLcHrVg==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-64@2.0.4: optional: true - /turbo-linux-arm64@2.0.4: - resolution: {integrity: sha512-AXfVOjst+mCtPDFT4tCu08Qrfv12Nj7NDd33AjGwV79NYN1Y1rcFY59UQ4nO3ij3rbcvV71Xc+TZJ4csEvRCSg==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-arm64@2.0.4: optional: true - /turbo-windows-64@2.0.4: - resolution: {integrity: sha512-QOnUR9hKl0T5gq5h1fAhVEqBSjpcBi/BbaO71YGQNgsr6pAnCQdbG8/r3MYXet53efM0KTdOhieWeO3KLNKybA==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + turbo-windows-64@2.0.4: optional: true - /turbo-windows-arm64@2.0.4: - resolution: {integrity: sha512-3v8WpdZy1AxZw0gha0q3caZmm+0gveBQ40OspD6mxDBIS+oBtO5CkxhIXkFJJW+jDKmDlM7wXDIGfMEq+QyNCQ==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + turbo-windows-arm64@2.0.4: optional: true - /turbo@2.0.4: - resolution: {integrity: sha512-Ilme/2Q5kYw0AeRr+aw3s02+WrEYaY7U8vPnqSZU/jaDG/qd6jHVN6nRWyd/9KXvJGYM69vE6JImoGoyNjLwaw==} - hasBin: true + turbo@2.0.4: optionalDependencies: turbo-darwin-64: 2.0.4 turbo-darwin-arm64: 2.0.4 @@ -12966,68 +14530,41 @@ packages: turbo-linux-arm64: 2.0.4 turbo-windows-64: 2.0.4 turbo-windows-arm64: 2.0.4 - dev: true - /tweetnacl@0.14.5: - resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + tweetnacl@0.14.5: {} - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - dev: true - /type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - dev: true + type-detect@4.0.8: {} - /type-detect@4.1.0: - resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} - engines: {node: '>=4'} - dev: true + type-detect@4.1.0: {} - /type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - dev: true + type-fest@0.20.2: {} - /type-fest@3.13.1: - resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} - engines: {node: '>=14.16'} + type-fest@3.13.1: {} - /type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} + type-is@1.6.18: dependencies: media-typer: 0.3.0 mime-types: 2.1.35 - dev: false - /typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} + typed-array-buffer@1.0.2: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-typed-array: 1.1.13 - dev: true - /typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} - engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.1: dependencies: call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.0.1 has-proto: 1.0.3 is-typed-array: 1.1.13 - dev: true - /typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} - engines: {node: '>= 0.4'} + typed-array-byte-offset@1.0.2: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 @@ -13035,11 +14572,8 @@ packages: gopd: 1.0.1 has-proto: 1.0.3 is-typed-array: 1.1.13 - dev: true - /typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} - engines: {node: '>= 0.4'} + typed-array-length@1.0.6: dependencies: call-bind: 1.0.7 for-each: 0.3.3 @@ -13047,109 +14581,61 @@ packages: has-proto: 1.0.3 is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - dev: true - /typedarray@0.0.6: - resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - dev: false + typedarray@0.0.6: {} - /typescript@3.9.10: - resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: false + typescript@3.9.10: {} - /typescript@4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} - hasBin: true + typescript@4.9.5: {} - /typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} - engines: {node: '>=14.17'} - hasBin: true + typescript@5.4.5: {} - /ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} - dev: true + ufo@1.5.3: {} - /uglify-js@3.18.0: - resolution: {integrity: sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==} - engines: {node: '>=0.8.0'} - hasBin: true - requiresBuild: true + uglify-js@3.18.0: optional: true - /unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 - dev: true - /unbzip2-stream@1.4.3: - resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + unbzip2-stream@1.4.3: dependencies: buffer: 5.7.1 through: 2.3.8 - /undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@5.26.5: {} - /universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} + universalify@2.0.1: {} - /unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - dev: false + unpipe@1.0.0: {} - /update-browserslist-db@1.1.0(browserslist@4.23.1): - resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' + update-browserslist-db@1.1.0(browserslist@4.23.1): dependencies: browserslist: 4.23.1 escalade: 3.1.2 picocolors: 1.0.1 - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uri-js@4.4.1: dependencies: punycode: 2.3.1 - /urlpattern-polyfill@10.0.0: - resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} + urlpattern-polyfill@10.0.0: {} - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util-deprecate@1.0.2: {} - /utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} - dev: false + utils-merge@1.0.1: {} - /uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true + uuid@9.0.1: {} - /v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - dev: true + v8-compile-cache-lib@3.0.1: {} - /vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - dev: false + vary@1.1.2: {} - /vite-node@1.6.0(@types/node@20.14.6): - resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true + vite-node@1.6.0(@types/node@20.14.6): dependencies: cac: 6.7.14 debug: 4.3.5 @@ -13165,70 +14651,18 @@ packages: - sugarss - supports-color - terser - dev: true - /vite@5.3.3(@types/node@20.14.6): - resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true + vite@5.3.3(@types/node@20.14.6): dependencies: - '@types/node': 20.14.6 esbuild: 0.21.5 postcss: 8.4.39 rollup: 4.18.1 optionalDependencies: + '@types/node': 20.14.6 fsevents: 2.3.3 - dev: true - /vitest@1.6.0(@types/node@20.14.6): - resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.6.0 - '@vitest/ui': 1.6.0 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true + vitest@1.6.0(@types/node@20.14.6): dependencies: - '@types/node': 20.14.6 '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 '@vitest/snapshot': 1.6.0 @@ -13249,6 +14683,8 @@ packages: vite: 5.3.3(@types/node@20.14.6) vite-node: 1.6.0(@types/node@20.14.6) why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.14.6 transitivePeerDependencies: - less - lightningcss @@ -13257,52 +14693,35 @@ packages: - sugarss - supports-color - terser - dev: true - /webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: true + webidl-conversions@3.0.1: {} - /webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - dev: false + webidl-conversions@7.0.0: {} - /whatwg-url@13.0.0: - resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} - engines: {node: '>=16'} + whatwg-url@13.0.0: dependencies: tr46: 4.1.1 webidl-conversions: 7.0.0 - dev: false - /whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: true - /whence@2.0.1: - resolution: {integrity: sha512-VtcCE1Pe3BKofF/k+P5xcpuoqQ0f1NJY6TmdUw5kInl9/pEr1ZEFD9+ZOUicf52tvpTbhMS93aWXriu2IQYTTw==} - engines: {node: '>=14'} + whence@2.0.1: dependencies: '@babel/parser': 7.24.7 eval-estree-expression: 2.0.0 - /which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-boxed-primitive@1.0.2: dependencies: is-bigint: 1.0.4 is-boolean-object: 1.1.2 is-number-object: 1.0.7 is-string: 1.0.7 is-symbol: 1.0.4 - dev: true - /which-builtin-type@1.1.3: - resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} - engines: {node: '>= 0.4'} + which-builtin-type@1.1.3: dependencies: function.prototype.name: 1.1.6 has-tostringtag: 1.0.2 @@ -13316,58 +14735,38 @@ packages: which-boxed-primitive: 1.0.2 which-collection: 1.0.2 which-typed-array: 1.1.15 - dev: true - /which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} + which-collection@1.0.2: dependencies: is-map: 2.0.3 is-set: 2.0.3 is-weakmap: 2.0.2 is-weakset: 2.0.3 - dev: true - /which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} + which-typed-array@1.1.15: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.2 - dev: true - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + which@2.0.2: dependencies: isexe: 2.0.0 - dev: true - /why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 - dev: true - /winston-transport@4.7.0: - resolution: {integrity: sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==} - engines: {node: '>= 12.0.0'} + winston-transport@4.7.0: dependencies: logform: 2.6.0 readable-stream: 3.6.2 triple-beam: 1.4.1 - dev: false - /winston@3.13.0: - resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==} - engines: {node: '>= 12.0.0'} + winston@3.13.0: dependencies: '@colors/colors': 1.6.0 '@dabh/diagnostics': 2.0.3 @@ -13380,74 +14779,40 @@ packages: stack-trace: 0.0.10 triple-beam: 1.4.1 winston-transport: 4.7.0 - dev: false - /word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - dev: true + word-wrap@1.2.5: {} - /wordwrap@1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + wordwrap@1.0.0: {} - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - /wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} + wrap-ansi@8.1.0: dependencies: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 - dev: true - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + wrappy@1.0.2: {} - /ws@8.17.1: - resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true + ws@8.17.1: {} - /xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} + xtend@4.0.2: {} - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} + y18n@5.0.8: {} - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@3.1.1: {} - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@4.0.0: {} - /yaml@2.4.5: - resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} - engines: {node: '>= 14'} - hasBin: true + yaml@2.4.5: {} - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} + yargs-parser@21.1.1: {} - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} + yargs@17.7.2: dependencies: cliui: 8.0.1 escalade: 3.1.2 @@ -13457,44 +14822,25 @@ packages: y18n: 5.0.8 yargs-parser: 21.1.1 - /yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yauzl@2.10.0: dependencies: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 - /yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - dev: true + yn@3.1.1: {} - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: true + yocto-queue@0.1.0: {} - /yocto-queue@1.1.1: - resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} - engines: {node: '>=12.20'} - dev: true + yocto-queue@1.1.1: {} - /zip-stream@4.1.1: - resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} - engines: {node: '>= 10'} + zip-stream@4.1.1: dependencies: archiver-utils: 3.0.4 compress-commons: 4.1.2 readable-stream: 3.6.2 - dev: true - /zod-validation-error@3.3.0(zod@3.23.8): - resolution: {integrity: sha512-Syib9oumw1NTqEv4LT0e6U83Td9aVRk9iTXPUQr1otyV1PuXQKOvOwhMNqZIq5hluzHP2pMgnOmHEo7kPdI2mw==} - engines: {node: '>=18.0.0'} - peerDependencies: - zod: ^3.18.0 + zod-validation-error@3.3.0(zod@3.23.8): dependencies: zod: 3.23.8 - dev: false - /zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + zod@3.23.8: {} From 070e617244c4f209257fce2e23707214b7b57488 Mon Sep 17 00:00:00 2001 From: Simone Camito <32327779+MalpenZibo@users.noreply.github.com> Date: Tue, 5 Nov 2024 18:44:54 +0100 Subject: [PATCH 09/34] PIN 5565, 5567 - ANAC and IVASS config (#1164) --- .../anac-certified-attributes-importer/src/config/config.ts | 2 +- .../src/config/sftpConfig.ts | 2 +- packages/datalake-data-export/src/index.ts | 5 +++++ .../ivass-certified-attributes-importer/src/config/config.ts | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/anac-certified-attributes-importer/src/config/config.ts b/packages/anac-certified-attributes-importer/src/config/config.ts index bc84118087..0d429e14ec 100644 --- a/packages/anac-certified-attributes-importer/src/config/config.ts +++ b/packages/anac-certified-attributes-importer/src/config/config.ts @@ -16,7 +16,7 @@ const AnacCertifiedAttributesImporterConfig = LoggerConfig.and( z .object({ TENANT_PROCESS_URL: APIEndpoint, - RECORDS_PROCESS_BATCH_SIZE: z.number(), + RECORDS_PROCESS_BATCH_SIZE: z.coerce.number(), ANAC_TENANT_ID: z.string(), }) .transform((c) => ({ diff --git a/packages/anac-certified-attributes-importer/src/config/sftpConfig.ts b/packages/anac-certified-attributes-importer/src/config/sftpConfig.ts index a278f4cf93..9f677fc18a 100644 --- a/packages/anac-certified-attributes-importer/src/config/sftpConfig.ts +++ b/packages/anac-certified-attributes-importer/src/config/sftpConfig.ts @@ -3,7 +3,7 @@ import { z } from "zod"; export const SftpConfig = z .object({ SFTP_HOST: z.string(), - SFTP_PORT: z.coerce.number().min(1001), + SFTP_PORT: z.coerce.number(), SFTP_USERNAME: z.string(), SFTP_PASSWORD: z.string(), SFTP_FILENAME_PREFIX: z.string(), diff --git a/packages/datalake-data-export/src/index.ts b/packages/datalake-data-export/src/index.ts index 7207a7180f..eba8c07cd6 100644 --- a/packages/datalake-data-export/src/index.ts +++ b/packages/datalake-data-export/src/index.ts @@ -27,3 +27,8 @@ export const dataLakeService = datalakeServiceBuilder( log.info("Datalake Data Exporter job started"); await dataLakeService.exportData(); log.info("Done!"); + +process.exit(0); +// process.exit() should not be required. +// however, something in this script hangs on exit. +// TODO figure out why and remove this workaround. diff --git a/packages/ivass-certified-attributes-importer/src/config/config.ts b/packages/ivass-certified-attributes-importer/src/config/config.ts index f20bb85f7a..515b13177c 100644 --- a/packages/ivass-certified-attributes-importer/src/config/config.ts +++ b/packages/ivass-certified-attributes-importer/src/config/config.ts @@ -18,7 +18,7 @@ const IvassCertifiedAttributesImporterConfig = LoggerConfig.and( SOURCE_URL: z.string(), HISTORY_BUCKET_NAME: z.string(), TENANT_PROCESS_URL: APIEndpoint, - RECORDS_PROCESS_BATCH_SIZE: z.number(), + RECORDS_PROCESS_BATCH_SIZE: z.coerce.number(), IVASS_TENANT_ID: z.string(), }) .transform((c) => ({ From 6df8c3ee0fedb344954fb2d3c15c9b1f7231d4de Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Wed, 6 Nov 2024 10:46:06 +0100 Subject: [PATCH 10/34] Removing leftover TS node usages in favor of TSX (#1165) --- .../package.json | 4 +- packages/dtd-catalog-exporter/package.json | 6 +- packages/one-trust-notices/package.json | 2 +- pnpm-lock.yaml | 112 +----------------- 4 files changed, 12 insertions(+), 112 deletions(-) diff --git a/packages/agreement-platformstate-writer/package.json b/packages/agreement-platformstate-writer/package.json index 4774847616..6aecb86d35 100644 --- a/packages/agreement-platformstate-writer/package.json +++ b/packages/agreement-platformstate-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/dtd-catalog-exporter/package.json b/packages/dtd-catalog-exporter/package.json index 96d58d487c..6d8ea31ecd 100644 --- a/packages/dtd-catalog-exporter/package.json +++ b/packages/dtd-catalog-exporter/package.json @@ -11,8 +11,8 @@ "format:check": "prettier --check src", "format:write": "prettier --write src", "check": "tsc --project tsconfig.check.json", - "start:migrate": "node --loader ts-node/esm -r 'dotenv-flow/config' ./src/index.ts", - "start:verify": "node --loader ts-node/esm -r 'dotenv-flow/config' ./src/read-models-migration-check.ts", + "start:migrate": "tsx -r 'dotenv-flow/config' ./src/index.ts", + "start:verify": "tsx -r 'dotenv-flow/config' ./src/read-models-migration-check.ts", "build": "tsc" }, "keywords": [], @@ -31,7 +31,7 @@ "@types/node": "20.14.6", "eslint": "8.57.0", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" } diff --git a/packages/one-trust-notices/package.json b/packages/one-trust-notices/package.json index 73df7d6445..56e48cdff3 100644 --- a/packages/one-trust-notices/package.json +++ b/packages/one-trust-notices/package.json @@ -14,7 +14,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --watch --loader ts-node/esm ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5cf7ee4f7..3ca6b15a3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -210,9 +210,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1568,9 +1568,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -3487,10 +3487,6 @@ packages: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - '@dabh/diagnostics@2.0.3': resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} @@ -3844,9 +3840,6 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@jsdevtools/ono@7.1.3': resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} @@ -4481,18 +4474,6 @@ packages: '@tsconfig/node-lts@20.1.3': resolution: {integrity: sha512-m3b7EP2U+h5tNSpaBMfcTuHmHn04wrgRPQQrfKt75YIPq6kPs2153/KfPHdqkEWGx5pEBvS6rnvToT+yTtC1iw==} - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - '@tsconfig/strictest@2.0.5': resolution: {integrity: sha512-ec4tjL2Rr0pkZ5hww65c+EEPYwxOi4Ryv+0MtjeaSQRJyq322Q27eOQiFbuNgw2hpL4hB1/W/HBGk3VKS43osg==} @@ -4795,9 +4776,6 @@ packages: resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} engines: {node: '>= 10'} - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -5134,9 +5112,6 @@ packages: resolution: {integrity: sha512-nXvJjnfDytOOaPOonX0h0a1ggMoqrhdekGeZkD6hkcWYvlCWhU719tKFVh8eU04CnMwu3uwe1JjwuUF2C3k2qg==} engines: {node: '>=4.0.0'} - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -5264,10 +5239,6 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - diff@5.2.0: resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} engines: {node: '>=0.3.1'} @@ -6244,9 +6215,6 @@ packages: magic-string@0.30.10: resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -7179,20 +7147,6 @@ packages: resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} engines: {node: '>= 14.0.0'} - ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - 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 - ts-pattern@5.2.0: resolution: {integrity: sha512-aGaSpOlDcns7ZoeG/OMftWyQG1KqPVhgplhJxNCvyIXqWrumM5uIoOSarw/hmmi/T1PnuQ/uD8NaFHvLpHicDg==} @@ -7367,9 +7321,6 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -7543,10 +7494,6 @@ packages: yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -10145,10 +10092,6 @@ snapshots: '@colors/colors@1.6.0': {} - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - '@dabh/diagnostics@2.0.3': dependencies: colorspace: 1.1.4 @@ -10373,11 +10316,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jsdevtools/ono@7.1.3': {} '@liuli-util/fs-extra@0.1.0': @@ -11335,14 +11273,6 @@ snapshots: '@tsconfig/node-lts@20.1.3': {} - '@tsconfig/node10@1.0.11': {} - - '@tsconfig/node12@1.0.11': {} - - '@tsconfig/node14@1.0.3': {} - - '@tsconfig/node16@1.0.4': {} - '@tsconfig/strictest@2.0.5': {} '@types/adm-zip@0.5.5': @@ -11719,8 +11649,6 @@ snapshots: tar-stream: 2.2.0 zip-stream: 4.1.1 - arg@4.1.3: {} - argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -12140,8 +12068,6 @@ snapshots: dependencies: lodash.get: 4.4.2 - create-require@1.1.1: {} - cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -12245,8 +12171,6 @@ snapshots: diff-sequences@29.6.3: {} - diff@4.0.2: {} - diff@5.2.0: {} dir-glob@3.0.1: @@ -13426,8 +13350,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 - make-error@1.3.6: {} - media-typer@0.3.0: {} memory-pager@1.5.0: {} @@ -14450,24 +14372,6 @@ snapshots: triple-beam@1.4.1: {} - ts-node@10.9.2(@types/node@20.14.6)(typescript@5.4.5): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.14.6 - acorn: 8.12.1 - acorn-walk: 8.3.3 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.4.5 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - ts-pattern@5.2.0: {} ts-toolbelt@9.6.0: {} @@ -14631,8 +14535,6 @@ snapshots: uuid@9.0.1: {} - v8-compile-cache-lib@3.0.1: {} - vary@1.1.2: {} vite-node@1.6.0(@types/node@20.14.6): @@ -14827,8 +14729,6 @@ snapshots: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 - yn@3.1.1: {} - yocto-queue@0.1.0: {} yocto-queue@1.1.1: {} From 3d01766729e0af03d2d667186b89fa055d1484cd Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Wed, 6 Nov 2024 12:18:55 +0100 Subject: [PATCH 11/34] PIN-5494 - datalake interface exporter (#1145) --- ...dd a consumer Document to an Agreement.bru | 2 +- .../catalog/Add new e-service document.bru | 8 +- .../Publish the selected descriptor.bru | 6 +- docker/docker-compose.yml | 13 +- .../allow-list/allow-list.txt | 0 .../privacy-notices-path/latest/it/pp.json | 0 .../privacy-notices-path/latest/it/tos.json | 0 .../commons-test/src/containerTestUtils.ts | 5 +- packages/datalake-interface-exporter/.env | 17 +++ .../datalake-interface-exporter/.env.test | 12 ++ .../datalake-interface-exporter/Dockerfile | 45 +++++++ .../datalake-interface-exporter/README.md | 8 ++ .../aws.config.local | 4 + .../datalake-interface-exporter/package.json | 41 +++++++ .../src/config/config.ts | 31 +++++ .../datalake-interface-exporter/src/index.ts | 46 +++++++ .../src/interfaceExporter.ts | 45 +++++++ .../src/interfaceExporterV1.ts | 52 ++++++++ .../src/interfaceExporterV2.ts | 58 +++++++++ .../test/interfaceExporter.test.ts | 115 ++++++++++++++++++ .../test/tsconfig.json | 4 + .../datalake-interface-exporter/test/utils.ts | 10 ++ .../test/vitestGlobalSetup.ts | 3 + .../tsconfig.check.json | 7 ++ .../datalake-interface-exporter/tsconfig.json | 7 ++ .../vitest.config.ts | 11 ++ pnpm-lock.yaml | 49 ++++++++ 27 files changed, 582 insertions(+), 17 deletions(-) rename docker/minio-seed/{ => interop-local-bucket}/allow-list/allow-list.txt (100%) rename docker/minio-seed/{ => interop-local-bucket}/privacy-notices-path/latest/it/pp.json (100%) rename docker/minio-seed/{ => interop-local-bucket}/privacy-notices-path/latest/it/tos.json (100%) create mode 100644 packages/datalake-interface-exporter/.env create mode 100644 packages/datalake-interface-exporter/.env.test create mode 100644 packages/datalake-interface-exporter/Dockerfile create mode 100644 packages/datalake-interface-exporter/README.md create mode 100644 packages/datalake-interface-exporter/aws.config.local create mode 100644 packages/datalake-interface-exporter/package.json create mode 100644 packages/datalake-interface-exporter/src/config/config.ts create mode 100644 packages/datalake-interface-exporter/src/index.ts create mode 100644 packages/datalake-interface-exporter/src/interfaceExporter.ts create mode 100644 packages/datalake-interface-exporter/src/interfaceExporterV1.ts create mode 100644 packages/datalake-interface-exporter/src/interfaceExporterV2.ts create mode 100644 packages/datalake-interface-exporter/test/interfaceExporter.test.ts create mode 100644 packages/datalake-interface-exporter/test/tsconfig.json create mode 100644 packages/datalake-interface-exporter/test/utils.ts create mode 100644 packages/datalake-interface-exporter/test/vitestGlobalSetup.ts create mode 100644 packages/datalake-interface-exporter/tsconfig.check.json create mode 100644 packages/datalake-interface-exporter/tsconfig.json create mode 100644 packages/datalake-interface-exporter/vitest.config.ts diff --git a/collections/bff/agreements/Add a consumer Document to an Agreement.bru b/collections/bff/agreements/Add a consumer Document to an Agreement.bru index a46fcf2bf9..99cef3fefe 100644 --- a/collections/bff/agreements/Add a consumer Document to an Agreement.bru +++ b/collections/bff/agreements/Add a consumer Document to an Agreement.bru @@ -23,7 +23,7 @@ headers { body:multipart-form { name: testone 2 prettyName: testone 2 - doc: @file(/Users/ecamel/Downloads/Backend for Frontend Micro Service.postman_collection.json) + doc: @file() } docs { diff --git a/collections/bff/catalog/Add new e-service document.bru b/collections/bff/catalog/Add new e-service document.bru index 04c33c5deb..2e4eb12aba 100644 --- a/collections/bff/catalog/Add new e-service document.bru +++ b/collections/bff/catalog/Add new e-service document.bru @@ -11,8 +11,8 @@ post { } params:path { - eServiceId: 26f433e1-2c3c-4022-a14e-3c300baefc51 - descriptorId: abafb202-4f61-42bf-be2f-4efa5d4d0bc4 + eServiceId: {{eserviceId}} + descriptorId: {{descriptorId}} } headers { @@ -21,10 +21,8 @@ headers { Content-Type: multipart/form-data } - - body:multipart-form { kind: INTERFACE prettyName: asdasd - doc: @file(/Users/ecamel/Downloads/Interfaccia.yaml) + doc: @file() } diff --git a/collections/bff/catalog/Publish the selected descriptor.bru b/collections/bff/catalog/Publish the selected descriptor.bru index 948be07dcd..2210cb63e5 100644 --- a/collections/bff/catalog/Publish the selected descriptor.bru +++ b/collections/bff/catalog/Publish the selected descriptor.bru @@ -11,13 +11,11 @@ post { } params:path { - eServiceId: - descriptorId: + eServiceId: {{eserviceId}} + descriptorId: {{descriptorId}} } headers { Authorization: {{JWT}} x-correlation-id: {{correlation-id}} } - - diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 8a84f69746..7f338ba38e 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -152,11 +152,9 @@ services: # MinIO is a local S3-compatible storage, to replace AWS S3 in local development minio: - image: quay.io/minio/minio:RELEASE.2024-02-06T21-36-22Z - entrypoint: sh - command: > - -c 'mkdir -p /data/interop-local-bucket && - /usr/bin/minio server /data --console-address ":9001"' + image: minio/minio:RELEASE.2024-02-06T21-36-22Z + entrypoint: > + /usr/bin/minio server /data --console-address ':9001' ports: - 9000:9000 - 9001:9001 @@ -178,8 +176,11 @@ services: mc alias set minio http://minio:9000 testawskey testawssecret; mc ready minio; echo 'MinIO is ready. Seeding data...'; - mc cp --recursive data/ minio/interop-local-bucket/; + mc mb minio/interop-local-bucket || true; mc mb minio/interop-datalake-bucket || true; + mc mb minio/interop-application-import-export-local || true; + mc mb minio/interop-data-lake-interfaces-exports-local-es1 || true; + mc cp --recursive data/ minio/; " volumes: - ./minio-seed:/data diff --git a/docker/minio-seed/allow-list/allow-list.txt b/docker/minio-seed/interop-local-bucket/allow-list/allow-list.txt similarity index 100% rename from docker/minio-seed/allow-list/allow-list.txt rename to docker/minio-seed/interop-local-bucket/allow-list/allow-list.txt diff --git a/docker/minio-seed/privacy-notices-path/latest/it/pp.json b/docker/minio-seed/interop-local-bucket/privacy-notices-path/latest/it/pp.json similarity index 100% rename from docker/minio-seed/privacy-notices-path/latest/it/pp.json rename to docker/minio-seed/interop-local-bucket/privacy-notices-path/latest/it/pp.json diff --git a/docker/minio-seed/privacy-notices-path/latest/it/tos.json b/docker/minio-seed/interop-local-bucket/privacy-notices-path/latest/it/tos.json similarity index 100% rename from docker/minio-seed/privacy-notices-path/latest/it/tos.json rename to docker/minio-seed/interop-local-bucket/privacy-notices-path/latest/it/tos.json diff --git a/packages/commons-test/src/containerTestUtils.ts b/packages/commons-test/src/containerTestUtils.ts index 3f76738c80..39a122d369 100644 --- a/packages/commons-test/src/containerTestUtils.ts +++ b/packages/commons-test/src/containerTestUtils.ts @@ -89,7 +89,10 @@ export const minioContainer = (config: S3Config): GenericContainer => }) .withEntrypoint(["sh", "-c"]) .withCommand([ - `mkdir -p /data/${config.s3Bucket} && /usr/bin/minio server /data`, + `mkdir -p /data/${config.s3Bucket} && + mkdir -p /data/test-bucket-1 && + mkdir -p /data/test-bucket-2 && + /usr/bin/minio server /data`, ]) .withExposedPorts(TEST_MINIO_PORT); diff --git a/packages/datalake-interface-exporter/.env b/packages/datalake-interface-exporter/.env new file mode 100644 index 0000000000..91570c3d23 --- /dev/null +++ b/packages/datalake-interface-exporter/.env @@ -0,0 +1,17 @@ +LOG_LEVEL=info + +KAFKA_CLIENT_ID="catalog" +KAFKA_GROUP_ID="catalog-group-local" +KAFKA_BROKERS="localhost:9092" +KAFKA_DISABLE_AWS_IAM_AUTH="true" +CATALOG_TOPIC="event-store.catalog.events" + +S3_CUSTOM_SERVER=true +S3_SERVER_HOST=http://localhost +S3_SERVER_PORT=9000 + +AWS_REGION=eu-south-1 +AWS_CONFIG_FILE=aws.config.local + +ESERVICE_DOCUMENTS_S3_BUCKET=interop-local-bucket +DATALAKE_INTERFACES_EXPORT_S3_BUCKET=interop-data-lake-interfaces-exports-local-es1 diff --git a/packages/datalake-interface-exporter/.env.test b/packages/datalake-interface-exporter/.env.test new file mode 100644 index 0000000000..829c6c2d55 --- /dev/null +++ b/packages/datalake-interface-exporter/.env.test @@ -0,0 +1,12 @@ + + +# Needed by the test infrastructure to start the Minio testcontainer +LOG_LEVEL=info +S3_CUSTOM_SERVER=true +S3_SERVER_HOST=http://localhost +S3_SERVER_PORT=9000 +S3_BUCKET=test-bucket + +# We set these two to the names of the two test buckets created by default by the test infra +ESERVICE_DOCUMENTS_S3_BUCKET=test-bucket-1 +DATALAKE_INTERFACES_EXPORT_S3_BUCKET=test-bucket-2 diff --git a/packages/datalake-interface-exporter/Dockerfile b/packages/datalake-interface-exporter/Dockerfile new file mode 100644 index 0000000000..dcd696ea52 --- /dev/null +++ b/packages/datalake-interface-exporter/Dockerfile @@ -0,0 +1,45 @@ +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ + +COPY ./packages/datalake-interface-exporter/package.json /app/packages/datalake-interface-exporter/package.json +COPY ./packages/commons/package.json /app/packages/commons/package.json +COPY ./packages/models/package.json /app/packages/models/package.json +COPY ./packages/kafka-iam-auth/package.json /app/packages/kafka-iam-auth/package.json + +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile + +COPY tsconfig.json /app/ +COPY turbo.json /app/ +COPY ./packages/datalake-interface-exporter /app/packages/datalake-interface-exporter +COPY ./packages/commons /app/packages/commons +COPY ./packages/models /app/packages/models +COPY ./packages/kafka-iam-auth /app/packages/kafka-iam-auth + +RUN pnpm build && \ + rm -rf /app/node_modules/.modules.yaml && \ + rm -rf /app/node_modules/.cache && \ + mkdir /out && \ + cp -a --parents -t /out \ + node_modules packages/datalake-interface-exporter/node_modules \ + package*.json packages/datalake-interface-exporter/package*.json \ + packages/commons \ + packages/models \ + packages/kafka-iam-auth \ + packages/datalake-interface-exporter/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final + +COPY --from=build /out /app + +WORKDIR /app/packages/datalake-interface-exporter +EXPOSE 3000 + +CMD ["node", "."] diff --git a/packages/datalake-interface-exporter/README.md b/packages/datalake-interface-exporter/README.md new file mode 100644 index 0000000000..4dbfd20ccc --- /dev/null +++ b/packages/datalake-interface-exporter/README.md @@ -0,0 +1,8 @@ +# Datalake Interface Exporter + +This consumer: + +- listens for events that publish a new version of an eservice +- copies the new interface file for the new version to the target S3 bucket + +This target S3 bucket replicates the files to a bucket owned by Datalake. diff --git a/packages/datalake-interface-exporter/aws.config.local b/packages/datalake-interface-exporter/aws.config.local new file mode 100644 index 0000000000..34826a60e2 --- /dev/null +++ b/packages/datalake-interface-exporter/aws.config.local @@ -0,0 +1,4 @@ +[default] +aws_access_key_id=testawskey +aws_secret_access_key=testawssecret +region=eu-south-1 diff --git a/packages/datalake-interface-exporter/package.json b/packages/datalake-interface-exporter/package.json new file mode 100644 index 0000000000..c206fe54a2 --- /dev/null +++ b/packages/datalake-interface-exporter/package.json @@ -0,0 +1,41 @@ +{ + "name": "pagopa-interop-datalake-interface-exporter", + "private": true, + "version": "1.0.0", + "description": "PagoPA Interoperability Datalake Eservice interface exporter", + "main": "dist", + "type": "module", + "scripts": { + "test": "vitest", + "test:it": "vitest integration", + "lint": "eslint . --ext .ts,.tsx", + "lint:autofix": "eslint . --ext .ts,.tsx --fix", + "format:check": "prettier --check src", + "format:write": "prettier --write src", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@pagopa/eslint-config": "3.0.0", + "@types/node": "20.14.6", + "prettier": "2.8.8", + "testcontainers": "10.9.0", + "tsx": "4.19.1", + "typescript": "5.4.5", + "vitest": "1.6.0" + }, + "dependencies": { + "dotenv-flow": "4.1.0", + "kafka-iam-auth": "workspace:*", + "kafkajs": "2.2.4", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-commons-test": "workspace:*", + "pagopa-interop-models": "workspace:*", + "ts-pattern": "5.2.0", + "zod": "3.23.8" + } +} diff --git a/packages/datalake-interface-exporter/src/config/config.ts b/packages/datalake-interface-exporter/src/config/config.ts new file mode 100644 index 0000000000..54bea1d948 --- /dev/null +++ b/packages/datalake-interface-exporter/src/config/config.ts @@ -0,0 +1,31 @@ +import { + CatalogTopicConfig, + FileManagerConfig, + KafkaConsumerConfig, + LoggerConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +export const DatalakeInterfaceExporterConfig = LoggerConfig.and( + FileManagerConfig +) + .and(KafkaConsumerConfig) + .and(CatalogTopicConfig) + .and( + z + .object({ + ESERVICE_DOCUMENTS_S3_BUCKET: z.string(), + DATALAKE_INTERFACES_EXPORT_S3_BUCKET: z.string(), + }) + .transform((c) => ({ + eserviceDocumentsS3Bucket: c.ESERVICE_DOCUMENTS_S3_BUCKET, + datalakeInterfacesExportS3Bucket: + c.DATALAKE_INTERFACES_EXPORT_S3_BUCKET, + })) + ); + +export type DatalakeInterfaceExporterConfig = z.infer< + typeof DatalakeInterfaceExporterConfig +>; +export const config: DatalakeInterfaceExporterConfig = + DatalakeInterfaceExporterConfig.parse(process.env); diff --git a/packages/datalake-interface-exporter/src/index.ts b/packages/datalake-interface-exporter/src/index.ts new file mode 100644 index 0000000000..81dcc68b00 --- /dev/null +++ b/packages/datalake-interface-exporter/src/index.ts @@ -0,0 +1,46 @@ +/* eslint-disable functional/immutable-data */ +import { runConsumer } from "kafka-iam-auth"; +import { EachMessagePayload } from "kafkajs"; +import { + decodeKafkaMessage, + initFileManager, + logger, +} from "pagopa-interop-commons"; +import { + CorrelationId, + EServiceEvent, + generateId, + unsafeBrandId, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { config } from "./config/config.js"; +import { exportInterfaceV1 } from "./interfaceExporterV1.js"; +import { exportInterfaceV2 } from "./interfaceExporterV2.js"; + +const fileManager = initFileManager(config); + +async function processMessage(payload: EachMessagePayload): Promise { + const decodedMsg = decodeKafkaMessage(payload.message, EServiceEvent); + const correlationId: CorrelationId = decodedMsg.correlation_id + ? unsafeBrandId(decodedMsg.correlation_id) + : generateId(); + + const loggerInstance = logger({ + serviceName: "datalake-interface-exporter", + eventType: decodedMsg.type, + eventVersion: decodedMsg.event_version, + streamId: decodedMsg.stream_id, + correlationId, + }); + + await match(decodedMsg) + .with({ event_version: 1 }, (msg) => + exportInterfaceV1(msg, payload, fileManager, loggerInstance) + ) + .with({ event_version: 2 }, (msg) => + exportInterfaceV2(msg, payload, fileManager, loggerInstance) + ) + .exhaustive(); +} + +await runConsumer(config, [config.catalogTopic], processMessage); diff --git a/packages/datalake-interface-exporter/src/interfaceExporter.ts b/packages/datalake-interface-exporter/src/interfaceExporter.ts new file mode 100644 index 0000000000..9afaae41df --- /dev/null +++ b/packages/datalake-interface-exporter/src/interfaceExporter.ts @@ -0,0 +1,45 @@ +import { FileManager, Logger } from "pagopa-interop-commons"; +import { + Descriptor, + EServiceId, + genericInternalError, +} from "pagopa-interop-models"; +import { config } from "./config/config.js"; + +export async function exportInterface( + eserviceId: EServiceId, + latestDescriptor: Descriptor, + fileManager: FileManager, + logger: Logger +): Promise { + logger.info( + `Exporting Interface for EService ${eserviceId} and Descriptor ${latestDescriptor.id}` + ); + + if (latestDescriptor.interface === undefined) { + throw genericInternalError( + `Published Descriptor ${latestDescriptor.id} does not have an interface` + ); + } + + const interfaceFile = await fileManager.get( + config.eserviceDocumentsS3Bucket, + latestDescriptor.interface.path, + logger + ); + + await fileManager.storeBytes( + { + bucket: config.datalakeInterfacesExportS3Bucket, + path: eserviceId, + resourceId: latestDescriptor.id, + name: latestDescriptor.interface.name, + content: Buffer.from(interfaceFile), + }, + logger + ); + + logger.info( + `Interface ${latestDescriptor.interface.name} for Eservice ${eserviceId} and Descriptor ${latestDescriptor.id} exported successfully` + ); +} diff --git a/packages/datalake-interface-exporter/src/interfaceExporterV1.ts b/packages/datalake-interface-exporter/src/interfaceExporterV1.ts new file mode 100644 index 0000000000..466a4014b5 --- /dev/null +++ b/packages/datalake-interface-exporter/src/interfaceExporterV1.ts @@ -0,0 +1,52 @@ +import { FileManager, Logger } from "pagopa-interop-commons"; +import { + descriptorState, + EServiceEventEnvelopeV1, + fromDescriptorV1, + unsafeBrandId, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { EachMessagePayload } from "kafkajs"; +import { exportInterface } from "./interfaceExporter.js"; + +export async function exportInterfaceV1( + decodedMsg: EServiceEventEnvelopeV1, + originalPayload: EachMessagePayload, + fileManager: FileManager, + logger: Logger +): Promise { + await match(decodedMsg) + .with({ type: "EServiceDescriptorUpdated" }, async ({ data }) => { + if (data.eserviceDescriptor) { + logger.info( + `Processing ${decodedMsg.type} message - Partition number: ${originalPayload.partition} - Offset: ${originalPayload.message.offset}` + ); + const updatedDescriptor = fromDescriptorV1(data.eserviceDescriptor); + if (updatedDescriptor.state === descriptorState.published) { + await exportInterface( + unsafeBrandId(data.eserviceId), + updatedDescriptor, + fileManager, + logger + ); + } + } + }) + .with( + { type: "EServiceAdded" }, + { type: "ClonedEServiceAdded" }, + { type: "EServiceUpdated" }, + { type: "EServiceWithDescriptorsDeleted" }, + { type: "EServiceDocumentUpdated" }, + { type: "EServiceDeleted" }, + { type: "EServiceDocumentAdded" }, + { type: "EServiceDocumentDeleted" }, + { type: "EServiceDescriptorAdded" }, + { type: "MovedAttributesFromEserviceToDescriptors" }, + { type: "EServiceRiskAnalysisAdded" }, + { type: "EServiceRiskAnalysisUpdated" }, + { type: "EServiceRiskAnalysisDeleted" }, + () => undefined + ) + .exhaustive(); +} diff --git a/packages/datalake-interface-exporter/src/interfaceExporterV2.ts b/packages/datalake-interface-exporter/src/interfaceExporterV2.ts new file mode 100644 index 0000000000..da5151180c --- /dev/null +++ b/packages/datalake-interface-exporter/src/interfaceExporterV2.ts @@ -0,0 +1,58 @@ +import { EServiceEventEnvelopeV2, fromEServiceV2 } from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { FileManager, Logger } from "pagopa-interop-commons"; +import { EachMessagePayload } from "kafkajs"; +import { exportInterface } from "./interfaceExporter.js"; + +export async function exportInterfaceV2( + decodedMsg: EServiceEventEnvelopeV2, + originalPayload: EachMessagePayload, + fileManager: FileManager, + logger: Logger +): Promise { + await match(decodedMsg) + .with({ type: "EServiceDescriptorPublished" }, async ({ data }) => { + if (data.eservice) { + logger.info( + `Processing ${decodedMsg.type} message - Partition number: ${originalPayload.partition} - Offset: ${originalPayload.message.offset}` + ); + const eservice = fromEServiceV2(data.eservice); + const publishedDescriptor = eservice.descriptors.find( + (d) => d.id === data.descriptorId + ); + if (publishedDescriptor) { + await exportInterface( + eservice.id, + publishedDescriptor, + fileManager, + logger + ); + } + } + }) + .with( + { type: "EServiceAdded" }, + { type: "DraftEServiceUpdated" }, + { type: "EServiceDeleted" }, + { type: "EServiceCloned" }, + { type: "EServiceDescriptorAdded" }, + { type: "EServiceDraftDescriptorUpdated" }, + { type: "EServiceDescriptorQuotasUpdated" }, + { type: "EServiceDescriptorActivated" }, + { type: "EServiceDescriptorArchived" }, + { type: "EServiceDescriptorSuspended" }, + { type: "EServiceDraftDescriptorDeleted" }, + { type: "EServiceDescriptorInterfaceAdded" }, + { type: "EServiceDescriptorDocumentAdded" }, + { type: "EServiceDescriptorInterfaceUpdated" }, + { type: "EServiceDescriptorDocumentUpdated" }, + { type: "EServiceDescriptorInterfaceDeleted" }, + { type: "EServiceDescriptorDocumentDeleted" }, + { type: "EServiceRiskAnalysisAdded" }, + { type: "EServiceRiskAnalysisUpdated" }, + { type: "EServiceRiskAnalysisDeleted" }, + { type: "EServiceDescriptionUpdated" }, + () => undefined + ) + .exhaustive(); +} diff --git a/packages/datalake-interface-exporter/test/interfaceExporter.test.ts b/packages/datalake-interface-exporter/test/interfaceExporter.test.ts new file mode 100644 index 0000000000..1b466a6f5b --- /dev/null +++ b/packages/datalake-interface-exporter/test/interfaceExporter.test.ts @@ -0,0 +1,115 @@ +import { describe, expect, it } from "vitest"; +import { + getMockDescriptor, + getMockDocument, +} from "pagopa-interop-commons-test"; +import { + Descriptor, + EServiceDocumentId, + EServiceId, + generateId, +} from "pagopa-interop-models"; +import { genericLogger } from "pagopa-interop-commons"; +import { config } from "../src/config/config.js"; +import { exportInterface } from "../src/interfaceExporter.js"; +import { fileManager } from "./utils.js"; + +describe("interfaceExporter", () => { + it("should export interface files for a descriptor", async () => { + const mockEserviceId1 = generateId(); + const mockDocumentId1 = generateId(); + const mockDocument1 = { + ...getMockDocument(), + id: mockDocumentId1, + name: "document1.json", + path: `interop-eservice-documents/${mockDocumentId1}/document1.json`, + }; + const mockDescriptor1: Descriptor = { + ...getMockDescriptor(), + interface: mockDocument1, + }; + + const mockEserviceId2 = generateId(); + const mockDocumentId2 = generateId(); + const mockDocument2 = { + ...getMockDocument(), + id: mockDocumentId2, + name: "document2.txt", + path: `interop-eservice-documents/${mockDocumentId2}/document2.txt`, + }; + const mockDescriptor2: Descriptor = { + ...getMockDescriptor(), + interface: mockDocument2, + }; + + await fileManager.storeBytes( + { + bucket: config.eserviceDocumentsS3Bucket, + path: "interop-eservice-documents", + resourceId: mockDocument1.id, + name: mockDocument1.name, + content: Buffer.from("test-content"), + }, + genericLogger + ); + + await fileManager.storeBytes( + { + bucket: config.eserviceDocumentsS3Bucket, + path: "interop-eservice-documents", + resourceId: mockDocument2.id, + name: mockDocument2.name, + content: Buffer.from("test-content"), + }, + genericLogger + ); + + await exportInterface( + mockEserviceId1, + mockDescriptor1, + fileManager, + genericLogger + ); + + await exportInterface( + mockEserviceId2, + mockDescriptor2, + fileManager, + genericLogger + ); + + expect( + await fileManager.listFiles( + config.datalakeInterfacesExportS3Bucket, + genericLogger + ) + ).toContain( + `${mockEserviceId1}/${mockDescriptor1.id}/${mockDocument1.name}` + ); + + expect( + await fileManager.listFiles( + config.datalakeInterfacesExportS3Bucket, + genericLogger + ) + ).toContain( + `${mockEserviceId2}/${mockDescriptor2.id}/${mockDocument2.name}` + ); + }); + + it("should fail if descriptor does not have an interface", async () => { + const mockEserviceId = generateId(); + const mockDescriptor: Descriptor = { + ...getMockDescriptor(), + interface: undefined, + }; + await expect( + exportInterface( + mockEserviceId, + mockDescriptor, + fileManager, + genericLogger + ) + ).rejects.toThrow(); + }); +}); diff --git a/packages/datalake-interface-exporter/test/tsconfig.json b/packages/datalake-interface-exporter/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/datalake-interface-exporter/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/datalake-interface-exporter/test/utils.ts b/packages/datalake-interface-exporter/test/utils.ts new file mode 100644 index 0000000000..45cae9e84c --- /dev/null +++ b/packages/datalake-interface-exporter/test/utils.ts @@ -0,0 +1,10 @@ +import { setupTestContainersVitest } from "pagopa-interop-commons-test"; +import { afterEach, inject } from "vitest"; + +export const { cleanup, fileManager } = await setupTestContainersVitest( + undefined, + undefined, + inject("fileManagerConfig") +); + +afterEach(cleanup); diff --git a/packages/datalake-interface-exporter/test/vitestGlobalSetup.ts b/packages/datalake-interface-exporter/test/vitestGlobalSetup.ts new file mode 100644 index 0000000000..32972b5c32 --- /dev/null +++ b/packages/datalake-interface-exporter/test/vitestGlobalSetup.ts @@ -0,0 +1,3 @@ +import { setupTestContainersVitestGlobal } from "pagopa-interop-commons-test"; + +export default setupTestContainersVitestGlobal(); diff --git a/packages/datalake-interface-exporter/tsconfig.check.json b/packages/datalake-interface-exporter/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/datalake-interface-exporter/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/datalake-interface-exporter/tsconfig.json b/packages/datalake-interface-exporter/tsconfig.json new file mode 100644 index 0000000000..039e0b4d16 --- /dev/null +++ b/packages/datalake-interface-exporter/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/packages/datalake-interface-exporter/vitest.config.ts b/packages/datalake-interface-exporter/vitest.config.ts new file mode 100644 index 0000000000..9ece1be991 --- /dev/null +++ b/packages/datalake-interface-exporter/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globalSetup: ["./test/vitestGlobalSetup.ts"], + testTimeout: 60000, + hookTimeout: 60000, + fileParallelism: false, + pool: "forks", + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3ca6b15a3d..a6da4af8b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1535,6 +1535,55 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) + packages/datalake-interface-exporter: + dependencies: + dotenv-flow: + specifier: 4.1.0 + version: 4.1.0 + kafka-iam-auth: + specifier: workspace:* + version: link:../kafka-iam-auth + kafkajs: + specifier: 2.2.4 + version: 2.2.4 + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + pagopa-interop-models: + specifier: workspace:* + version: link:../models + ts-pattern: + specifier: 5.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + prettier: + specifier: 2.8.8 + version: 2.8.8 + testcontainers: + specifier: 10.9.0 + version: 10.9.0 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + packages/dtd-catalog-exporter: dependencies: dotenv-flow: From 3cbbd8071c3c6697ddd1d327683360aa3a069620 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 6 Nov 2024 13:04:21 +0100 Subject: [PATCH 12/34] IMN-793 Add scaffold for authorization-platformstate-writer (#1051) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../authorization-platformstate-writer/.env | 12 +++ .../Dockerfile | 45 +++++++++++ .../aws.config.local | 9 +++ .../package.json | 47 +++++++++++ .../src/config/config.ts | 15 ++++ .../src/consumerServiceV1.ts | 26 ++++++ .../src/consumerServiceV2.ts | 32 ++++++++ .../src/index.ts | 43 ++++++++++ .../test/sample.integration.test.ts | 46 +++++++++++ .../test/tsconfig.json | 4 + .../test/utils.ts | 3 + .../test/vitestGlobalSetup.ts | 3 + .../tsconfig.check.json | 7 ++ .../tsconfig.json | 9 +++ .../vitest.config.ts | 11 +++ packages/models/src/index.ts | 2 +- .../{dynamoDB-keys.ts => commons.ts} | 11 +++ .../platform-states-entry.ts | 3 + .../token-generation-states-entry.ts | 11 +-- pnpm-lock.yaml | 81 ++++++++++++++++++- 20 files changed, 408 insertions(+), 12 deletions(-) create mode 100644 packages/authorization-platformstate-writer/.env create mode 100644 packages/authorization-platformstate-writer/Dockerfile create mode 100644 packages/authorization-platformstate-writer/aws.config.local create mode 100644 packages/authorization-platformstate-writer/package.json create mode 100644 packages/authorization-platformstate-writer/src/config/config.ts create mode 100644 packages/authorization-platformstate-writer/src/consumerServiceV1.ts create mode 100644 packages/authorization-platformstate-writer/src/consumerServiceV2.ts create mode 100644 packages/authorization-platformstate-writer/src/index.ts create mode 100644 packages/authorization-platformstate-writer/test/sample.integration.test.ts create mode 100644 packages/authorization-platformstate-writer/test/tsconfig.json create mode 100644 packages/authorization-platformstate-writer/test/utils.ts create mode 100644 packages/authorization-platformstate-writer/test/vitestGlobalSetup.ts create mode 100644 packages/authorization-platformstate-writer/tsconfig.check.json create mode 100644 packages/authorization-platformstate-writer/tsconfig.json create mode 100644 packages/authorization-platformstate-writer/vitest.config.ts rename packages/models/src/token-generation-readmodel/{dynamoDB-keys.ts => commons.ts} (88%) diff --git a/packages/authorization-platformstate-writer/.env b/packages/authorization-platformstate-writer/.env new file mode 100644 index 0000000000..def5bafc7f --- /dev/null +++ b/packages/authorization-platformstate-writer/.env @@ -0,0 +1,12 @@ +LOG_LEVEL=info + +KAFKA_CLIENT_ID="authorization" +KAFKA_GROUP_ID="authorization-group-local" +KAFKA_BROKERS="localhost:9092" +KAFKA_DISABLE_AWS_IAM_AUTH="true" +AUTHORIZATION_TOPIC="event-store.authorization.events" +AWS_CONFIG_FILE=aws.config.local +TOKEN_GENERATION_READMODEL_TABLE_NAME_PLATFORM="platform-states" +TOKEN_GENERATION_READMODEL_TABLE_NAME_TOKEN_GENERATION="token-generation-states" + +AWS_REGION="eu-south-1" diff --git a/packages/authorization-platformstate-writer/Dockerfile b/packages/authorization-platformstate-writer/Dockerfile new file mode 100644 index 0000000000..9d527f08bb --- /dev/null +++ b/packages/authorization-platformstate-writer/Dockerfile @@ -0,0 +1,45 @@ +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ + +COPY ./packages/authorization-platformstate-writer/package.json /app/packages/authorization-platformstate-writer/package.json +COPY ./packages/commons/package.json /app/packages/commons/package.json +COPY ./packages/models/package.json /app/packages/models/package.json +COPY ./packages/kafka-iam-auth/package.json /app/packages/kafka-iam-auth/package.json + +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile + +COPY tsconfig.json /app/ +COPY turbo.json /app/ +COPY ./packages/authorization-platformstate-writer /app/packages/authorization-platformstate-writer +COPY ./packages/commons /app/packages/commons +COPY ./packages/models /app/packages/models +COPY ./packages/kafka-iam-auth /app/packages/kafka-iam-auth + +RUN pnpm build && \ + rm -rf /app/node_modules/.modules.yaml && \ + rm -rf /app/node_modules/.cache && \ + mkdir /out && \ + cp -a --parents -t /out \ + node_modules packages/authorization-platformstate-writer/node_modules \ + package*.json packages/authorization-platformstate-writer/package*.json \ + packages/commons \ + packages/models \ + packages/kafka-iam-auth \ + packages/authorization-platformstate-writer/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final + +COPY --from=build /out /app + +WORKDIR /app/packages/authorization-platformstate-writer +EXPOSE 3000 + +CMD ["node", "."] diff --git a/packages/authorization-platformstate-writer/aws.config.local b/packages/authorization-platformstate-writer/aws.config.local new file mode 100644 index 0000000000..f3016f81d6 --- /dev/null +++ b/packages/authorization-platformstate-writer/aws.config.local @@ -0,0 +1,9 @@ +[default] +aws_access_key_id=key +aws_secret_access_key=secret +region=eu-south-1 +services=local + +[services local] +dynamodb= + endpoint_url=http://localhost:8085 diff --git a/packages/authorization-platformstate-writer/package.json b/packages/authorization-platformstate-writer/package.json new file mode 100644 index 0000000000..6106ef0a72 --- /dev/null +++ b/packages/authorization-platformstate-writer/package.json @@ -0,0 +1,47 @@ +{ + "name": "pagopa-interop-authorization-platformstate-writer", + "private": true, + "version": "1.0.0", + "description": "PagoPA Interoperability authorization consumer service that updates the token-generation-read-model", + "main": "dist", + "type": "module", + "scripts": { + "test": "vitest", + "test:it": "vitest integration", + "lint": "eslint . --ext .ts,.tsx", + "lint:autofix": "eslint . --ext .ts,.tsx --fix", + "format:check": "prettier --check src", + "format:write": "prettier --write src", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@pagopa/eslint-config": "3.0.0", + "@types/node": "20.14.6", + "@types/uuid": "9.0.8", + "date-fns": "3.6.0", + "pagopa-interop-commons-test": "workspace:*", + "prettier": "2.8.8", + "tsx": "4.19.1", + "typescript": "5.4.5", + "uuid": "10.0.0", + "vitest": "1.6.0" + }, + "dependencies": { + "@aws-sdk/client-dynamodb": "3.637.0", + "@aws-sdk/util-dynamodb": "3.637.0", + "@protobuf-ts/runtime": "2.9.4", + "connection-string": "4.4.0", + "dotenv-flow": "4.1.0", + "kafka-iam-auth": "workspace:*", + "kafkajs": "2.2.4", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "ts-pattern": "5.2.0", + "zod": "3.23.8" + } +} diff --git a/packages/authorization-platformstate-writer/src/config/config.ts b/packages/authorization-platformstate-writer/src/config/config.ts new file mode 100644 index 0000000000..3258ca8188 --- /dev/null +++ b/packages/authorization-platformstate-writer/src/config/config.ts @@ -0,0 +1,15 @@ +import { + AuthorizationTopicConfig, + PlatformStateWriterConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +export const AuthorizationPlatformStateWriterConfig = + PlatformStateWriterConfig.and(AuthorizationTopicConfig); + +export type AuthorizationPlatformStateWriterConfig = z.infer< + typeof AuthorizationPlatformStateWriterConfig +>; + +export const config: AuthorizationPlatformStateWriterConfig = + AuthorizationPlatformStateWriterConfig.parse(process.env); diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts new file mode 100644 index 0000000000..78b5d36e02 --- /dev/null +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -0,0 +1,26 @@ +import { match } from "ts-pattern"; +import { AuthorizationEventEnvelopeV1 } from "pagopa-interop-models"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; + +export async function handleMessageV1( + message: AuthorizationEventEnvelopeV1, + _dynamoDBClient: DynamoDBClient +): Promise { + await match(message) + .with( + { type: "ClientAdded" }, + { type: "ClientDeleted" }, + { type: "ClientPurposeAdded" }, + { type: "ClientPurposeRemoved" }, + { type: "KeyDeleted" }, + { type: "KeyRelationshipToUserMigrated" }, + { type: "KeysAdded" }, + { type: "RelationshipAdded" }, + { type: "RelationshipAdded" }, + { type: "RelationshipRemoved" }, + { type: "UserAdded" }, + { type: "UserRemoved" }, + async () => Promise.resolve() + ) + .exhaustive(); +} diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts new file mode 100644 index 0000000000..4ad0fd25ab --- /dev/null +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -0,0 +1,32 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { AuthorizationEventEnvelopeV2 } from "pagopa-interop-models"; +import { match } from "ts-pattern"; + +export async function handleMessageV2( + message: AuthorizationEventEnvelopeV2, + _dynamoDBClient: DynamoDBClient +): Promise { + await match(message) + .with( + { type: "ClientAdded" }, + { type: "ClientDeleted" }, + { type: "ClientKeyAdded" }, + { type: "ClientKeyDeleted" }, + { type: "ClientPurposeAdded" }, + { type: "ClientPurposeRemoved" }, + { type: "ClientUserAdded" }, + { type: "ClientUserDeleted" }, + { type: "ProducerKeychainAdded" }, + { type: "ProducerKeychainDeleted" }, + { type: "ProducerKeychainEServiceAdded" }, + { type: "ProducerKeychainEServiceRemoved" }, + { type: "ProducerKeychainEServiceAdded" }, + { type: "ProducerKeychainEServiceRemoved" }, + { type: "ProducerKeychainKeyAdded" }, + { type: "ProducerKeychainKeyDeleted" }, + { type: "ProducerKeychainUserAdded" }, + { type: "ProducerKeychainUserDeleted" }, + () => Promise.resolve() + ) + .exhaustive(); +} diff --git a/packages/authorization-platformstate-writer/src/index.ts b/packages/authorization-platformstate-writer/src/index.ts new file mode 100644 index 0000000000..661f322d2d --- /dev/null +++ b/packages/authorization-platformstate-writer/src/index.ts @@ -0,0 +1,43 @@ +import { EachMessagePayload } from "kafkajs"; +import { logger, decodeKafkaMessage } from "pagopa-interop-commons"; +import { runConsumer } from "kafka-iam-auth"; +import { + AuthorizationEvent, + CorrelationId, + generateId, + unsafeBrandId, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { handleMessageV1 } from "./consumerServiceV1.js"; +import { handleMessageV2 } from "./consumerServiceV2.js"; +import { config } from "./config/config.js"; + +const dynamoDBClient = new DynamoDBClient({}); +async function processMessage({ + message, + partition, +}: EachMessagePayload): Promise { + const decodedMessage = decodeKafkaMessage(message, AuthorizationEvent); + + const loggerInstance = logger({ + serviceName: "authorization-platformstate-writer", + eventType: decodedMessage.type, + eventVersion: decodedMessage.event_version, + streamId: decodedMessage.stream_id, + correlationId: decodedMessage.correlation_id + ? unsafeBrandId(decodedMessage.correlation_id) + : generateId(), + }); + + await match(decodedMessage) + .with({ event_version: 1 }, (msg) => handleMessageV1(msg, dynamoDBClient)) + .with({ event_version: 2 }, (msg) => handleMessageV2(msg, dynamoDBClient)) + .exhaustive(); + + loggerInstance.info( + `Token-generation read model was updated. Partition number: ${partition}. Offset: ${message.offset}` + ); +} + +await runConsumer(config, [config.authorizationTopic], processMessage); diff --git a/packages/authorization-platformstate-writer/test/sample.integration.test.ts b/packages/authorization-platformstate-writer/test/sample.integration.test.ts new file mode 100644 index 0000000000..a4e96fa400 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/sample.integration.test.ts @@ -0,0 +1,46 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { fail } from "assert"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, +} from "pagopa-interop-commons-test"; +import { config } from "./utils.js"; + +describe("integration tests V2 events", async () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + const mockDate = new Date(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + it("sample", () => { + expect(1).toBe(1); + }); +}); diff --git a/packages/authorization-platformstate-writer/test/tsconfig.json b/packages/authorization-platformstate-writer/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/authorization-platformstate-writer/test/utils.ts b/packages/authorization-platformstate-writer/test/utils.ts new file mode 100644 index 0000000000..aca07c9cc6 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/utils.ts @@ -0,0 +1,3 @@ +import { inject } from "vitest"; + +export const config = inject("tokenGenerationReadModelConfig"); diff --git a/packages/authorization-platformstate-writer/test/vitestGlobalSetup.ts b/packages/authorization-platformstate-writer/test/vitestGlobalSetup.ts new file mode 100644 index 0000000000..85a4c8ea41 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/vitestGlobalSetup.ts @@ -0,0 +1,3 @@ +import { setupTestContainersVitestGlobal } from "pagopa-interop-commons-test/index.js"; + +export default setupTestContainersVitestGlobal(); diff --git a/packages/authorization-platformstate-writer/tsconfig.check.json b/packages/authorization-platformstate-writer/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/authorization-platformstate-writer/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/authorization-platformstate-writer/tsconfig.json b/packages/authorization-platformstate-writer/tsconfig.json new file mode 100644 index 0000000000..a1ec44f6e6 --- /dev/null +++ b/packages/authorization-platformstate-writer/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src" + ] +} diff --git a/packages/authorization-platformstate-writer/vitest.config.ts b/packages/authorization-platformstate-writer/vitest.config.ts new file mode 100644 index 0000000000..9ece1be991 --- /dev/null +++ b/packages/authorization-platformstate-writer/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globalSetup: ["./test/vitestGlobalSetup.ts"], + testTimeout: 60000, + hookTimeout: 60000, + fileParallelism: false, + pool: "forks", + }, +}); diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index 930036a35a..853c28d655 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -56,7 +56,7 @@ export * from "./user/user.js"; export * from "./token-generation-readmodel/platform-states-entry.js"; export * from "./token-generation-readmodel/token-generation-states-entry.js"; -export * from "./token-generation-readmodel/dynamoDB-keys.js"; +export * from "./token-generation-readmodel/commons.js"; // Protobuf export * from "./protobuf/protobuf.js"; diff --git a/packages/models/src/token-generation-readmodel/dynamoDB-keys.ts b/packages/models/src/token-generation-readmodel/commons.ts similarity index 88% rename from packages/models/src/token-generation-readmodel/dynamoDB-keys.ts rename to packages/models/src/token-generation-readmodel/commons.ts index 184c039d5d..86308beab5 100644 --- a/packages/models/src/token-generation-readmodel/dynamoDB-keys.ts +++ b/packages/models/src/token-generation-readmodel/commons.ts @@ -1,3 +1,4 @@ +import { z } from "zod"; import { EServiceId, DescriptorId, @@ -93,3 +94,13 @@ export const makeGSIPKClientIdPurposeId = ({ purposeId: PurposeId; }): GSIPKClientIdPurposeId => unsafeBrandId(`${clientId}#${purposeId}`); + +export const clientKindTokenStates = { + consumer: "CONSUMER", + api: "API", +} as const; +export const ClientKindTokenStates = z.enum([ + Object.values(clientKindTokenStates)[0], + ...Object.values(clientKindTokenStates).slice(1), +]); +export type ClientKindTokenStates = z.infer; diff --git a/packages/models/src/token-generation-readmodel/platform-states-entry.ts b/packages/models/src/token-generation-readmodel/platform-states-entry.ts index 3c0c507435..e12c8ae6ec 100644 --- a/packages/models/src/token-generation-readmodel/platform-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/platform-states-entry.ts @@ -11,6 +11,7 @@ import { PurposeVersionId, TenantId, } from "../brandedIds.js"; +import { ClientKindTokenStates } from "./commons.js"; export const itemState = { active: "ACTIVE", @@ -60,6 +61,8 @@ export type PlatformStatesAgreementEntry = z.infer< export const PlatformStatesClientEntry = PlatformStatesBaseEntry.extend({ PK: PlatformStatesClientPK, + clientKind: ClientKindTokenStates, + clientConsumerId: TenantId, clientPurposesIds: z.array(PurposeId), }); export type PlatformStatesClientEntry = z.infer< diff --git a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts index 2ec202ccd7..db65a91e19 100644 --- a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts @@ -12,16 +12,7 @@ import { TokenGenerationStatesClientKidPurposePK, } from "../brandedIds.js"; import { ItemState } from "./platform-states-entry.js"; - -export const clientKindTokenStates = { - consumer: "CONSUMER", - api: "API", -} as const; -export const ClientKindTokenStates = z.enum([ - Object.values(clientKindTokenStates)[0], - ...Object.values(clientKindTokenStates).slice(1), -]); -export type ClientKindTokenStates = z.infer; +import { ClientKindTokenStates } from "./commons.js"; const TokenGenerationStatesBaseEntry = z.object({ consumerId: TenantId, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6da4af8b5..8373799962 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -674,6 +674,73 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) + packages/authorization-platformstate-writer: + dependencies: + '@aws-sdk/client-dynamodb': + specifier: 3.637.0 + version: 3.637.0 + '@aws-sdk/util-dynamodb': + specifier: 3.637.0 + version: 3.637.0(@aws-sdk/client-dynamodb@3.637.0) + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + connection-string: + specifier: 4.4.0 + version: 4.4.0 + dotenv-flow: + specifier: 4.1.0 + version: 4.1.0 + kafka-iam-auth: + specifier: workspace:* + version: link:../kafka-iam-auth + kafkajs: + specifier: 2.2.4 + version: 2.2.4 + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-models: + specifier: workspace:* + version: link:../models + ts-pattern: + specifier: 5.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + '@types/uuid': + specifier: 9.0.8 + version: 9.0.8 + date-fns: + specifier: 3.6.0 + version: 3.6.0 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + uuid: + specifier: 10.0.0 + version: 10.0.0 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + packages/authorization-process: dependencies: '@zodios/core': @@ -4640,6 +4707,9 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/uuid@9.0.8': + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} @@ -5554,6 +5624,7 @@ packages: eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true espree@3.0.0-alpha-1: @@ -7366,6 +7437,10 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true @@ -9429,7 +9504,7 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.1.3 + '@smithy/credential-provider-imds': 3.2.0 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 @@ -11460,6 +11535,8 @@ snapshots: '@types/triple-beam@1.3.5': {} + '@types/uuid@9.0.8': {} + '@types/webidl-conversions@7.0.3': {} '@types/whatwg-url@11.0.5': @@ -14582,6 +14659,8 @@ snapshots: utils-merge@1.0.1: {} + uuid@10.0.0: {} + uuid@9.0.1: {} vary@1.1.2: {} From d4d8f54f96bddb049086dd7e52b842e8dddfb707 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Wed, 6 Nov 2024 14:21:43 +0100 Subject: [PATCH 13/34] PIN-5566 - Fixing DTD catalog exporter termination and package.json (#1166) --- packages/dtd-catalog-exporter/package.json | 7 +++---- packages/dtd-catalog-exporter/src/index.ts | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/dtd-catalog-exporter/package.json b/packages/dtd-catalog-exporter/package.json index 6d8ea31ecd..48dac47f26 100644 --- a/packages/dtd-catalog-exporter/package.json +++ b/packages/dtd-catalog-exporter/package.json @@ -10,10 +10,9 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "check": "tsc --project tsconfig.check.json", - "start:migrate": "tsx -r 'dotenv-flow/config' ./src/index.ts", - "start:verify": "tsx -r 'dotenv-flow/config' ./src/read-models-migration-check.ts", - "build": "tsc" + "start": "tsx -r 'dotenv-flow/config' ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" }, "keywords": [], "author": "", diff --git a/packages/dtd-catalog-exporter/src/index.ts b/packages/dtd-catalog-exporter/src/index.ts index ea2659fc04..f3150907ee 100644 --- a/packages/dtd-catalog-exporter/src/index.ts +++ b/packages/dtd-catalog-exporter/src/index.ts @@ -16,3 +16,8 @@ await dtdCatalogExporterServiceBuilder({ correlationId: generateId(), }), }).exportDtdPublicCatalog(); + +process.exit(0); +// process.exit() should not be required. +// however, something in this script hangs on exit. +// TODO figure out why and remove this workaround. From b34486279d0c04efd5c99d6bfbf84e54e615a0a7 Mon Sep 17 00:00:00 2001 From: Simone Camito <32327779+MalpenZibo@users.noreply.github.com> Date: Wed, 6 Nov 2024 14:53:05 +0100 Subject: [PATCH 14/34] Fix one trust notices (#1167) --- packages/one-trust-notices/.env | 2 ++ packages/one-trust-notices/package.json | 2 +- .../src/services/html2json.ts | 2 +- packages/pn-consumers/.env | 4 +++- pnpm-lock.yaml | 19 +++++++++++++------ 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/one-trust-notices/.env b/packages/one-trust-notices/.env index 34a132a77e..94054af2e8 100644 --- a/packages/one-trust-notices/.env +++ b/packages/one-trust-notices/.env @@ -1,3 +1,5 @@ +LOG_LEVEL=info + AWS_REGION=eu-south-1 LANGS=it,de,fr,es diff --git a/packages/one-trust-notices/package.json b/packages/one-trust-notices/package.json index 56e48cdff3..8e263e39ae 100644 --- a/packages/one-trust-notices/package.json +++ b/packages/one-trust-notices/package.json @@ -21,7 +21,7 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/html2json": "1.0.1", - "@types/lodash": "4.14.196", + "@types/lodash.isempty": "4.4.9", "@types/node": "20.4.9", "prettier": "2.8.8", "tsx": "4.19.1", diff --git a/packages/one-trust-notices/src/services/html2json.ts b/packages/one-trust-notices/src/services/html2json.ts index fa78126b13..fbbb4ce6df 100644 --- a/packages/one-trust-notices/src/services/html2json.ts +++ b/packages/one-trust-notices/src/services/html2json.ts @@ -2,7 +2,7 @@ /* eslint-disable fp/no-delete */ import h2j from "html2json"; -import isEmpty from "lodash/isEmpty.js"; +import isEmpty from "lodash.isempty"; export function html2json(html: string): h2j.Node { const jsonHtlmNodes = h2j.html2json(html); diff --git a/packages/pn-consumers/.env b/packages/pn-consumers/.env index 7f4a870b38..b47ddc4580 100644 --- a/packages/pn-consumers/.env +++ b/packages/pn-consumers/.env @@ -1,5 +1,7 @@ LOG_LEVEL=info +AWS_REGION=eu-south-1 + READMODEL_DB_HOST="localhost" READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" @@ -7,7 +9,7 @@ READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 MAIL_RECIPIENTS="" -REPORT_SENDER_MAIL="" +REPORT_SENDER_MAIL="test@test.it" REPORT_SENDER_LABEL="" PN_ESERVICE_ID="" COMUNI_E_LORO_CONSORZI_E_ASSOCIAZIONI_ATTRIBUTE_ID="" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8373799962..a9841b24c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2094,9 +2094,9 @@ importers: '@types/html2json': specifier: 1.0.1 version: 1.0.1 - '@types/lodash': - specifier: 4.14.196 - version: 4.14.196 + '@types/lodash.isempty': + specifier: 4.4.9 + version: 4.4.9 '@types/node': specifier: 20.4.9 version: 20.4.9 @@ -4641,11 +4641,14 @@ packages: '@types/jsonwebtoken@9.0.6': resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} + '@types/lodash.isempty@4.4.9': + resolution: {integrity: sha512-DPSFfnT2JmZiAWNWOU8IRZws/Ha6zyGF5m06TydfsY+0dVoQqby2J61Na2QU4YtwiZ+moC6cJS6zWYBJq4wBVw==} + '@types/lodash.isequal@4.5.8': resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} - '@types/lodash@4.14.196': - resolution: {integrity: sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==} + '@types/lodash@4.17.13': + resolution: {integrity: sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==} '@types/lodash@4.17.6': resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} @@ -11461,11 +11464,15 @@ snapshots: dependencies: '@types/node': 20.14.6 + '@types/lodash.isempty@4.4.9': + dependencies: + '@types/lodash': 4.17.13 + '@types/lodash.isequal@4.5.8': dependencies: '@types/lodash': 4.17.6 - '@types/lodash@4.14.196': {} + '@types/lodash@4.17.13': {} '@types/lodash@4.17.6': {} From 3c71dd3fe34c393b7069426a84b0addf6563b658 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 6 Nov 2024 17:54:18 +0100 Subject: [PATCH 15/34] IMN-795 Add events service v2 in authorization-platformstate-writer (#1062) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../package.json | 2 - .../src/consumerServiceV2.ts | 391 ++- .../src/utils.ts | 1121 +++++++ .../consumerServiceV2.integration.test.ts | 2768 +++++++++++++++++ .../test/utils.test.ts | 779 +++++ packages/commons-test/src/testUtils.ts | 49 +- .../src/tokenGenerationReadmodelUtils.ts | 111 +- packages/models/src/brandedIds.ts | 14 +- .../src/token-generation-readmodel/commons.ts | 4 + .../platform-states-entry.ts | 9 + .../token-generation-states-entry.ts | 11 +- pnpm-lock.yaml | 17 - 12 files changed, 5238 insertions(+), 38 deletions(-) create mode 100644 packages/authorization-platformstate-writer/src/utils.ts create mode 100644 packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts create mode 100644 packages/authorization-platformstate-writer/test/utils.test.ts diff --git a/packages/authorization-platformstate-writer/package.json b/packages/authorization-platformstate-writer/package.json index 6106ef0a72..62ee6846f6 100644 --- a/packages/authorization-platformstate-writer/package.json +++ b/packages/authorization-platformstate-writer/package.json @@ -22,13 +22,11 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "tsx": "4.19.1", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index 4ad0fd25ab..4fd99439fa 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -1,19 +1,385 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { AuthorizationEventEnvelopeV2 } from "pagopa-interop-models"; +import { + AuthorizationEventEnvelopeV2, + Client, + ClientV2, + fromClientV2, + genericInternalError, + itemState, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKKid, + makePlatformStatesClientPK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + missingKafkaMessageDataError, + PlatformStatesClientEntry, + PurposeId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, + unsafeBrandId, +} from "pagopa-interop-models"; import { match } from "ts-pattern"; +import { + clientKindToTokenGenerationStatesClientKind, + convertEntriesToClientKidInTokenGenerationStates, + deleteClientEntryFromPlatformStates, + deleteEntriesFromTokenStatesByClient, + deleteEntriesFromTokenStatesByKid, + deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable, + readClientEntry, + readClientEntriesInTokenGenerationStates, + cleanClientPurposeIdsInPlatformStatesEntry, + deleteClientEntryFromTokenGenerationStatesTable, + extractKidFromTokenEntryPK, + extractAgreementIdFromAgreementPK, + retrievePlatformStatesByPurpose, + upsertPlatformClientEntry, + upsertTokenClientKidEntry, + upsertTokenStateClientPurposeEntry, + updateTokenDataForSecondRetrieval, + createTokenClientPurposeEntry, +} from "./utils.js"; export async function handleMessageV2( message: AuthorizationEventEnvelopeV2, - _dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient ): Promise { await match(message) + .with({ type: "ClientKeyAdded" }, async (msg) => { + const client = parseClient(msg.data.client, msg.type); + + const pem = client.keys.find( + (key) => key.kid === msg.data.kid + )?.encodedPem; + if (!pem) { + throw missingKafkaMessageDataError("key", msg.type); + } + + const platformClientPK = makePlatformStatesClientPK(client.id); + const clientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + + if (clientEntry && clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + // platform-states + const platformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + clientPurposesIds: [], + version: msg.version, + updatedAt: new Date().toISOString(), + }; + await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + } + + // token-generation-states + if (client.purposes.length > 0) { + const addedEntries = await Promise.all( + client.purposes.map(async (purposeId) => { + const { purposeEntry, agreementEntry, catalogEntry } = + await retrievePlatformStatesByPurpose(purposeId, dynamoDBClient); + + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: msg.data.kid, + purposeId, + }); + + const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + PK: tokenClientKidPurposePK, + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind( + client.kind + ), + publicKey: pem, + updatedAt: new Date().toISOString(), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(msg.data.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId, + }), + GSIPK_purposeId: purposeId, + ...((purposeEntry && { + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: client.consumerId, + eserviceId: purposeEntry.purposeEserviceId, + }), + purposeState: purposeEntry.state, + purposeVersionId: purposeEntry.purposeVersionId, + }) || + {}), + ...((purposeEntry && + agreementEntry && { + agreementId: extractAgreementIdFromAgreementPK( + agreementEntry.PK + ), + agreementState: agreementEntry.state, + GSIPK_eserviceId_descriptorId: + makeGSIPKEServiceIdDescriptorId({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: agreementEntry.agreementDescriptorId, + }), + }) || + {}), + ...((catalogEntry && { + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: + catalogEntry.descriptorVoucherLifespan, + }) || + {}), + }; + + await upsertTokenStateClientPurposeEntry( + clientKidPurposeEntry, + dynamoDBClient + ); + return clientKidPurposeEntry; + }) + ); + + // Second check for updated fields + await Promise.all( + client.purposes.map(async (purposeId, index) => { + const { + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + } = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + const addedClientKidPurposeEntry = addedEntries[index]; + await updateTokenDataForSecondRetrieval({ + dynamoDBClient, + entry: addedClientKidPurposeEntry, + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + }); + }) + ); + } else { + const clientKidEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: msg.data.kid, + }), + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + publicKey: pem, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(msg.data.kid), + updatedAt: new Date().toISOString(), + }; + await upsertTokenClientKidEntry(clientKidEntry, dynamoDBClient); + } + }) + .with({ type: "ClientKeyDeleted" }, async (msg) => { + const client = parseClient(msg.data.client, msg.type); + const pk = makePlatformStatesClientPK(client.id); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + + if (clientEntry && clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const platformClientEntry: PlatformStatesClientEntry = { + PK: pk, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + clientPurposesIds: [], + version: msg.version, + updatedAt: new Date().toISOString(), + }; + await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + } + + const GSIPK_kid = makeGSIPKKid(msg.data.kid); + await deleteEntriesFromTokenStatesByKid(GSIPK_kid, dynamoDBClient); + }) + .with({ type: "ClientPurposeAdded" }, async (msg) => { + const client = parseClient(msg.data.client, msg.type); + + // platform-states + const platformClientEntryPK = makePlatformStatesClientPK(client.id); + const clientEntry = await readClientEntry( + platformClientEntryPK, + dynamoDBClient + ); + if (clientEntry && clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + // platform-states + const platformClientEntry: PlatformStatesClientEntry = { + PK: platformClientEntryPK, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + clientPurposesIds: [], + version: msg.version, + updatedAt: new Date().toISOString(), + }; + await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + } + + // token-generation-states + const GSIPK_clientId = client.id; + const tokenClientEntries = await readClientEntriesInTokenGenerationStates( + GSIPK_clientId, + dynamoDBClient + ); + if (tokenClientEntries.length === 0) { + return Promise.resolve(); + } else { + const purposeId = unsafeBrandId(msg.data.purposeId); + const { purposeEntry, agreementEntry, catalogEntry } = + await retrievePlatformStatesByPurpose(purposeId, dynamoDBClient); + + const seenKids = new Set(); + const addedTokenClientPurposeEntries = await Promise.all( + tokenClientEntries.map(async (entry) => { + const parsedTokenClientEntry = + TokenGenerationStatesClientEntry.safeParse(entry); + const parsedTokenClientPurposeEntry = + TokenGenerationStatesClientPurposeEntry.safeParse(entry); + + if (parsedTokenClientEntry.success) { + const newTokenClientPurposeEntry = createTokenClientPurposeEntry({ + tokenEntry: parsedTokenClientEntry.data, + kid: extractKidFromTokenEntryPK(parsedTokenClientEntry.data.PK), + clientId: client.id, + consumerId: client.consumerId, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, + }); + + await upsertTokenStateClientPurposeEntry( + newTokenClientPurposeEntry, + dynamoDBClient + ); + await deleteClientEntryFromTokenGenerationStatesTable( + entry, + dynamoDBClient + ); + return newTokenClientPurposeEntry; + } + + if (parsedTokenClientPurposeEntry.success) { + const kid = extractKidFromTokenEntryPK( + parsedTokenClientPurposeEntry.data.PK + ); + if (!seenKids.has(kid)) { + const newTokenClientPurposeEntry = + createTokenClientPurposeEntry({ + tokenEntry: parsedTokenClientPurposeEntry.data, + kid, + clientId: client.id, + consumerId: client.consumerId, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, + }); + + await upsertTokenStateClientPurposeEntry( + newTokenClientPurposeEntry, + dynamoDBClient + ); + seenKids.add(kid); + return newTokenClientPurposeEntry; + } + } + + throw genericInternalError(`Unable to parse ${entry}`); + }) + ); + + // Second check for updated fields + await Promise.all( + addedTokenClientPurposeEntries.map(async (entry) => { + const { + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + } = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + await updateTokenDataForSecondRetrieval({ + dynamoDBClient, + entry, + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + }); + }) + ); + } + }) + .with({ type: "ClientPurposeRemoved" }, async (msg) => { + const client = parseClient(msg.data.client, msg.type); + const pk = makePlatformStatesClientPK(client.id); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + + if (clientEntry) { + if (clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: unsafeBrandId(msg.data.purposeId), + }); + + // platform-states + await cleanClientPurposeIdsInPlatformStatesEntry( + pk, + msg.version, + dynamoDBClient + ); + + // token-generation-states + if (client.purposes.length > 0) { + await deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable( + GSIPK_clientId_purposeId, + dynamoDBClient + ); + } else { + await convertEntriesToClientKidInTokenGenerationStates( + GSIPK_clientId_purposeId, + dynamoDBClient + ); + } + } + } + }) + .with({ type: "ClientDeleted" }, async (msg) => { + const client = parseClient(msg.data.client, msg.type); + const pk = makePlatformStatesClientPK(client.id); + await deleteClientEntryFromPlatformStates(pk, dynamoDBClient); + + const GSIPK_clientId = client.id; + await deleteEntriesFromTokenStatesByClient( + GSIPK_clientId, + dynamoDBClient + ); + }) .with( { type: "ClientAdded" }, - { type: "ClientDeleted" }, - { type: "ClientKeyAdded" }, - { type: "ClientKeyDeleted" }, - { type: "ClientPurposeAdded" }, - { type: "ClientPurposeRemoved" }, { type: "ClientUserAdded" }, { type: "ClientUserDeleted" }, { type: "ProducerKeychainAdded" }, @@ -30,3 +396,14 @@ export async function handleMessageV2( ) .exhaustive(); } + +const parseClient = ( + clientV2: ClientV2 | undefined, + eventType: string +): Client => { + if (!clientV2) { + throw missingKafkaMessageDataError("client", eventType); + } + + return fromClientV2(clientV2); +}; diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts new file mode 100644 index 0000000000..9659d82c7c --- /dev/null +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -0,0 +1,1121 @@ +import { + AttributeValue, + DeleteItemCommand, + DeleteItemInput, + DynamoDBClient, + GetItemCommand, + GetItemCommandOutput, + GetItemInput, + PutItemCommand, + PutItemInput, + QueryCommand, + QueryCommandOutput, + QueryInput, +} from "@aws-sdk/client-dynamodb"; +import { + AgreementId, + ClientId, + clientKind, + ClientKind, + clientKindTokenStates, + ClientKindTokenStates, + genericInternalError, + GSIPKClientIdPurposeId, + GSIPKConsumerIdEServiceId, + GSIPKKid, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKKid, + makePlatformStatesEServiceDescriptorPK, + makePlatformStatesPurposePK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + PlatformStatesAgreementEntry, + PlatformStatesAgreementPK, + PlatformStatesCatalogEntry, + PlatformStatesClientEntry, + PlatformStatesClientPK, + PlatformStatesEServiceDescriptorPK, + PlatformStatesPurposeEntry, + PlatformStatesPurposePK, + PurposeId, + TenantId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientKidPK, + TokenGenerationStatesClientKidPurposePK, + TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesGenericEntry, +} from "pagopa-interop-models"; +import { z } from "zod"; +import { unmarshall } from "@aws-sdk/util-dynamodb"; +import { match } from "ts-pattern"; +import { UpdateItemInput } from "@aws-sdk/client-dynamodb"; +import { UpdateItemCommand } from "@aws-sdk/client-dynamodb"; +import { config } from "./config/config.js"; + +export const deleteEntriesFromTokenStatesByKid = async ( + GSIPK_kid: GSIPKKid, + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( + GSIPK_kid: GSIPKKid, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "Kid", + KeyConditionExpression: `GSIPK_kid = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: GSIPK_kid }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesGenericEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + for (const entry of tokenStateEntries.data) { + await deleteClientEntryFromTokenGenerationStatesTable( + entry, + dynamoDBClient + ); + } + + if (data.LastEvaluatedKey) { + await runPaginatedQuery( + GSIPK_kid, + dynamoDBClient, + data.LastEvaluatedKey + ); + } + } + }; + + await runPaginatedQuery(GSIPK_kid, dynamoDBClient, undefined); +}; + +export const deleteClientEntryFromPlatformStates = async ( + pk: PlatformStatesClientPK, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: DeleteItemInput = { + Key: { + PK: { S: pk }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new DeleteItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const deleteEntriesFromTokenStatesByClient = async ( + GSIPK_client: ClientId, + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( + GSIPK_client: ClientId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "Client", + KeyConditionExpression: `GSIPK_clientId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: GSIPK_client }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesGenericEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + for (const entry of tokenStateEntries.data) { + await deleteClientEntryFromTokenGenerationStatesTable( + entry, + dynamoDBClient + ); + } + + if (data.LastEvaluatedKey) { + await runPaginatedQuery( + GSIPK_client, + dynamoDBClient, + data.LastEvaluatedKey + ); + } + } + }; + + await runPaginatedQuery(GSIPK_client, dynamoDBClient, undefined); +}; + +export const deleteClientEntryFromTokenGenerationStatesTable = async ( + entryToDelete: TokenGenerationStatesGenericEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: DeleteItemInput = { + Key: { + PK: { S: entryToDelete.PK }, + }, + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + }; + const command = new DeleteItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const readClientEntry = async ( + primaryKey: PlatformStatesClientPK, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: GetItemInput = { + Key: { + PK: { S: primaryKey }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new GetItemCommand(input); + const data: GetItemCommandOutput = await dynamoDBClient.send(command); + + if (!data.Item) { + return undefined; + } else { + const unmarshalled = unmarshall(data.Item); + const clientEntry = PlatformStatesClientEntry.safeParse(unmarshalled); + + if (!clientEntry.success) { + throw genericInternalError( + `Unable to parse client entry item: result ${JSON.stringify( + clientEntry + )} - data ${JSON.stringify(data)} ` + ); + } + return clientEntry.data; + } +}; + +const readTokenStateEntriesByGSIPKClientPurpose = async ( + GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record +): Promise<{ + tokenStateEntries: TokenGenerationStatesClientPurposeEntry[]; + lastEvaluatedKey: Record | undefined; +}> => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "ClientPurpose", + KeyConditionExpression: `GSIPK_clientId_purposeId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: GSIPK_clientId_purposeId }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesClientPurposeEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + return { + tokenStateEntries: tokenStateEntries.data, + lastEvaluatedKey: data.LastEvaluatedKey, + }; + } +}; + +export const deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable = + async ( + GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, + dynamoDBClient: DynamoDBClient + ): Promise => { + const runPaginatedQuery = async ( + GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const res = await readTokenStateEntriesByGSIPKClientPurpose( + GSIPK_clientId_purposeId, + dynamoDBClient, + exclusiveStartKey + ); + + for (const entry of res.tokenStateEntries) { + await deleteClientEntryFromTokenGenerationStatesTable( + entry, + dynamoDBClient + ); + } + + if (res.lastEvaluatedKey) { + await runPaginatedQuery( + GSIPK_clientId_purposeId, + dynamoDBClient, + res.lastEvaluatedKey + ); + } + }; + await runPaginatedQuery(GSIPK_clientId_purposeId, dynamoDBClient); + }; + +export const convertEntriesToClientKidInTokenGenerationStates = async ( + GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( + GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const res = await readTokenStateEntriesByGSIPKClientPurpose( + GSIPK_clientId_purposeId, + dynamoDBClient, + exclusiveStartKey + ); + + // convert entries + for (const entry of res.tokenStateEntries) { + const newEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: entry.GSIPK_clientId, + kid: entry.GSIPK_kid, + }), + consumerId: entry.consumerId, + clientKind: entry.clientKind, + publicKey: entry.publicKey, + GSIPK_clientId: entry.GSIPK_clientId, + GSIPK_kid: entry.GSIPK_kid, + updatedAt: new Date().toISOString(), + }; + + // write the new one + await writeTokenStateClientEntry(newEntry, dynamoDBClient); + + // delete the old one + await deleteClientEntryFromTokenGenerationStatesTable( + entry, + dynamoDBClient + ); + } + + if (!res.lastEvaluatedKey) { + return res.tokenStateEntries; + } else { + return [ + ...res.tokenStateEntries, + ...(await runPaginatedQuery( + GSIPK_clientId_purposeId, + dynamoDBClient, + res.lastEvaluatedKey + )), + ]; + } + }; + await runPaginatedQuery(GSIPK_clientId_purposeId, dynamoDBClient); +}; + +export const writeTokenStateClientEntry = async ( + tokenStateEntry: TokenGenerationStatesClientEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: tokenStateEntry.PK, + }, + updatedAt: { + S: tokenStateEntry.updatedAt, + }, + consumerId: { + S: tokenStateEntry.consumerId, + }, + clientKind: { + S: tokenStateEntry.clientKind, + }, + publicKey: { + S: tokenStateEntry.publicKey, + }, + GSIPK_clientId: { + S: tokenStateEntry.GSIPK_clientId, + }, + GSIPK_kid: { + S: tokenStateEntry.GSIPK_kid, + }, + }, + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const readCatalogEntry = async ( + primaryKey: PlatformStatesEServiceDescriptorPK, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: GetItemInput = { + Key: { + PK: { S: primaryKey }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new GetItemCommand(input); + const data: GetItemCommandOutput = await dynamoDBClient.send(command); + + if (!data.Item) { + return undefined; + } else { + const unmarshalled = unmarshall(data.Item); + const catalogEntry = PlatformStatesCatalogEntry.safeParse(unmarshalled); + + if (!catalogEntry.success) { + throw genericInternalError( + `Unable to parse catalog entry item: result ${JSON.stringify( + catalogEntry + )} - data ${JSON.stringify(data)} ` + ); + } + return catalogEntry.data; + } +}; + +export const readPlatformAgreementEntryByGSIPKConsumerIdEServiceId = async ( + gsiPKConsumerIdEServiceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNamePlatform, + IndexName: "Agreement", + KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: gsiPKConsumerIdEServiceId }, + }, + ScanIndexForward: false, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + return undefined; + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + const platformAgreementEntries = z + .array(PlatformStatesAgreementEntry) + .safeParse(unmarshalledItems); + + if (platformAgreementEntries.success) { + return platformAgreementEntries.data[0]; + } else { + throw genericInternalError( + `Unable to parse platform agreement entries: result ${JSON.stringify( + platformAgreementEntries + )} ` + ); + } + } +}; + +export const readPlatformPurposeEntry = async ( + primaryKey: PlatformStatesPurposePK, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: GetItemInput = { + Key: { + PK: { S: primaryKey }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new GetItemCommand(input); + const data: GetItemCommandOutput = await dynamoDBClient.send(command); + + if (!data.Item) { + return undefined; + } else { + const unmarshalled = unmarshall(data.Item); + const purposeEntry = PlatformStatesPurposeEntry.safeParse(unmarshalled); + + if (!purposeEntry.success) { + throw genericInternalError( + `Unable to parse purpose entry item: result ${JSON.stringify( + purposeEntry + )} - data ${JSON.stringify(data)} ` + ); + } + return purposeEntry.data; + } +}; + +export const upsertTokenStateClientPurposeEntry = async ( + tokenStateEntry: TokenGenerationStatesClientPurposeEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + Item: { + PK: { + S: tokenStateEntry.PK, + }, + ...(tokenStateEntry.descriptorState + ? { + descriptorState: { + S: tokenStateEntry.descriptorState, + }, + } + : {}), + ...(tokenStateEntry.descriptorAudience + ? { + descriptorAudience: { + L: tokenStateEntry.descriptorAudience.map((item) => ({ + S: item, + })), + }, + } + : {}), + ...(tokenStateEntry.descriptorVoucherLifespan + ? { + descriptorVoucherLifespan: { + N: tokenStateEntry.descriptorVoucherLifespan.toString(), + }, + } + : {}), + updatedAt: { + S: tokenStateEntry.updatedAt, + }, + consumerId: { + S: tokenStateEntry.consumerId, + }, + ...(tokenStateEntry.agreementId + ? { + agreementId: { + S: tokenStateEntry.agreementId, + }, + } + : {}), + ...(tokenStateEntry.purposeVersionId + ? { + purposeVersionId: { + S: tokenStateEntry.purposeVersionId, + }, + } + : {}), + ...(tokenStateEntry.GSIPK_consumerId_eserviceId + ? { + GSIPK_consumerId_eserviceId: { + S: tokenStateEntry.GSIPK_consumerId_eserviceId, + }, + } + : {}), + clientKind: { + S: tokenStateEntry.clientKind, + }, + publicKey: { + S: tokenStateEntry.publicKey, + }, + GSIPK_clientId: { + S: tokenStateEntry.GSIPK_clientId, + }, + GSIPK_kid: { + S: tokenStateEntry.GSIPK_kid, + }, + ...(tokenStateEntry.GSIPK_clientId_purposeId + ? { + GSIPK_clientId_purposeId: { + S: tokenStateEntry.GSIPK_clientId_purposeId, + }, + } + : {}), + ...(tokenStateEntry.agreementState + ? { + agreementState: { + S: tokenStateEntry.agreementState, + }, + } + : {}), + ...(tokenStateEntry.GSIPK_eserviceId_descriptorId + ? { + GSIPK_eserviceId_descriptorId: { + S: tokenStateEntry.GSIPK_eserviceId_descriptorId, + }, + } + : {}), + ...(tokenStateEntry.GSIPK_purposeId + ? { + GSIPK_purposeId: { + S: tokenStateEntry.GSIPK_purposeId, + }, + } + : {}), + ...(tokenStateEntry.purposeState + ? { + purposeState: { + S: tokenStateEntry.purposeState, + }, + } + : {}), + }, + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const clientKindToTokenGenerationStatesClientKind = ( + kind: ClientKind +): ClientKindTokenStates => + match(kind) + .with(clientKind.consumer, () => clientKindTokenStates.consumer) + .with(clientKind.api, () => clientKindTokenStates.api) + .exhaustive(); + +export const writeClientEntry = async ( + clientEntry: PlatformStatesClientEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: clientEntry.PK, + }, + state: { + S: clientEntry.state, + }, + clientPurposesIds: { + L: clientEntry.clientPurposesIds.map((purposeId) => ({ + S: purposeId, + })), + }, + clientKind: { + S: clientEntry.clientKind, + }, + clientConsumerId: { + S: clientEntry.clientConsumerId, + }, + version: { + N: clientEntry.version.toString(), + }, + updatedAt: { + S: clientEntry.updatedAt, + }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const readClientEntriesInTokenGenerationStates = async ( + GSIPK_clientId: ClientId, + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( + GSIPK_clientId: ClientId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "Client", + KeyConditionExpression: `GSIPK_clientId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: GSIPK_clientId }, + }, + ExclusiveStartKey: exclusiveStartKey, + ScanIndexForward: false, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read platform state client entries: result ${JSON.stringify( + data + )} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const clientEntries = z + .array(TokenGenerationStatesGenericEntry) + .safeParse(unmarshalledItems); + + if (!clientEntries.success) { + throw genericInternalError( + `Unable to parse token state entry items: result ${JSON.stringify( + clientEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + if (!data.LastEvaluatedKey) { + return clientEntries.data; + } else { + return [ + ...clientEntries.data, + ...(await runPaginatedQuery( + GSIPK_clientId, + dynamoDBClient, + data.LastEvaluatedKey + )), + ]; + } + } + }; + + return await runPaginatedQuery(GSIPK_clientId, dynamoDBClient, undefined); +}; + +export const cleanClientPurposeIdsInPlatformStatesEntry = async ( + primaryKey: PlatformStatesClientPK, + version: number, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: UpdateItemInput = { + ConditionExpression: "attribute_exists(PK)", + Key: { + PK: { + S: primaryKey, + }, + }, + ExpressionAttributeValues: { + ":clientPurposesIds": { + L: [], + }, + ":newVersion": { + N: version.toString(), + }, + ":newUpdateAt": { + S: new Date().toISOString(), + }, + }, + UpdateExpression: + "SET clientPurposesIds = :clientPurposesIds, updatedAt = :newUpdateAt, version = :newVersion", + TableName: config.tokenGenerationReadModelTableNamePlatform, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const extractKidFromTokenEntryPK = ( + pk: TokenGenerationStatesClientKidPK | TokenGenerationStatesClientKidPurposePK +): string => pk.split("#")[2]; + +export const extractAgreementIdFromAgreementPK = ( + pk: PlatformStatesAgreementPK +): AgreementId => { + const substrings = pk.split("#"); + const agreementId = substrings[1]; + const result = AgreementId.safeParse(agreementId); + + if (!result.success) { + throw genericInternalError( + `Unable to parse agreement PK: result ${JSON.stringify( + result + )} - data ${JSON.stringify(agreementId)} ` + ); + } + return result.data; +}; + +export const retrievePlatformStatesByPurpose = async ( + purposeId: PurposeId, + dynamoDBClient: DynamoDBClient +): Promise<{ + purposeEntry?: PlatformStatesPurposeEntry; + agreementEntry?: PlatformStatesAgreementEntry; + catalogEntry?: PlatformStatesCatalogEntry; +}> => { + const purposePK = makePlatformStatesPurposePK(purposeId); + const purposeEntry = await readPlatformPurposeEntry( + purposePK, + dynamoDBClient + ); + + if (!purposeEntry) { + return { + purposeEntry: undefined, + }; + } + + const agreementGSI = makeGSIPKConsumerIdEServiceId({ + eserviceId: purposeEntry.purposeEserviceId, + consumerId: purposeEntry.purposeConsumerId, + }); + + const agreementEntry = + await readPlatformAgreementEntryByGSIPKConsumerIdEServiceId( + agreementGSI, + dynamoDBClient + ); + + if (!agreementEntry) { + return { + purposeEntry, + agreementEntry: undefined, + }; + } + + const catalogPK = makePlatformStatesEServiceDescriptorPK({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: agreementEntry.agreementDescriptorId, + }); + const catalogEntry = await readCatalogEntry(catalogPK, dynamoDBClient); + + if (!catalogEntry) { + return { + purposeEntry, + agreementEntry, + catalogEntry: undefined, + }; + } + return { + purposeEntry, + agreementEntry, + catalogEntry, + }; +}; + +export const upsertPlatformClientEntry = async ( + entry: PlatformStatesClientEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + Item: { + PK: { + S: entry.PK, + }, + state: { + S: entry.state, + }, + clientPurposesIds: { + L: entry.clientPurposesIds.map((purposeId) => ({ + S: purposeId, + })), + }, + clientKind: { + S: entry.clientKind, + }, + clientConsumerId: { + S: entry.clientConsumerId, + }, + version: { + N: entry.version.toString(), + }, + updatedAt: { + S: entry.updatedAt, + }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const upsertTokenClientKidEntry = async ( + entry: TokenGenerationStatesClientEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + Item: { + PK: { + S: entry.PK, + }, + consumerId: { + S: entry.consumerId, + }, + clientKind: { + S: entry.clientKind, + }, + publicKey: { + S: entry.publicKey, + }, + GSIPK_clientId: { + S: entry.GSIPK_clientId, + }, + GSIPK_kid: { + S: entry.GSIPK_kid, + }, + updatedAt: { + S: entry.updatedAt, + }, + }, + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const updateTokenDataForSecondRetrieval = async ({ + dynamoDBClient, + entry, + purposeEntry, + agreementEntry, + catalogEntry, +}: { + dynamoDBClient: DynamoDBClient; + entry: TokenGenerationStatesClientPurposeEntry; + purposeEntry?: PlatformStatesPurposeEntry; + agreementEntry?: PlatformStatesAgreementEntry; + catalogEntry?: PlatformStatesCatalogEntry; +}): Promise => { + const setIfChanged = < + K extends keyof TokenGenerationStatesClientPurposeEntry + >( + key: K, + newValue: TokenGenerationStatesClientPurposeEntry[K] + ): Partial => { + const oldValue = entry[key]; + + if (Array.isArray(oldValue) && Array.isArray(newValue)) { + return !oldValue.every((value) => newValue.includes(value)) + ? { [key]: newValue } + : {}; + } + + return oldValue !== newValue ? { [key]: newValue } : {}; + }; + const updatedFields: Partial = { + ...(purposeEntry + ? { + ...setIfChanged( + "GSIPK_consumerId_eserviceId", + makeGSIPKConsumerIdEServiceId({ + consumerId: purposeEntry.purposeConsumerId, + eserviceId: purposeEntry.purposeEserviceId, + }) + ), + ...setIfChanged("purposeVersionId", purposeEntry.purposeVersionId), + ...setIfChanged("purposeState", purposeEntry.state), + } + : {}), + ...(purposeEntry && agreementEntry + ? { + ...setIfChanged( + "GSIPK_eserviceId_descriptorId", + makeGSIPKEServiceIdDescriptorId({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: agreementEntry.agreementDescriptorId, + }) + ), + ...setIfChanged("agreementState", agreementEntry.state), + } + : {}), + ...(catalogEntry + ? { + ...setIfChanged( + "descriptorAudience", + catalogEntry.descriptorAudience + ), + ...setIfChanged( + "descriptorVoucherLifespan", + catalogEntry.descriptorVoucherLifespan + ), + ...setIfChanged("descriptorState", catalogEntry.state), + } + : {}), + }; + + if (Object.keys(updatedFields).length > 0) { + const { expressionAttributeValues, updateExpression } = + generateUpdateItemInputData(updatedFields); + + const input: UpdateItemInput = { + ConditionExpression: "attribute_exists(PK)", + Key: { + PK: { + S: entry.PK, + }, + }, + ExpressionAttributeValues: expressionAttributeValues, + UpdateExpression: updateExpression, + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); + } +}; + +const convertValueToAttributeValue = ( + value: string | number | boolean | Array +): AttributeValue => { + if (typeof value === "string") { + return { S: value }; + } else if (typeof value === "number") { + return { N: value.toString() }; + } else if (typeof value === "boolean") { + return { BOOL: value }; + } else if (Array.isArray(value)) { + return { L: value.map((item) => convertValueToAttributeValue(item)) }; + } else { + throw genericInternalError( + `Unsupported DynamoDB type ${typeof value} while converting to AttributeValue` + ); + } +}; + +const convertToExpressionAttributeValues = ( + updatedFields: Partial +): Record => { + const expressionAttributeValues = Object.keys(updatedFields).reduce( + (acc, key) => { + const value = updatedFields[key as keyof typeof updatedFields]; + if (value !== undefined) { + const dynamoKey = `:${key}`; + return { + ...acc, + [dynamoKey]: convertValueToAttributeValue(value), + }; + } + return acc; + }, + {} + ); + + return { + ...expressionAttributeValues, + ":newUpdateAt": { S: new Date().toISOString() }, + }; +}; + +const generateUpdateItemInputData = ( + updatedFields: Partial +): { + updateExpression: string; + expressionAttributeValues: Record; +} => { + const expressionAttributeValues = + convertToExpressionAttributeValues(updatedFields); + + const updateExpressionTmp = Object.keys(updatedFields) + .map((key) => `${key} = :${key}`) + .join(", "); + + const updateExpression = `SET updatedAt = :newUpdateAt, ${updateExpressionTmp}`; + + return { + updateExpression, + expressionAttributeValues, + }; +}; + +export const createTokenClientPurposeEntry = ({ + tokenEntry: baseEntry, + kid, + clientId, + consumerId, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, +}: { + tokenEntry: TokenGenerationStatesGenericEntry; + kid: string; + clientId: ClientId; + consumerId: TenantId; + purposeId: PurposeId; + purposeEntry?: PlatformStatesPurposeEntry; + agreementEntry?: PlatformStatesAgreementEntry; + catalogEntry?: PlatformStatesCatalogEntry; +}): TokenGenerationStatesClientPurposeEntry => { + const pk = makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }); + const isTokenClientPurposeEntry = + TokenGenerationStatesClientPurposeEntry.safeParse(baseEntry).success; + + return { + PK: pk, + consumerId: baseEntry.consumerId, + updatedAt: new Date().toISOString(), + clientKind: isTokenClientPurposeEntry + ? baseEntry.clientKind + : clientKindTokenStates.consumer, + publicKey: baseEntry.publicKey, + GSIPK_clientId: baseEntry.GSIPK_clientId, + GSIPK_kid: isTokenClientPurposeEntry + ? baseEntry.GSIPK_kid + : makeGSIPKKid(kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId, + purposeId, + }), + GSIPK_purposeId: purposeId, + ...(purposeEntry && { + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: purposeEntry.purposeEserviceId, + }), + purposeState: purposeEntry.state, + purposeVersionId: purposeEntry.purposeVersionId, + }), + ...(purposeEntry && + agreementEntry && { + agreementId: extractAgreementIdFromAgreementPK(agreementEntry.PK), + agreementState: agreementEntry.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: agreementEntry.agreementDescriptorId, + }), + }), + ...(catalogEntry && { + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + }), + }; +}; diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts new file mode 100644 index 0000000000..bb8fce9f23 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -0,0 +1,2768 @@ +import { fail } from "assert"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockClient, + getMockTokenStatesClientEntry, + getMockTokenStatesClientPurposeEntry, + readAllPlatformStateItems, + readAllTokenStateItems, + getMockPurpose, + getMockPurposeVersion, + writePlatformPurposeEntry, + getMockAgreement, + writePlatformAgreementEntry, + getMockDescriptor, + writeCatalogEntry, + getMockKey, + writeTokenStateEntry, +} from "pagopa-interop-commons-test"; +import { + AuthorizationEventEnvelope, + Client, + ClientDeletedV2, + ClientId, + ClientKeyAddedV2, + ClientKeyDeletedV2, + ClientPurposeAddedV2, + ClientPurposeRemovedV2, + Descriptor, + generateId, + itemState, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKKid, + makePlatformStatesAgreementPK, + makePlatformStatesClientPK, + makePlatformStatesEServiceDescriptorPK, + makePlatformStatesPurposePK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, + PlatformStatesClientEntry, + PlatformStatesPurposeEntry, + Purpose, + PurposeId, + purposeVersionState, + TenantId, + toClientV2, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, +} from "pagopa-interop-models"; +import { handleMessageV2 } from "../src/consumerServiceV2.js"; +import { + clientKindToTokenGenerationStatesClientKind, + readClientEntry, + writeClientEntry, + writeTokenStateClientEntry, +} from "../src/utils.js"; +import { config } from "./utils.js"; + +describe("integration tests V2 events", async () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + const mockDate = new Date(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + describe("ClientKeyAdded", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const key = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [key], + purposes: [generateId()], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: key.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: client.purposes, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: key.kid, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(key.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should insert platform-states entry and insert token-generation-states client-kid-purpose entries if the client contains at least one purpose", async () => { + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + consumerId, + keys: [oldKey, addedKey], + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: addedKey.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose2.id, + }); + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry1.GSIPK_consumerId_eserviceId, + agreementId: agreement1.id, + agreementState: platformAgreementEntry1.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + descriptorState: previousDescriptorEntry1.state, + descriptorAudience: previousDescriptorEntry1.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry1.descriptorVoucherLifespan, + GSIPK_purposeId: purpose1.id, + purposeState: platformPurposeEntry1.state, + purposeVersionId: platformPurposeEntry1.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }), + updatedAt: new Date().toISOString(), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(4); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and insert token-generation-states client-kid-purpose entries", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + consumerId, + keys: [oldKey, addedKey], + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: addedKey.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id, purpose2.id], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose2.id, + }); + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry1.GSIPK_consumerId_eserviceId, + agreementId: agreement1.id, + agreementState: platformAgreementEntry1.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + descriptorState: previousDescriptorEntry1.state, + descriptorAudience: previousDescriptorEntry1.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry1.descriptorVoucherLifespan, + GSIPK_purposeId: purpose1.id, + purposeState: platformPurposeEntry1.state, + purposeVersionId: platformPurposeEntry1.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }), + updatedAt: new Date().toISOString(), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(4); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and update token-generation-states client-kid-purpose entries", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + consumerId, + keys: [oldKey, addedKey], + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: addedKey.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id, purpose2.id], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose2.id, + }); + const tokenClientKidPurposePK3 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK4 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }); + + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const gsiPKClientIdPurposeId3 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId4 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + const tokenClientPurposeEntry3: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK3), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId3, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry4: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK4), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId4, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry3, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry4, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry1.GSIPK_consumerId_eserviceId, + agreementId: agreement1.id, + agreementState: platformAgreementEntry1.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + descriptorState: previousDescriptorEntry1.state, + descriptorAudience: previousDescriptorEntry1.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry1.descriptorVoucherLifespan, + GSIPK_purposeId: purpose1.id, + purposeState: platformPurposeEntry1.state, + purposeVersionId: platformPurposeEntry1.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }), + updatedAt: new Date().toISOString(), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(4); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should insert platform-states entry and insert token-generation-states client-kid entry if the client does not contain purposes", async () => { + const messageVersion = 1; + + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [oldKey, addedKey], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: addedKey.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: oldKey.kid, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: messageVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }), + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + publicKey: addedKey.encodedPem, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry, expectedTokenClientEntry]) + ); + }); + + it("should update platform-states entry and insert token-generation-states client-kid entry", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [oldKey, addedKey], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: addedKey.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: oldKey.kid, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }), + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + publicKey: addedKey.encodedPem, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry, expectedTokenClientEntry]) + ); + }); + + it("should update platform-states entry and update token-generation-states client-kid entry", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [oldKey, addedKey], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: addedKey.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: oldKey.kid, + }); + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }); + const tokenClientEntry1: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + }; + const tokenClientEntry2: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK2), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); + await writeTokenStateClientEntry(tokenClientEntry2, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }), + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + publicKey: addedKey.encodedPem, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry1, expectedTokenClientEntry]) + ); + }); + }); + + describe("ClientKeyDeleted", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + const kidToRemove = "removed kid"; + + const payload: ClientKeyDeletedV2 = { + client: toClientV2(client), + kid: kidToRemove, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyDeleted", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: kidToRemove, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kidToRemove), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should insert platform-states entry and delete token-generation-states entries for that kid", async () => { + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + const kidToRemove = "removed kid"; + + const payload: ClientKeyDeletedV2 = { + client: toClientV2(client), + kid: kidToRemove, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyDeleted", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // token-generation-states + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kidToRemove, + purposeId, + }); + + const tokenClientPurposeEntryWithKid: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId: client.id, + }; + const tokenClientPurposeEntryWithOtherKid: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(), + GSIPK_clientId: client.id, + }; + + await writeTokenStateEntry( + tokenClientPurposeEntryWithKid, + dynamoDBClient + ); + await writeTokenStateEntry( + tokenClientPurposeEntryWithOtherKid, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + clientPurposesIds: [], + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([ + tokenClientPurposeEntryWithOtherKid, + ]); + }); + + it("should update platform-states entry and delete token-generation-states entries for that kid", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + const kidToRemove = "removed kid"; + const otherKid = "other kid"; + + const payload: ClientKeyDeletedV2 = { + client: toClientV2(client), + kid: kidToRemove, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyDeleted", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purposeId], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kidToRemove, + purposeId, + }); + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: otherKid, + }); + + const tokenClientPurposeEntryWithKid: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId: client.id, + }; + + const tokenClientEntryWithOtherKid: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(otherKid), + }; + + await writeTokenStateEntry( + tokenClientPurposeEntryWithKid, + dynamoDBClient + ); + await writeTokenStateClientEntry( + tokenClientEntryWithOtherKid, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntryWithOtherKid]); + }); + }); + + describe("ClientPurposeAdded", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeAddedV2 = { + purposeId, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should update only platform-states entry if there are no keys in the client", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeAddedV2 = { + purposeId, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + + updatedAt: new Date().toISOString(), + clientPurposesIds: [purposeId], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientPurposeEntry = getMockTokenStatesClientPurposeEntry(); + const tokenClientEntry = getMockTokenStatesClientEntry(); + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientPurposeEntry, tokenClientEntry]) + ); + }); + + it("should update platform-states entry and convert client-kid entries to client-kid-purpose entries in token-generation-states table", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const client: Client = { + ...getMockClient(), + consumerId, + purposes: [purpose.id], + }; + + const payload: ClientPurposeAddedV2 = { + purposeId: purpose.id, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose.versions[0].id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry, dynamoDBClient); + + const agreement = getMockAgreement(); + const platformAgreementEntry: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + await writePlatformAgreementEntry(platformAgreementEntry, dynamoDBClient); + + const descriptor: Descriptor = { + ...getMockDescriptor(), + id: agreement.descriptorId, + }; + const previousDescriptorEntry: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose.eserviceId, + descriptorId: descriptor.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const kid1 = "KID1"; + const kid2 = "KID2"; + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: kid1, + }); + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: kid2, + }); + + const tokenClientEntry1: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK1), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + }; + const tokenClientEntry2: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK2), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + }; + const tokenClientPurposeEntryWithOtherClient = + getMockTokenStatesClientPurposeEntry(); + + await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); + await writeTokenStateClientEntry(tokenClientEntry2, dynamoDBClient); + await writeTokenStateEntry( + tokenClientPurposeEntryWithOtherClient, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const newTokenClientPurposeEntryData = { + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose.id, + }), + GSIPK_purposeId: purpose.id, + purposeState: platformPurposeEntry.state, + purposeVersionId: platformPurposeEntry.purposeVersionId, + GSIPK_consumerId_eserviceId: + platformAgreementEntry.GSIPK_consumerId_eserviceId, + agreementId: agreement.id, + agreementState: platformAgreementEntry.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose.eserviceId, + descriptorId: descriptor.id, + }), + descriptorState: previousDescriptorEntry.state, + descriptorAudience: previousDescriptorEntry.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientEntry1, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose.id, + kid: kid1, + }), + }; + + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientEntry2, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose.id, + kid: kid2, + }), + }; + + expect(retrievedTokenEntries).toHaveLength(3); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntryWithOtherClient, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and insert client-kid-purpose entries to token-generation-states table", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + const client: Client = { + ...getMockClient(), + consumerId, + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientPurposeAddedV2 = { + purposeId: purpose2.id, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const kid1 = "KID1"; + const kid2 = "KID2"; + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid1, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid2, + purposeId: purpose1.id, + }); + + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientEntryWithOtherClient = getMockTokenStatesClientEntry(); + + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenStateClientEntry( + tokenClientEntryWithOtherClient, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const newTokenClientPurposeEntryData = { + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid1, + }), + }; + + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid2, + }), + }; + + expect(retrievedTokenEntries).toHaveLength(5); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientEntryWithOtherClient, + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and update client-kid-purpose entries to token-generation-states table", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + const client: Client = { + ...getMockClient(), + consumerId, + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientPurposeAddedV2 = { + purposeId: purpose2.id, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const kid1 = "KID1"; + const kid2 = "KID2"; + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid1, + purposeId: purpose1.id, + }); + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid2, + purposeId: purpose1.id, + }); + const tokenClientKidPK3 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid1, + purposeId: purpose2.id, + }); + const tokenClientKidPK4 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid2, + purposeId: purpose2.id, + }); + + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK1), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK2), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry3: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK3), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose2.id, + }; + const tokenClientPurposeEntry4: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK4), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose2.id, + }; + const tokenClientEntryWithOtherClient = getMockTokenStatesClientEntry(); + + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry3, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry4, dynamoDBClient); + await writeTokenStateClientEntry( + tokenClientEntryWithOtherClient, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const newTokenClientPurposeEntryData = { + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid1, + }), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid2, + }), + }; + + expect(retrievedTokenEntries).toHaveLength(5); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientEntryWithOtherClient, + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + }); + + describe("ClientPurposeRemoved", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeRemovedV2 = { + purposeId, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeRemoved", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should do no operation if the purpose platform-states entry doesn't exist and the token-generation-states entries aren't associated to the purpose id in the message", async () => { + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeRemovedV2 = { + purposeId, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeRemoved", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should update platform-states entry and delete token-generation-states entries for that purpose", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const purposeId1 = generateId(); + const purposeId2 = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId1], + }; + + const payload: ClientPurposeRemovedV2 = { + purposeId: purposeId2, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeRemoved", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purposeId1, purposeId2], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const mockClientKidPurpose1 = "mockClientKidPurpose1"; + const mockClientKidPurpose2 = "mockClientKidPurpose2"; + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: mockClientKidPurpose1, + purposeId: purposeId1, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: mockClientKidPurpose2, + purposeId: purposeId2, + }); + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purposeId1, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purposeId2, + }); + + const tokenClientEntry = getMockTokenStatesClientEntry(); + + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(mockClientKidPurpose1), + GSIPK_purposeId: purposeId1, + }; + + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(mockClientKidPurpose2), + GSIPK_purposeId: purposeId2, + }; + + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry, tokenClientPurposeEntry1]) + ); + }); + }); + + describe("ClientDeleted", () => { + it("should delete platform-states entry and token-generation-states entries", async () => { + const messageVersion = 2; + + const purposeId = generateId(); + const client = getMockClient(); + + const payload: ClientDeletedV2 = { + client: toClientV2(client), + clientId: client.id, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientDeleted", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + const otherClientId = generateId(); + + // platform-states + const pk1PlatformStates = makePlatformStatesClientPK(client.id); + const clientPlatformStateEntry1: PlatformStatesClientEntry = { + PK: pk1PlatformStates, + version: 1, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + + const pk2PlatformStates = makePlatformStatesClientPK(otherClientId); + const clientPlatformStateEntry2: PlatformStatesClientEntry = { + PK: pk2PlatformStates, + version: 1, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(clientPlatformStateEntry1, dynamoDBClient); + await writeClientEntry(clientPlatformStateEntry2, dynamoDBClient); + + // token-generation-states + const pkTokenStates1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: "kid", + purposeId, + }); + + const pkTokenStates2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: otherClientId, + kid: "kid", + purposeId, + }); + + const clientPurposeTokenStateEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(pkTokenStates1), + GSIPK_clientId: client.id, + }; + + const otherClientPurposeTokenStateEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(pkTokenStates2), + GSIPK_clientId: otherClientId, + }; + + await writeTokenStateEntry(clientPurposeTokenStateEntry, dynamoDBClient); + await writeTokenStateEntry( + otherClientPurposeTokenStateEntry, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntries = await readAllPlatformStateItems( + dynamoDBClient + ); + expect(retrievedPlatformStatesEntries).toEqual([ + clientPlatformStateEntry2, + ]); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([ + otherClientPurposeTokenStateEntry, + ]); + }); + }); +}); diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts new file mode 100644 index 0000000000..7d3e7b2b48 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -0,0 +1,779 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { fail } from "assert"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockAgreementEntry, + getMockKey, + getMockPlatformStatesClientEntry, + getMockPurposeVersion, + getMockTokenStatesClientEntry, + getMockTokenStatesClientPurposeEntry, + getMockPurpose, + getMockClient, + readAllPlatformStateItems, + readAllTokenStateItems, + writeCatalogEntry, + writePlatformAgreementEntry, + writePlatformPurposeEntry, + writeTokenStateEntry, + getMockAgreement, + getMockDescriptor, + getMockEService, +} from "pagopa-interop-commons-test"; +import { + Agreement, + AgreementId, + Client, + ClientId, + clientKindTokenStates, + DescriptorId, + EService, + EServiceId, + generateId, + GSIPKKid, + itemState, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKKid, + makePlatformStatesAgreementPK, + makePlatformStatesClientPK, + makePlatformStatesEServiceDescriptorPK, + makePlatformStatesPurposePK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, + PlatformStatesClientEntry, + PlatformStatesPurposeEntry, + Purpose, + PurposeId, + purposeVersionState, + TenantId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesGenericEntry, + unsafeBrandId, +} from "pagopa-interop-models"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { z } from "zod"; +import { + cleanClientPurposeIdsInPlatformStatesEntry, + convertEntriesToClientKidInTokenGenerationStates, + deleteClientEntryFromPlatformStates, + deleteClientEntryFromTokenGenerationStatesTable, + deleteEntriesFromTokenStatesByClient, + deleteEntriesFromTokenStatesByKid, + deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable, + readClientEntriesInTokenGenerationStates, + readClientEntry, + readPlatformAgreementEntryByGSIPKConsumerIdEServiceId, + retrievePlatformStatesByPurpose, + updateTokenDataForSecondRetrieval, + upsertPlatformClientEntry, + upsertTokenClientKidEntry, + upsertTokenStateClientPurposeEntry, + writeClientEntry, + writeTokenStateClientEntry, +} from "../src/utils.js"; +import { config } from "./utils.js"; + +describe("utils", () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + const mockDate = new Date(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + it("deleteEntriesFromTokenStatesByKid", async () => { + const kid = unsafeBrandId("mock kid"); + const clientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + GSIPK_kid: kid, + }; + + const clientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + GSIPK_kid: kid, + }; + + const otherClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + }; + + await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + await writeTokenStateEntry(clientPurposeEntry, dynamoDBClient); + await writeTokenStateEntry(otherClientPurposeEntry, dynamoDBClient); + + await deleteEntriesFromTokenStatesByKid(kid, dynamoDBClient); + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual([otherClientPurposeEntry]); + }); + + it("deleteClientEntryFromPlatformStates", async () => { + const pk1 = makePlatformStatesClientPK(generateId()); + const pk2 = makePlatformStatesClientPK(generateId()); + + const clientEntry1: PlatformStatesClientEntry = { + ...getMockPlatformStatesClientEntry(pk1), + }; + const clientEntry2: PlatformStatesClientEntry = { + ...getMockPlatformStatesClientEntry(pk2), + }; + + await writeClientEntry(clientEntry1, dynamoDBClient); + await writeClientEntry(clientEntry2, dynamoDBClient); + + await deleteClientEntryFromPlatformStates(pk1, dynamoDBClient); + + const res = await readAllPlatformStateItems(dynamoDBClient); + + expect(res).toEqual([clientEntry2]); + }); + + it("deleteEntriesFromTokenStatesByClient", async () => { + const GSIPK_clientId = generateId(); + + const clientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + GSIPK_clientId, + }; + + const clientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + GSIPK_clientId, + }; + + const otherClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + }; + await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + await writeTokenStateEntry(clientPurposeEntry, dynamoDBClient); + await writeTokenStateEntry(otherClientPurposeEntry, dynamoDBClient); + + await deleteEntriesFromTokenStatesByClient(GSIPK_clientId, dynamoDBClient); + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual([otherClientPurposeEntry]); + }); + + describe("deleteClientEntryFromTokenGenerationStatesTable", () => { + it("clientKid entry", async () => { + const clientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + }; + + const clientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + }; + + await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + await writeTokenStateEntry(clientPurposeEntry, dynamoDBClient); + + await deleteClientEntryFromTokenGenerationStatesTable( + clientEntry, + dynamoDBClient + ); + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual([clientPurposeEntry]); + }); + it("clientKidPurpose entry", async () => { + const clientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + }; + + const clientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + }; + + await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + await writeTokenStateEntry(clientPurposeEntry, dynamoDBClient); + + await deleteClientEntryFromTokenGenerationStatesTable( + clientPurposeEntry, + dynamoDBClient + ); + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual([clientEntry]); + }); + }); + + it("readClientEntry", async () => { + const clientEntry1 = getMockPlatformStatesClientEntry(); + + const clientEntry2 = getMockPlatformStatesClientEntry(); + + await writeClientEntry(clientEntry1, dynamoDBClient); + await writeClientEntry(clientEntry2, dynamoDBClient); + + const res = await readClientEntry(clientEntry1.PK, dynamoDBClient); + + expect(res).toEqual(clientEntry1); + }); + + it("deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable", async () => { + const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ + clientId: generateId(), + purposeId: generateId(), + }); + const clientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + GSIPK_clientId_purposeId, + }; + + const clientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + GSIPK_clientId_purposeId, + }; + + const clientPurposeEntry3 = getMockTokenStatesClientPurposeEntry(); + + await writeTokenStateEntry(clientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(clientPurposeEntry2, dynamoDBClient); + await writeTokenStateEntry(clientPurposeEntry3, dynamoDBClient); + + await deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable( + GSIPK_clientId_purposeId, + dynamoDBClient + ); + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual([clientPurposeEntry3]); + }); + + it("convertEntriesToClientKidInTokenGenerationStates", async () => { + const clientId = generateId(); + const kid1 = "kid1"; + const kid2 = "kid2"; + + const purposeId = generateId(); + const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ + clientId, + purposeId, + }); + + const pk1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid: kid1, + purposeId, + }); + const clientKidPurposeEntry1: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(pk1), + GSIPK_clientId_purposeId, + GSIPK_clientId: clientId, + GSIPK_kid: unsafeBrandId(kid1), + }; + + const pk2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid: kid2, + purposeId, + }); + const clientKidPurposeEntry2: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(pk2), + GSIPK_clientId_purposeId, + GSIPK_clientId: clientId, + GSIPK_kid: unsafeBrandId(kid2), + }; + + const clientKidPurposeEntry3 = getMockTokenStatesClientPurposeEntry(); + + await writeTokenStateEntry(clientKidPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(clientKidPurposeEntry2, dynamoDBClient); + await writeTokenStateEntry(clientKidPurposeEntry3, dynamoDBClient); + + await convertEntriesToClientKidInTokenGenerationStates( + GSIPK_clientId_purposeId, + dynamoDBClient + ); + + const expectedEntry1: TokenGenerationStatesClientEntry = { + consumerId: clientKidPurposeEntry1.consumerId, + updatedAt: new Date().toISOString(), + PK: makeTokenGenerationStatesClientKidPK({ clientId, kid: kid1 }), + clientKind: clientKindTokenStates.consumer, + publicKey: clientKidPurposeEntry1.publicKey, + GSIPK_clientId: clientId, + GSIPK_kid: unsafeBrandId(kid1), + }; + + const expectedEntry2: TokenGenerationStatesClientEntry = { + consumerId: clientKidPurposeEntry2.consumerId, + updatedAt: new Date().toISOString(), + PK: makeTokenGenerationStatesClientKidPK({ clientId, kid: kid2 }), + clientKind: "CONSUMER", + publicKey: clientKidPurposeEntry1.publicKey, + GSIPK_clientId: clientId, + GSIPK_kid: unsafeBrandId(kid2), + }; + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual( + expect.arrayContaining([ + expectedEntry2, + expectedEntry1, + clientKidPurposeEntry3, + ]) + ); + }); + + describe("writeTokenStateClientEntry", () => { + it("should succeed if the entry doesn't exist", async () => { + const clientEntry = getMockTokenStatesClientEntry(); + await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual([clientEntry]); + }); + + it("should throw error if the entry already exists", async () => { + const clientEntry = getMockTokenStatesClientEntry(); + await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + + expect( + writeTokenStateClientEntry(clientEntry, dynamoDBClient) + ).rejects.toThrowError(); + }); + }); + + it("readPlatformAgreementEntryByGSIPKConsumerIdEServiceId", async () => { + const pk1 = makePlatformStatesAgreementPK(generateId()); + const pk2 = makePlatformStatesAgreementPK(generateId()); + const pk3 = makePlatformStatesAgreementPK(generateId()); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + + const threeHoursAgo = new Date(); + threeHoursAgo.setHours(threeHoursAgo.getHours() - 3); + + const agreementEntry1: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(pk1, GSIPK_consumerId_eserviceId), + GSISK_agreementTimestamp: threeHoursAgo.toISOString(), + }; + + const agreementEntry2: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(pk2, GSIPK_consumerId_eserviceId), + GSISK_agreementTimestamp: new Date().toISOString(), + }; + + const agreementEntry3 = getMockAgreementEntry(pk3); + + await writePlatformAgreementEntry(agreementEntry1, dynamoDBClient); + await writePlatformAgreementEntry(agreementEntry2, dynamoDBClient); + await writePlatformAgreementEntry(agreementEntry3, dynamoDBClient); + + const res = await readPlatformAgreementEntryByGSIPKConsumerIdEServiceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(res).toEqual(agreementEntry2); + }); + + describe("upsertTokenStateClientPurposeEntry", () => { + it("write", async () => { + const clientKidPurposeEntry = getMockTokenStatesClientPurposeEntry(); + + const resultBefore = await readAllTokenStateItems(dynamoDBClient); + expect(resultBefore).toEqual([]); + + await upsertTokenStateClientPurposeEntry( + clientKidPurposeEntry, + dynamoDBClient + ); + + const resultAfter = await readAllTokenStateItems(dynamoDBClient); + expect(resultAfter).toEqual([clientKidPurposeEntry]); + }); + it("update", async () => { + const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + descriptorState: itemState.active, + }; + await writeTokenStateEntry(clientKidPurposeEntry, dynamoDBClient); + + const updatedEntry: TokenGenerationStatesClientPurposeEntry = { + ...clientKidPurposeEntry, + descriptorState: itemState.inactive, + }; + await upsertTokenStateClientPurposeEntry(updatedEntry, dynamoDBClient); + + const resultAfter = await readAllTokenStateItems(dynamoDBClient); + expect(resultAfter).toEqual([updatedEntry]); + }); + }); + + it("readClientEntriesInTokenGenerationStates", async () => { + const clientId = generateId(); + const pk1 = makeTokenGenerationStatesClientKidPK({ clientId, kid: "" }); + const pk2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid: "", + purposeId: generateId(), + }); + + const GSIPK_clientId = clientId; + + const clientKidEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(pk1), + GSIPK_clientId, + }; + + const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(pk2), + GSIPK_clientId, + }; + + await writeTokenStateClientEntry(clientKidEntry, dynamoDBClient); + await writeTokenStateEntry(clientKidPurposeEntry, dynamoDBClient); + + const res = await readClientEntriesInTokenGenerationStates( + GSIPK_clientId, + dynamoDBClient + ); + + expect(res).toEqual( + expect.arrayContaining([clientKidEntry, clientKidPurposeEntry]) + ); + }); + + it("cleanClientPurposeIdsInPlatformStatesEntry", async () => { + const clientEntry1: PlatformStatesClientEntry = { + ...getMockPlatformStatesClientEntry(), + clientPurposesIds: [generateId(), generateId()], + }; + const clientEntry2: PlatformStatesClientEntry = { + ...getMockPlatformStatesClientEntry(), + clientPurposesIds: [generateId(), generateId()], + }; + + await writeClientEntry(clientEntry1, dynamoDBClient); + await writeClientEntry(clientEntry2, dynamoDBClient); + + await cleanClientPurposeIdsInPlatformStatesEntry( + clientEntry1.PK, + clientEntry1.version + 1, + dynamoDBClient + ); + + const res = await readAllPlatformStateItems(dynamoDBClient); + expect(res).toEqual( + expect.arrayContaining([ + { + ...clientEntry1, + clientPurposesIds: [], + version: clientEntry1.version + 1, + }, + clientEntry2, + ]) + ); + }); + + it("retrievePlatformStatesByPurpose", async () => { + const purposeId = generateId(); + const purposePK = makePlatformStatesPurposePK(purposeId); + const agreementId = generateId(); + const eserviceId = generateId(); + const descriptorId = generateId(); + const consumerId = generateId(); + + const purposeEntry: PlatformStatesPurposeEntry = { + PK: purposePK, + state: itemState.inactive, + purposeVersionId: generateId(), + purposeEserviceId: eserviceId, + purposeConsumerId: consumerId, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writePlatformPurposeEntry(purposeEntry, dynamoDBClient); + + const agreementPK = makePlatformStatesAgreementPK(agreementId); + const agreementEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementPK), + agreementDescriptorId: descriptorId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }), + }; + + await writePlatformAgreementEntry(agreementEntry, dynamoDBClient); + + const catalogPK = makePlatformStatesEServiceDescriptorPK({ + eserviceId, + descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: catalogPK, + state: itemState.active, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + descriptorVoucherLifespan: 60, + version: 3, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + const res = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + expect(res).toEqual({ + purposeEntry, + agreementEntry, + catalogEntry, + }); + }); + + describe("upsertPlatformClientEntry", () => { + it("write", async () => { + const clientEntry: PlatformStatesClientEntry = { + ...getMockPlatformStatesClientEntry(), + clientKind: clientKindTokenStates.consumer, + }; + + const resultBefore = await readAllPlatformStateItems(dynamoDBClient); + expect(resultBefore).toEqual([]); + + await upsertPlatformClientEntry(clientEntry, dynamoDBClient); + + const resultAfter = await readAllPlatformStateItems(dynamoDBClient); + expect(resultAfter).toEqual([clientEntry]); + }); + it("update", async () => { + const clientEntry: PlatformStatesClientEntry = { + ...getMockPlatformStatesClientEntry(), + clientKind: clientKindTokenStates.consumer, + }; + + await writeClientEntry(clientEntry, dynamoDBClient); + + const updatedEntry: PlatformStatesClientEntry = { + ...clientEntry, + clientKind: clientKindTokenStates.api, + }; + await upsertPlatformClientEntry(updatedEntry, dynamoDBClient); + + const resultAfter = await readAllPlatformStateItems(dynamoDBClient); + expect(resultAfter).toEqual([updatedEntry]); + }); + }); + + describe("upsertTokenClientKidEntry", () => { + it("write", async () => { + const clientKidEntry = getMockTokenStatesClientEntry(); + + const resultBefore = await readAllTokenStateItems(dynamoDBClient); + expect(resultBefore).toEqual([]); + + await upsertTokenClientKidEntry(clientKidEntry, dynamoDBClient); + + const resultAfter = await readAllTokenStateItems(dynamoDBClient); + expect(resultAfter).toEqual([clientKidEntry]); + }); + it("update", async () => { + const clientKidEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + clientKind: clientKindTokenStates.consumer, + }; + await writeTokenStateClientEntry(clientKidEntry, dynamoDBClient); + + const updatedEntry: TokenGenerationStatesClientEntry = { + ...clientKidEntry, + clientKind: clientKindTokenStates.api, + }; + await upsertTokenClientKidEntry(updatedEntry, dynamoDBClient); + + const resultAfter = await readAllTokenStateItems(dynamoDBClient); + expect(resultAfter).toEqual([updatedEntry]); + }); + }); + + it("parsing TokenGenerationStatesGenericEntry", () => { + const clientKidEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + }; + const entries1 = z + .array(TokenGenerationStatesGenericEntry) + .safeParse([clientKidEntry]); + + expect(entries1.data![0]).toEqual(clientKidEntry); + + const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + }; + const entries2 = z + .array(TokenGenerationStatesGenericEntry) + .safeParse([clientKidPurposeEntry]); + + expect(entries2.data![0]).toEqual(clientKidPurposeEntry); + + const clientId = generateId(); + const clientKidPurposeEntryWithUndefined: TokenGenerationStatesClientPurposeEntry = + { + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid: "kid", + purposeId: generateId(), + }), + consumerId: generateId(), + clientKind: clientKindTokenStates.api, + publicKey: "publicKey", + GSIPK_clientId: generateId(), + GSIPK_kid: unsafeBrandId("kid"), + updatedAt: new Date().toISOString(), + }; + const entries3 = z + .array(TokenGenerationStatesGenericEntry) + .safeParse([clientKidPurposeEntryWithUndefined]); + + expect(entries3.data![0]).toEqual(clientKidPurposeEntryWithUndefined); + }); + + it("updateTokenDataForSecondRetrieval", async () => { + const consumerId = generateId(); + const descriptor = getMockDescriptor(); + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const purpose: Purpose = { + ...getMockPurpose(), + consumerId, + eserviceId: eservice.id, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const key = getMockKey(); + const client: Client = { + ...getMockClient(), + consumerId, + keys: [key], + purposes: [purpose.id], + }; + const agreement: Agreement = { + ...getMockAgreement(), + eserviceId: eservice.id, + descriptorId: descriptor.id, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId: client.id, + kid: key.kid, + purposeId: purpose.id, + } + ); + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(key.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose.id, + }), + GSIPK_purposeId: purpose.id, + purposeVersionId: purpose.versions[0].id, + descriptorAudience: descriptor.audience, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: eservice.id, + }), + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }), + }; + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + + const platformPurposeEntry: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose.id), + version: 1, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose.versions[0].id, + purposeEserviceId: eservice.id, + purposeConsumerId: consumerId, + }; + + const platformAgreementEntry: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement.id), + version: 1, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: eservice.id, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + + const platformCatalogEntry: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose.eserviceId, + descriptorId: descriptor.id, + }), + state: itemState.inactive, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + + await updateTokenDataForSecondRetrieval({ + dynamoDBClient, + entry: tokenClientPurposeEntry, + purposeEntry: platformPurposeEntry, + agreementEntry: platformAgreementEntry, + catalogEntry: platformCatalogEntry, + }); + + const retrievedTokenEntries = await readAllTokenStateItems(dynamoDBClient); + const expectedTokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry, + purposeState: itemState.inactive, + agreementState: itemState.inactive, + descriptorState: itemState.inactive, + }; + expect(retrievedTokenEntries).toHaveLength(1); + expect(retrievedTokenEntries[0]).toEqual(expectedTokenClientPurposeEntry); + }); +}); diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 15a439945e..ff2f045b04 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -51,6 +51,13 @@ import { GSIPKConsumerIdEServiceId, PlatformStatesAgreementEntry, PlatformStatesAgreementPK, + makeGSIPKKid, + TokenGenerationStatesClientKidPK, + TokenGenerationStatesClientEntry, + makeTokenGenerationStatesClientKidPK, + PlatformStatesClientPK, + PlatformStatesClientEntry, + makePlatformStatesClientPK, } from "pagopa-interop-models"; import { AuthData } from "pagopa-interop-commons"; import { z } from "zod"; @@ -334,13 +341,14 @@ export const getMockTokenStatesClientPurposeEntry = ( const descriptorId = generateId(); const agreementId = generateId(); const purposeVersionId = generateId(); + const kid = `kid ${Math.random()}`; return { PK: tokenStateEntryPK || makeTokenGenerationStatesClientKidPurposePK({ clientId, - kid: `kid ${Math.random()}`, + kid, purposeId, }), descriptorState: itemState.inactive, @@ -357,8 +365,8 @@ export const getMockTokenStatesClientPurposeEntry = ( clientKind: clientKindTokenStates.consumer, publicKey: "PEM", GSIPK_clientId: clientId, - GSIPK_kid: "KID", - agreementState: "ACTIVE", + GSIPK_kid: makeGSIPKKid(kid), + agreementState: itemState.active, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ eserviceId, descriptorId, @@ -389,3 +397,38 @@ export const getMockAgreementEntry = ( GSISK_agreementTimestamp: new Date().toISOString(), agreementDescriptorId: generateId(), }); + +export const getMockTokenStatesClientEntry = ( + tokenStateEntryPK?: TokenGenerationStatesClientKidPK +): TokenGenerationStatesClientEntry => { + const clientId = generateId(); + const consumerId = generateId(); + const kid = `kid ${Math.random()}`; + + return { + PK: + tokenStateEntryPK || + makeTokenGenerationStatesClientKidPK({ + clientId, + kid, + }), + updatedAt: new Date().toISOString(), + consumerId, + clientKind: clientKindTokenStates.consumer, + publicKey: "PEM", + GSIPK_clientId: clientId, + GSIPK_kid: makeGSIPKKid(kid), + }; +}; + +export const getMockPlatformStatesClientEntry = ( + pk?: PlatformStatesClientPK +): PlatformStatesClientEntry => ({ + PK: pk || makePlatformStatesClientPK(generateId()), + version: 0, + state: "ACTIVE", + updatedAt: new Date().toISOString(), + clientKind: "CONSUMER", + clientConsumerId: generateId(), + clientPurposesIds: [], +}); diff --git a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts index 36a78b5c87..18f5e7df98 100644 --- a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts +++ b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { AttributeValue, DynamoDBClient, @@ -17,7 +15,11 @@ import { GSIPKConsumerIdEServiceId, GSIPKEServiceIdDescriptorId, PlatformStatesCatalogEntry, + PlatformStatesGenericEntry, TokenGenerationStatesClientPurposeEntry, + PlatformStatesPurposeEntry, + PlatformStatesAgreementEntry, + TokenGenerationStatesGenericEntry, } from "pagopa-interop-models"; import { unmarshall } from "@aws-sdk/util-dynamodb"; import { z } from "zod"; @@ -138,7 +140,7 @@ export const writeTokenStateEntry = async ( export const readAllTokenStateItems = async ( dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const readInput: ScanInput = { TableName: "token-generation-states", }; @@ -153,7 +155,7 @@ export const readAllTokenStateItems = async ( const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenStateEntries = z - .array(TokenGenerationStatesClientPurposeEntry) + .array(TokenGenerationStatesGenericEntry) .safeParse(unmarshalledItems); if (!tokenStateEntries.success) { @@ -167,6 +169,37 @@ export const readAllTokenStateItems = async ( } }; +export const readAllPlatformStateItems = async ( + dynamoDBClient: DynamoDBClient +): Promise => { + const readInput: ScanInput = { + TableName: "platform-states", + }; + const commandQuery = new ScanCommand(readInput); + const data: ScanCommandOutput = await dynamoDBClient.send(commandQuery); + + if (!data.Items) { + throw genericInternalError( + `Unable to read platform state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const platformStateEntries = z + .array(PlatformStatesGenericEntry) + .safeParse(unmarshalledItems); + + if (!platformStateEntries.success) { + throw genericInternalError( + `Unable to parse platform state entry item: result ${JSON.stringify( + platformStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + return platformStateEntries.data; + } +}; + export const readTokenStateEntriesByEserviceIdAndDescriptorId = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, dynamoDBClient: DynamoDBClient @@ -324,3 +357,73 @@ export const writeCatalogEntry = async ( const command = new PutItemCommand(input); await dynamoDBClient.send(command); }; + +export const writePlatformPurposeEntry = async ( + purposeEntry: PlatformStatesPurposeEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: purposeEntry.PK, + }, + state: { + S: purposeEntry.state, + }, + purposeVersionId: { + S: purposeEntry.purposeVersionId, + }, + purposeEserviceId: { + S: purposeEntry.purposeEserviceId, + }, + purposeConsumerId: { + S: purposeEntry.purposeConsumerId, + }, + version: { + N: purposeEntry.version.toString(), + }, + updatedAt: { + S: purposeEntry.updatedAt, + }, + }, + TableName: "platform-states", + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const writePlatformAgreementEntry = async ( + agreementEntry: PlatformStatesAgreementEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: agreementEntry.PK, + }, + state: { + S: agreementEntry.state, + }, + version: { + N: agreementEntry.version.toString(), + }, + updatedAt: { + S: agreementEntry.updatedAt, + }, + GSIPK_consumerId_eserviceId: { + S: agreementEntry.GSIPK_consumerId_eserviceId, + }, + GSISK_agreementTimestamp: { + S: agreementEntry.GSISK_agreementTimestamp, + }, + agreementDescriptorId: { + S: agreementEntry.agreementDescriptorId, + }, + }, + TableName: "platform-states", + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index d2e9470fb0..84d0dd1708 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -101,16 +101,20 @@ export type GSIPKConsumerIdEServiceId = z.infer< typeof GSIPKConsumerIdEServiceId >; +export const clientKidPurposePrefix = "CLIENTKIDPURPOSE#"; export const TokenGenerationStatesClientKidPurposePK = z .string() - .brand(`CLIENTKIDPURPOSE#clientId#kid#purposeId`); + .refine((pk) => pk.startsWith(clientKidPurposePrefix)) + .brand(`${clientKidPurposePrefix}clientId#kid#purposeId`); export type TokenGenerationStatesClientKidPurposePK = z.infer< typeof TokenGenerationStatesClientKidPurposePK >; +export const clientKidPrefix = "CLIENTKID#"; export const TokenGenerationStatesClientKidPK = z .string() - .brand(`CLIENTKID#clientId#kid`); + .refine((pk) => pk.startsWith(clientKidPrefix)) + .brand(`${clientKidPrefix}clientId#kid`); export type TokenGenerationStatesClientKidPK = z.infer< typeof TokenGenerationStatesClientKidPK >; @@ -125,6 +129,9 @@ export type GSIPKEServiceIdDescriptorId = z.infer< export const GSIPKClientIdPurposeId = z.string().brand(`clientId#purposeId`); export type GSIPKClientIdPurposeId = z.infer; +export const GSIPKKid = z.string().brand("kid"); +export type GSIPKKid = z.infer; + type IDS = | CorrelationId | EServiceId @@ -153,7 +160,8 @@ type IDS = | TokenGenerationStatesClientKidPurposePK | TokenGenerationStatesClientKidPK | GSIPKEServiceIdDescriptorId - | GSIPKClientIdPurposeId; + | GSIPKClientIdPurposeId + | GSIPKKid; // This function is used to generate a new ID for a new object // it infers the type of the ID based on how is used the result diff --git a/packages/models/src/token-generation-readmodel/commons.ts b/packages/models/src/token-generation-readmodel/commons.ts index 86308beab5..ea1cc7ea38 100644 --- a/packages/models/src/token-generation-readmodel/commons.ts +++ b/packages/models/src/token-generation-readmodel/commons.ts @@ -16,6 +16,7 @@ import { GSIPKEServiceIdDescriptorId, GSIPKClientIdPurposeId, unsafeBrandId, + GSIPKKid, } from "../brandedIds.js"; export const makePlatformStatesEServiceDescriptorPK = ({ @@ -95,6 +96,9 @@ export const makeGSIPKClientIdPurposeId = ({ }): GSIPKClientIdPurposeId => unsafeBrandId(`${clientId}#${purposeId}`); +export const makeGSIPKKid = (kid: string): GSIPKKid => + unsafeBrandId(kid); + export const clientKindTokenStates = { consumer: "CONSUMER", api: "API", diff --git a/packages/models/src/token-generation-readmodel/platform-states-entry.ts b/packages/models/src/token-generation-readmodel/platform-states-entry.ts index e12c8ae6ec..4563e3b8db 100644 --- a/packages/models/src/token-generation-readmodel/platform-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/platform-states-entry.ts @@ -68,3 +68,12 @@ export const PlatformStatesClientEntry = PlatformStatesBaseEntry.extend({ export type PlatformStatesClientEntry = z.infer< typeof PlatformStatesClientEntry >; + +export const PlatformStatesGenericEntry = PlatformStatesCatalogEntry.or( + PlatformStatesAgreementEntry +) + .or(PlatformStatesPurposeEntry) + .or(PlatformStatesClientEntry); +export type PlatformStatesGenericEntry = z.infer< + typeof PlatformStatesGenericEntry +>; diff --git a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts index db65a91e19..c36a56cf9a 100644 --- a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts @@ -5,6 +5,7 @@ import { GSIPKClientIdPurposeId, GSIPKConsumerIdEServiceId, GSIPKEServiceIdDescriptorId, + GSIPKKid, PurposeId, PurposeVersionId, TenantId, @@ -19,7 +20,7 @@ const TokenGenerationStatesBaseEntry = z.object({ clientKind: ClientKindTokenStates, publicKey: z.string(), GSIPK_clientId: ClientId, - GSIPK_kid: z.string(), + GSIPK_kid: GSIPKKid, updatedAt: z.string().datetime(), }); type TokenGenerationStatesBaseEntry = z.infer< @@ -50,5 +51,11 @@ export const TokenGenerationStatesClientEntry = PK: TokenGenerationStatesClientKidPK, }); export type TokenGenerationStatesClientEntry = z.infer< - typeof TokenGenerationStatesClientPurposeEntry + typeof TokenGenerationStatesClientEntry +>; + +export const TokenGenerationStatesGenericEntry = + TokenGenerationStatesClientPurposeEntry.or(TokenGenerationStatesClientEntry); +export type TokenGenerationStatesGenericEntry = z.infer< + typeof TokenGenerationStatesGenericEntry >; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a9841b24c7..daedbfc6b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -716,9 +716,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -734,9 +731,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -4710,9 +4704,6 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} - '@types/uuid@9.0.8': - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} @@ -7440,10 +7431,6 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} - uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true - uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true @@ -11542,8 +11529,6 @@ snapshots: '@types/triple-beam@1.3.5': {} - '@types/uuid@9.0.8': {} - '@types/webidl-conversions@7.0.3': {} '@types/whatwg-url@11.0.5': @@ -14666,8 +14651,6 @@ snapshots: utils-merge@1.0.1: {} - uuid@10.0.0: {} - uuid@9.0.1: {} vary@1.1.2: {} From d0b6b0e4bc30020ee5b45cbe023c1565b4d60f87 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Thu, 7 Nov 2024 08:51:54 +0100 Subject: [PATCH 16/34] PIN-5566 - fixing dtd catalog exporter docker run (#1170) --- packages/dtd-catalog-exporter/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/dtd-catalog-exporter/package.json b/packages/dtd-catalog-exporter/package.json index 48dac47f26..4b8cbd21e8 100644 --- a/packages/dtd-catalog-exporter/package.json +++ b/packages/dtd-catalog-exporter/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "private": true, "description": "PagoPA Interoperability catalog exporter job", + "main": "dist", "type": "module", "scripts": { "test": "vitest", From 53c4fedb3fd00d767ff8109c2ee3b3f0ef0b1ffe Mon Sep 17 00:00:00 2001 From: Simone Camito <32327779+MalpenZibo@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:16:02 +0100 Subject: [PATCH 17/34] PIN 5615 - Fix wrong anac file download path (#1172) --- .../src/service/sftpService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/anac-certified-attributes-importer/src/service/sftpService.ts b/packages/anac-certified-attributes-importer/src/service/sftpService.ts index db998bf45f..48a1a1f384 100644 --- a/packages/anac-certified-attributes-importer/src/service/sftpService.ts +++ b/packages/anac-certified-attributes-importer/src/service/sftpService.ts @@ -29,7 +29,9 @@ export class SftpClient { logger.info(`Loading file ${fileName}`); - const file = await sftpClient.get(this.config.folderPath + fileName); + const file = await sftpClient.get( + [this.config.folderPath, fileName].join("/") + ); await sftpClient.end(); From ba191ef4f4c16b6743cc0bf3eb22a50cae15e1b5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 7 Nov 2024 15:35:06 +0100 Subject: [PATCH 18/34] PIN-5626 Fix logic for updated descriptor quotas (#1174) --- packages/authorization-updater/src/index.ts | 13 +++- .../test/authorizationUpdater.test.ts | 66 +++++++++++++++++-- 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/packages/authorization-updater/src/index.ts b/packages/authorization-updater/src/index.ts index 2ba671348e..0c88769cc6 100644 --- a/packages/authorization-updater/src/index.ts +++ b/packages/authorization-updater/src/index.ts @@ -103,6 +103,18 @@ export async function sendCatalogAuthUpdate( ); } ) + .with({ type: "EServiceDescriptorQuotasUpdated" }, async (msg) => { + const data = getDescriptorFromEvent(msg, decodedMessage.type); + await authService.updateEServiceState( + descriptorStateToClientState(data.descriptor.state), + data.descriptor.id, + data.eserviceId, + data.descriptor.audience, + data.descriptor.voucherLifespan, + logger, + correlationId + ); + }) .with( { type: P.union( @@ -122,7 +134,6 @@ export async function sendCatalogAuthUpdate( "EServiceRiskAnalysisAdded", "EServiceRiskAnalysisUpdated", "EServiceRiskAnalysisDeleted", - "EServiceDescriptorQuotasUpdated", "EServiceDescriptionUpdated" ), }, diff --git a/packages/authorization-updater/test/authorizationUpdater.test.ts b/packages/authorization-updater/test/authorizationUpdater.test.ts index d5a685494c..ab0c7a1d69 100644 --- a/packages/authorization-updater/test/authorizationUpdater.test.ts +++ b/packages/authorization-updater/test/authorizationUpdater.test.ts @@ -136,7 +136,7 @@ describe("Authorization Updater processMessage", () => { "EServiceDescriptorSuspended", "EServiceDescriptorActivated", ])( - "should correctly process a catalog message with type %t and call updateEServiceState", + "should correctly process a catalog message with type %s and call updateEServiceState", async (eventType) => { const descriptor: Descriptor = { ...getMockDescriptorPublished(), @@ -203,6 +203,62 @@ describe("Authorization Updater processMessage", () => { } ); + it("should correctly process a catalog message with type EServiceDescriptorQuotasUpdated and call updateEServiceState", async () => { + const descriptor: Descriptor = { + ...getMockDescriptorPublished(), + version: "1", + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const message: EServiceEventEnvelopeV2 = { + sequence_num: 1, + stream_id: eservice.id, + version: 1, + type: "EServiceDescriptorQuotasUpdated", + event_version: 2, + data: { + eservice: toEServiceV2(eservice), + descriptorId: descriptor.id, + }, + log_date: new Date(), + } as EServiceEventEnvelopeV2; + + await sendCatalogAuthUpdate( + message, + authorizationService, + genericLogger, + testCorrelationId + ); + + const expectedUpdateEServiceStatePayload = { + state: authorizationManagementApi.ClientComponentState.Values.ACTIVE, + descriptorId: descriptor.id, + audience: descriptor.audience, + voucherLifespan: descriptor.voucherLifespan, + }; + + expect( + authorizationManagementClients.purposeApiClient.updateEServiceState + ).toHaveBeenCalledTimes(1); + expect( + authorizationManagementClients.purposeApiClient.updateEServiceState + ).toHaveBeenCalledWith(expectedUpdateEServiceStatePayload, { + params: { eserviceId: eservice.id }, + withCredentials: true, + headers: testHeaders, + }); + + expect( + authorizationManagementClients.purposeApiClient.updateAgreementState + ).not.toHaveBeenCalled(); + expect( + authorizationManagementClients.purposeApiClient + .updateAgreementAndEServiceStates + ).not.toHaveBeenCalled(); + }); + it.each([ "AgreementSubmitted", "AgreementActivated", @@ -214,7 +270,7 @@ describe("Authorization Updater processMessage", () => { "AgreementUnsuspendedByProducer", "AgreementArchivedByConsumer", ])( - "Should correctly process an agreement messages with type %t and call updateAgreementState", + "Should correctly process an agreement messages with type %s and call updateAgreementState", async (eventType) => { const agreement: Agreement = getMockAgreement(); @@ -433,7 +489,7 @@ describe("Authorization Updater processMessage", () => { }); it.each(["DraftPurposeDeleted", "WaitingForApprovalPurposeDeleted"])( - "should correctly process purposes message with type %t and call deletePurposeFromClient", + "should correctly process purposes message with type %s and call deletePurposeFromClient", async (eventType) => { const purpose: Purpose = { ...getMockPurpose(), @@ -512,7 +568,7 @@ describe("Authorization Updater processMessage", () => { "PurposeVersionActivated", "PurposeArchived", ])( - "should correctly process purposes message with type %t and call updatePurposeState with ACTIVE state", + "should correctly process purposes message with type %s and call updatePurposeState with ACTIVE state", async (eventType) => { const purposeVersion: PurposeVersion = { ...getMockPurposeVersion(), @@ -575,7 +631,7 @@ describe("Authorization Updater processMessage", () => { "PurposeVersionActivated", "PurposeArchived", ])( - "should correctly process purposes message with type %t and call updatePurposeState with INACTIVE state", + "should correctly process purposes message with type %s and call updatePurposeState with INACTIVE state", async (eventType) => { const purposeVersion: PurposeVersion = { ...getMockPurposeVersion(), From 217c119a6be36900aa5a52c5763b430a6413bfbc Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Thu, 7 Nov 2024 16:32:25 +0100 Subject: [PATCH 19/34] PIN-5627 - small fixes in interop jobs (#1176) --- .../anac-certified-attributes-importer/.env | 16 ++++++------- .../aws.config.local | 4 ---- .../src/index.ts | 5 ++++ .../src/model/tenant.ts | 11 +++++++++ .../src/service/processor.ts | 20 ++++++++++------ .../src/service/readmodelQueriesService.ts | 23 +++++++++++-------- packages/dtd-catalog-exporter/package.json | 2 +- .../ivass-certified-attributes-importer/.env | 11 +++++---- .../src/index.ts | 5 ++++ .../src/model/tenant.ts | 11 +++++++++ .../src/service/processor.ts | 20 ++++++++-------- .../src/service/readModelQueriesService.ts | 17 ++++++++------ packages/one-trust-notices/.env | 4 ++-- packages/one-trust-notices/aws.config.local | 4 ++++ 14 files changed, 101 insertions(+), 52 deletions(-) delete mode 100644 packages/anac-certified-attributes-importer/aws.config.local create mode 100644 packages/anac-certified-attributes-importer/src/model/tenant.ts create mode 100644 packages/ivass-certified-attributes-importer/src/model/tenant.ts diff --git a/packages/anac-certified-attributes-importer/.env b/packages/anac-certified-attributes-importer/.env index b593a96e2b..405a18517a 100644 --- a/packages/anac-certified-attributes-importer/.env +++ b/packages/anac-certified-attributes-importer/.env @@ -5,14 +5,14 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 - -SFTP_HOST="" -SFTP_PORT=0 -SFTP_USERNAME="" -SFTP_PASSWORD="" + +SFTP_HOST="127.0.0.1" +SFTP_PORT=2022 +SFTP_USERNAME="test" +SFTP_PASSWORD="test" SFTP_FILENAME_PREFIX="" SFTP_PATH="" -FORCE_REMOTE_FILE_NAME="" +FORCE_REMOTE_FILE_NAME="anac-test-csv.csv" INTERNAL_JWT_KID=ffcc9b5b-4612-49b1-9374-9d203a3834f2 INTERNAL_JWT_SUBJECT="dev-refactor.interop-eservice-descriptors-archiver" @@ -22,6 +22,4 @@ INTERNAL_JWT_SECONDS_DURATION=60 TENANT_PROCESS_URL="http://localhost:3500" RECORDS_PROCESS_BATCH_SIZE=5 -ANAC_TENANT_ID="" - -AWS_CONFIG_FILE=aws.config.local +ANAC_TENANT_ID="69e2865e-65ab-4e48-a638-2037a9ee2ee7" diff --git a/packages/anac-certified-attributes-importer/aws.config.local b/packages/anac-certified-attributes-importer/aws.config.local deleted file mode 100644 index 34826a60e2..0000000000 --- a/packages/anac-certified-attributes-importer/aws.config.local +++ /dev/null @@ -1,4 +0,0 @@ -[default] -aws_access_key_id=testawskey -aws_secret_access_key=testawssecret -region=eu-south-1 diff --git a/packages/anac-certified-attributes-importer/src/index.ts b/packages/anac-certified-attributes-importer/src/index.ts index 05f43051ab..46956f9c30 100644 --- a/packages/anac-certified-attributes-importer/src/index.ts +++ b/packages/anac-certified-attributes-importer/src/index.ts @@ -37,3 +37,8 @@ await importAttributes( loggerInstance, correlationId ); + +process.exit(0); +// process.exit() should not be required. +// however, something in this script hangs on exit. +// TODO figure out why and remove this workaround. diff --git a/packages/anac-certified-attributes-importer/src/model/tenant.ts b/packages/anac-certified-attributes-importer/src/model/tenant.ts new file mode 100644 index 0000000000..08af14d753 --- /dev/null +++ b/packages/anac-certified-attributes-importer/src/model/tenant.ts @@ -0,0 +1,11 @@ +import { Tenant } from "pagopa-interop-models"; +import { z } from "zod"; + +export const AnacReadModelTenant = Tenant.pick({ + id: true, + externalId: true, + attributes: true, + features: true, +}); + +export type AnacReadModelTenant = z.infer; diff --git a/packages/anac-certified-attributes-importer/src/service/processor.ts b/packages/anac-certified-attributes-importer/src/service/processor.ts index 41d9c4930a..eb82b0e825 100644 --- a/packages/anac-certified-attributes-importer/src/service/processor.ts +++ b/packages/anac-certified-attributes-importer/src/service/processor.ts @@ -1,7 +1,7 @@ /* eslint-disable max-params */ import { parse } from "csv/sync"; import { Logger, RefreshableInteropToken, zipBy } from "pagopa-interop-commons"; -import { CorrelationId, Tenant } from "pagopa-interop-models"; +import { CorrelationId } from "pagopa-interop-models"; import { AnacAttributes, AttributeIdentifiers, @@ -9,6 +9,7 @@ import { } from "../model/processorModel.js"; import { CsvRow, NonPaRow, PaRow } from "../model/csvRowModel.js"; import { InteropContext } from "../model/interopContextModel.js"; +import { AnacReadModelTenant } from "../model/tenant.js"; import { TenantProcessService } from "./tenantProcessService.js"; import { ReadModelQueries } from "./readmodelQueriesService.js"; import { SftpClient } from "./sftpService.js"; @@ -196,7 +197,9 @@ async function getAttributesIdentifiers( readModel: ReadModelQueries, anacTenantId: string ): Promise { - const anacTenant: Tenant = await readModel.getTenantById(anacTenantId); + const anacTenant: AnacReadModelTenant = await readModel.getTenantById( + anacTenantId + ); const certifier = anacTenant.features.find( (f) => f.type === "PersistentCertifier" ); @@ -253,7 +256,7 @@ const prepareTenantsProcessor = ( async function processTenants( orgs: T[], extractTenantCode: (org: T) => string, - retrieveTenants: (codes: string[]) => Promise + retrieveTenants: (codes: string[]) => Promise ): Promise { if (orgs.length === 0) { return; @@ -344,7 +347,7 @@ const prepareTenantsProcessor = ( async function assignAttribute( tenantProcess: TenantProcessService, refreshableToken: RefreshableInteropToken, - tenant: Tenant, + tenant: AnacReadModelTenant, attribute: AttributeIdentifiers, logger: Logger, correlationId: CorrelationId @@ -371,7 +374,7 @@ async function assignAttribute( async function unassignAttribute( tenantProcess: TenantProcessService, refreshableToken: RefreshableInteropToken, - tenant: Tenant, + tenant: AnacReadModelTenant, attribute: AttributeIdentifiers, logger: Logger, correlationId: CorrelationId @@ -395,7 +398,10 @@ async function unassignAttribute( } } -function tenantContainsAttribute(tenant: Tenant, attributeId: string): boolean { +function tenantContainsAttribute( + tenant: AnacReadModelTenant, + attributeId: string +): boolean { return ( tenant.attributes.find((attribute) => attribute.id === attributeId) !== undefined @@ -404,7 +410,7 @@ function tenantContainsAttribute(tenant: Tenant, attributeId: string): boolean { function getMissingTenants( expectedExternalId: string[], - tenants: Tenant[] + tenants: AnacReadModelTenant[] ): string[] { const existingSet = new Set(tenants.map((t) => t.externalId.value)); diff --git a/packages/anac-certified-attributes-importer/src/service/readmodelQueriesService.ts b/packages/anac-certified-attributes-importer/src/service/readmodelQueriesService.ts index 007811de07..0b379c1fec 100644 --- a/packages/anac-certified-attributes-importer/src/service/readmodelQueriesService.ts +++ b/packages/anac-certified-attributes-importer/src/service/readmodelQueriesService.ts @@ -1,5 +1,6 @@ import { ReadModelRepository } from "pagopa-interop-commons"; -import { Attribute, Tenant } from "pagopa-interop-models"; +import { Attribute } from "pagopa-interop-models"; +import { AnacReadModelTenant } from "../model/tenant.js"; const projectUnrevokedCertifiedAttributes = { _id: 0, @@ -26,7 +27,9 @@ export class ReadModelQueries { /** * Retrieve all PA tenants that matches the given IPA codes, with their unrevoked certified attribute */ - public async getPATenants(ipaCodes: string[]): Promise { + public async getPATenants( + ipaCodes: string[] + ): Promise { return await this.readModelClient.tenants .aggregate([ { @@ -39,14 +42,16 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => AnacReadModelTenant.parse(data)) .toArray(); } /** * Retrieve all non-PA tenants that matches the given tax codes, with their unrevoked certified attribute */ - public async getNonPATenants(taxCodes: string[]): Promise { + public async getNonPATenants( + taxCodes: string[] + ): Promise { return await this.readModelClient.tenants .aggregate([ { @@ -59,11 +64,11 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => AnacReadModelTenant.parse(data)) .toArray(); } - public async getTenantById(tenantId: string): Promise { + public async getTenantById(tenantId: string): Promise { const result = await this.readModelClient.tenants .aggregate([ { @@ -75,7 +80,7 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => AnacReadModelTenant.parse(data)) .toArray(); if (result.length === 0) { @@ -111,7 +116,7 @@ export class ReadModelQueries { public async getTenantsWithAttributes( attributeIds: string[] - ): Promise { + ): Promise { return await this.readModelClient.tenants .aggregate([ { @@ -123,7 +128,7 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => AnacReadModelTenant.parse(data)) .toArray(); } } diff --git a/packages/dtd-catalog-exporter/package.json b/packages/dtd-catalog-exporter/package.json index 4b8cbd21e8..c68f38c705 100644 --- a/packages/dtd-catalog-exporter/package.json +++ b/packages/dtd-catalog-exporter/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "tsx -r 'dotenv-flow/config' ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, diff --git a/packages/ivass-certified-attributes-importer/.env b/packages/ivass-certified-attributes-importer/.env index c50cd35134..10ddd4fccb 100644 --- a/packages/ivass-certified-attributes-importer/.env +++ b/packages/ivass-certified-attributes-importer/.env @@ -5,7 +5,7 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 - + INTERNAL_JWT_KID=ffcc9b5b-4612-49b1-9374-9d203a3834f2 INTERNAL_JWT_SUBJECT="dev-refactor.interop-eservice-descriptors-archiver" INTERNAL_JWT_ISSUER="dev-refactor.interop.pagopa.it" @@ -14,8 +14,11 @@ INTERNAL_JWT_SECONDS_DURATION=60 TENANT_PROCESS_URL="http://localhost:3500" RECORDS_PROCESS_BATCH_SIZE=5 -IVASS_TENANT_ID="" -SOURCE_URL="" -HISTORY_BUCKET_NAME="" +IVASS_TENANT_ID="69e2865e-65ab-4e48-a638-2037a9ee2ee7" +SOURCE_URL="http://127.0.0.1:8080/ivass-test-csv.zip" +HISTORY_BUCKET_NAME="interop-local-bucket" +S3_CUSTOM_SERVER=true +S3_SERVER_HOST=http://localhost +S3_SERVER_PORT=9000 AWS_CONFIG_FILE=aws.config.local diff --git a/packages/ivass-certified-attributes-importer/src/index.ts b/packages/ivass-certified-attributes-importer/src/index.ts index f33d4e6bc1..a5bd1b0d71 100644 --- a/packages/ivass-certified-attributes-importer/src/index.ts +++ b/packages/ivass-certified-attributes-importer/src/index.ts @@ -47,3 +47,8 @@ await importAttributes( loggerInstance, correlationId ); + +process.exit(0); +// process.exit() should not be required. +// however, something in this script hangs on exit. +// TODO figure out why and remove this workaround. diff --git a/packages/ivass-certified-attributes-importer/src/model/tenant.ts b/packages/ivass-certified-attributes-importer/src/model/tenant.ts new file mode 100644 index 0000000000..bd62828821 --- /dev/null +++ b/packages/ivass-certified-attributes-importer/src/model/tenant.ts @@ -0,0 +1,11 @@ +import { Tenant } from "pagopa-interop-models"; +import { z } from "zod"; + +export const IvassReadModelTenant = Tenant.pick({ + id: true, + externalId: true, + attributes: true, + features: true, +}); + +export type IvassReadModelTenant = z.infer; diff --git a/packages/ivass-certified-attributes-importer/src/service/processor.ts b/packages/ivass-certified-attributes-importer/src/service/processor.ts index bf29bddebf..7bad3510e0 100644 --- a/packages/ivass-certified-attributes-importer/src/service/processor.ts +++ b/packages/ivass-certified-attributes-importer/src/service/processor.ts @@ -1,10 +1,6 @@ /* eslint-disable max-params */ import { Logger, RefreshableInteropToken } from "pagopa-interop-commons"; -import { - CorrelationId, - Tenant, - TenantFeatureCertifier, -} from "pagopa-interop-models"; +import { CorrelationId, TenantFeatureCertifier } from "pagopa-interop-models"; import { parse } from "csv/sync"; import { AttributeIdentifiers, @@ -14,6 +10,7 @@ import { import { IVASS_INSURANCES_ATTRIBUTE_CODE } from "../config/constants.js"; import { CsvRow, RawCsvRow } from "../model/csvRowModel.js"; import { InteropContext } from "../model/interopContextModel.js"; +import { IvassReadModelTenant } from "../model/tenant.js"; import { ReadModelQueries } from "./readModelQueriesService.js"; import { TenantProcessService } from "./tenantProcessService.js"; @@ -167,7 +164,9 @@ async function getAttributesIdentifiers( readModel: ReadModelQueries, ivassTenantId: string ): Promise { - const ivassTenant: Tenant = await readModel.getTenantById(ivassTenantId); + const ivassTenant: IvassReadModelTenant = await readModel.getTenantById( + ivassTenantId + ); const certifier = ivassTenant.features.find( (f) => f.type === "PersistentCertifier" ); @@ -199,7 +198,7 @@ const isAttributeAssigned = (org: CsvRow, now: number): boolean => async function assignAttribute( tenantProcess: TenantProcessService, refreshableToken: RefreshableInteropToken, - tenant: Tenant, + tenant: IvassReadModelTenant, attribute: AttributeIdentifiers, logger: Logger, correlationId: CorrelationId @@ -226,7 +225,7 @@ async function assignAttribute( async function unassignAttribute( tenantProcess: TenantProcessService, refreshableToken: RefreshableInteropToken, - tenant: Tenant, + tenant: IvassReadModelTenant, attribute: AttributeIdentifiers, logger: Logger, correlationId: CorrelationId @@ -250,7 +249,10 @@ async function unassignAttribute( } } -function tenantContainsAttribute(tenant: Tenant, attributeId: string): boolean { +function tenantContainsAttribute( + tenant: IvassReadModelTenant, + attributeId: string +): boolean { return ( tenant.attributes.find((attribute) => attribute.id === attributeId) !== undefined diff --git a/packages/ivass-certified-attributes-importer/src/service/readModelQueriesService.ts b/packages/ivass-certified-attributes-importer/src/service/readModelQueriesService.ts index b6c853b478..61cadf6e2d 100644 --- a/packages/ivass-certified-attributes-importer/src/service/readModelQueriesService.ts +++ b/packages/ivass-certified-attributes-importer/src/service/readModelQueriesService.ts @@ -1,5 +1,6 @@ import { ReadModelRepository } from "pagopa-interop-commons"; -import { Attribute, Tenant } from "pagopa-interop-models"; +import { Attribute } from "pagopa-interop-models"; +import { IvassReadModelTenant } from "../model/tenant.js"; const projectUnrevokedCertifiedAttributes = { _id: 0, @@ -26,7 +27,9 @@ export class ReadModelQueries { /** * Retrieve tenants that match the given tax codes, with their unrevoked certified attribute */ - public async getIVASSTenants(externalId: string[]): Promise { + public async getIVASSTenants( + externalId: string[] + ): Promise { return await this.readModelClient.tenants .aggregate([ { @@ -39,13 +42,13 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => IvassReadModelTenant.parse(data)) .toArray(); } public async getTenantsWithAttributes( attributeIds: string[] - ): Promise { + ): Promise { return await this.readModelClient.tenants .aggregate([ { @@ -57,11 +60,11 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => IvassReadModelTenant.parse(data)) .toArray(); } - public async getTenantById(tenantId: string): Promise { + public async getTenantById(tenantId: string): Promise { const result = await this.readModelClient.tenants .aggregate([ { @@ -73,7 +76,7 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => IvassReadModelTenant.parse(data)) .toArray(); if (result.length === 0) { diff --git a/packages/one-trust-notices/.env b/packages/one-trust-notices/.env index 94054af2e8..258059f0f2 100644 --- a/packages/one-trust-notices/.env +++ b/packages/one-trust-notices/.env @@ -1,6 +1,4 @@ LOG_LEVEL=info - -AWS_REGION=eu-south-1 LANGS=it,de,fr,es CONTENT_STORAGE_BUCKET=one-trust-notices-content @@ -16,3 +14,5 @@ PRIVACY_NOTICES_DYNAMO_TABLE_NAME=one-trust-notices S3_CUSTOM_SERVER=true S3_SERVER_HOST=http://localhost S3_SERVER_PORT=9000 +AWS_CONFIG_FILE=aws.config.local +AWS_REGION=eu-south-1 diff --git a/packages/one-trust-notices/aws.config.local b/packages/one-trust-notices/aws.config.local index 06a4c18070..0414a88c2b 100644 --- a/packages/one-trust-notices/aws.config.local +++ b/packages/one-trust-notices/aws.config.local @@ -2,3 +2,7 @@ aws_access_key_id=testawskey aws_secret_access_key=testawskey region=eu-south-1 + +[services local] +dynamodb= + endpoint_url=http://localhost:8085 From 70331043dffae6a892cc670ba72c2977336f1901 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 7 Nov 2024 17:48:55 +0100 Subject: [PATCH 20/34] IMN-794 Add events service v1 in authorization-platformstate-writer (#1063) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../src/consumerServiceV1.ts | 427 ++- .../src/consumerServiceV2.ts | 7 +- .../src/utils.ts | 17 +- .../consumerServiceV1.integration.test.ts | 2733 +++++++++++++++++ .../test/utils.test.ts | 13 +- 5 files changed, 3176 insertions(+), 21 deletions(-) create mode 100644 packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index 78b5d36e02..bc0af7806f 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -1,20 +1,412 @@ import { match } from "ts-pattern"; -import { AuthorizationEventEnvelopeV1 } from "pagopa-interop-models"; +import { + AuthorizationEventEnvelopeV1, + Client, + ClientId, + ClientV1, + fromClientV1, + fromKeyV1, + genericInternalError, + itemState, + Key, + KeyV1, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKKid, + makePlatformStatesClientPK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + missingKafkaMessageDataError, + PlatformStatesClientEntry, + PurposeId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, + unsafeBrandId, +} from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + clientKindToTokenGenerationStatesClientKind, + convertEntriesToClientKidInTokenGenerationStates, + createTokenClientPurposeEntry, + deleteClientEntryFromPlatformStates, + deleteClientEntryFromTokenGenerationStatesTable, + deleteEntriesFromTokenStatesByClient, + deleteEntriesFromTokenStatesByKid, + deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable, + extractAgreementIdFromAgreementPK, + extractKidFromTokenEntryPK, + readClientEntriesInTokenGenerationStates, + readClientEntry, + retrievePlatformStatesByPurpose, + setClientPurposeIdsInPlatformStatesEntry, + updateTokenDataForSecondRetrieval, + upsertPlatformClientEntry, + upsertTokenClientKidEntry, + upsertTokenStateClientPurposeEntry, +} from "./utils.js"; export async function handleMessageV1( message: AuthorizationEventEnvelopeV1, - _dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient ): Promise { await match(message) + .with({ type: "ClientAdded" }, async (msg) => { + const client = parseClient(msg.data.client, msg.type); + + const pk = makePlatformStatesClientPK(client.id); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + + if (clientEntry && clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const updatedClientEntry: PlatformStatesClientEntry = { + PK: pk, + version: msg.version, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: client.purposes, + }; + await upsertPlatformClientEntry(updatedClientEntry, dynamoDBClient); + } + }) + .with({ type: "KeysAdded" }, async (msg) => { + const keyV1 = msg.data.keys[0].value; + + const clientId = unsafeBrandId(msg.data.clientId); + const key = parseKey(keyV1, msg.type); + const kid = key.kid; + const pem = key.encodedPem; + + const pk = makePlatformStatesClientPK(clientId); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + + if (!clientEntry || clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const clientPurposesIds = clientEntry?.clientPurposesIds || []; + + const platformClientEntry: PlatformStatesClientEntry = { + PK: pk, + state: itemState.active, + clientKind: clientEntry.clientKind, + clientConsumerId: clientEntry.clientConsumerId, + clientPurposesIds, + version: msg.version, + updatedAt: new Date().toISOString(), + }; + await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + + if (clientPurposesIds.length > 0) { + const addedEntries = await Promise.all( + clientPurposesIds.map(async (purposeId) => { + const { purposeEntry, agreementEntry, catalogEntry } = + await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }); + + const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + PK: tokenClientKidPurposePK, + consumerId: platformClientEntry.clientConsumerId, + clientKind: platformClientEntry.clientKind, + publicKey: pem, + updatedAt: new Date().toISOString(), + GSIPK_clientId: clientId, + GSIPK_kid: makeGSIPKKid(kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId, + purposeId, + }), + GSIPK_purposeId: purposeId, + ...((purposeEntry && { + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: platformClientEntry.clientConsumerId, + eserviceId: purposeEntry.purposeEserviceId, + }), + purposeState: purposeEntry.state, + purposeVersionId: purposeEntry.purposeVersionId, + }) || + {}), + ...((purposeEntry && + agreementEntry && { + agreementId: extractAgreementIdFromAgreementPK( + agreementEntry.PK + ), + agreementState: agreementEntry.state, + GSIPK_eserviceId_descriptorId: + makeGSIPKEServiceIdDescriptorId({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: agreementEntry.agreementDescriptorId, + }), + }) || + {}), + ...((catalogEntry && { + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: + catalogEntry.descriptorVoucherLifespan, + }) || + {}), + }; + + await upsertTokenStateClientPurposeEntry( + clientKidPurposeEntry, + dynamoDBClient + ); + return clientKidPurposeEntry; + }) + ); + + // Second check for updated fields + await Promise.all( + clientPurposesIds.map(async (purposeId, index) => { + const { + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + } = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + const addedClientKidPurposeEntry = addedEntries[index]; + await updateTokenDataForSecondRetrieval({ + dynamoDBClient, + entry: addedClientKidPurposeEntry, + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + }); + }) + ); + } else { + const clientKidEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId, + kid, + }), + consumerId: platformClientEntry.clientConsumerId, + clientKind: platformClientEntry.clientKind, + publicKey: pem, + GSIPK_clientId: clientId, + GSIPK_kid: makeGSIPKKid(kid), + updatedAt: new Date().toISOString(), + }; + await upsertTokenClientKidEntry(clientKidEntry, dynamoDBClient); + } + } + }) + .with({ type: "KeyDeleted" }, async (msg) => { + const clientId = unsafeBrandId(msg.data.clientId); + const pk = makePlatformStatesClientPK(clientId); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + + if (!clientEntry || clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const platformClientEntry: PlatformStatesClientEntry = { + PK: pk, + state: itemState.active, + clientKind: clientEntry.clientKind, + clientConsumerId: clientEntry.clientConsumerId, + clientPurposesIds: clientEntry.clientPurposesIds, + version: msg.version, + updatedAt: new Date().toISOString(), + }; + await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + + const GSIPK_kid = makeGSIPKKid(msg.data.keyId); + await deleteEntriesFromTokenStatesByKid(GSIPK_kid, dynamoDBClient); + } + }) + .with({ type: "ClientPurposeAdded" }, async (msg) => { + const clientId = unsafeBrandId(msg.data.clientId); + const unparsedPurposeId = msg.data.statesChain?.purpose?.purposeId; + + if (!unparsedPurposeId) { + throw missingKafkaMessageDataError("purposeId", msg.type); + } + const purposeId = unsafeBrandId(unparsedPurposeId); + + const pk = makePlatformStatesClientPK(clientId); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + if (!clientEntry || clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const purposeIds = [...clientEntry.clientPurposesIds, purposeId]; + await setClientPurposeIdsInPlatformStatesEntry( + { + primaryKey: pk, + version: msg.version, + clientPurposeIds: purposeIds, + }, + dynamoDBClient + ); + } + + const GSIPK_clientId = clientId; + const tokenClientEntries = await readClientEntriesInTokenGenerationStates( + GSIPK_clientId, + dynamoDBClient + ); + if (tokenClientEntries.length === 0) { + return Promise.resolve(); + } else { + const { purposeEntry, agreementEntry, catalogEntry } = + await retrievePlatformStatesByPurpose(purposeId, dynamoDBClient); + + const seenKids = new Set(); + const addedTokenClientPurposeEntries = await Promise.all( + tokenClientEntries.map(async (entry) => { + const parsedTokenClientEntry = + TokenGenerationStatesClientEntry.safeParse(entry); + const parsedTokenClientPurposeEntry = + TokenGenerationStatesClientPurposeEntry.safeParse(entry); + + if (parsedTokenClientEntry.success) { + const newTokenClientPurposeEntry = createTokenClientPurposeEntry({ + tokenEntry: parsedTokenClientEntry.data, + kid: extractKidFromTokenEntryPK(parsedTokenClientEntry.data.PK), + clientId, + consumerId: parsedTokenClientEntry.data.consumerId, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, + }); + + await upsertTokenStateClientPurposeEntry( + newTokenClientPurposeEntry, + dynamoDBClient + ); + await deleteClientEntryFromTokenGenerationStatesTable( + entry, + dynamoDBClient + ); + return newTokenClientPurposeEntry; + } + + if (parsedTokenClientPurposeEntry.success) { + const kid = extractKidFromTokenEntryPK( + parsedTokenClientPurposeEntry.data.PK + ); + if (!seenKids.has(kid)) { + const newTokenClientPurposeEntry = + createTokenClientPurposeEntry({ + tokenEntry: parsedTokenClientPurposeEntry.data, + kid, + clientId, + consumerId: parsedTokenClientPurposeEntry.data.consumerId, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, + }); + + await upsertTokenStateClientPurposeEntry( + newTokenClientPurposeEntry, + dynamoDBClient + ); + seenKids.add(kid); + return newTokenClientPurposeEntry; + } + } + + throw genericInternalError(`Unable to parse ${entry}`); + }) + ); + + // Second check for updated fields + await Promise.all( + addedTokenClientPurposeEntries.map(async (entry) => { + const { + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + } = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + await updateTokenDataForSecondRetrieval({ + dynamoDBClient, + entry, + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + }); + }) + ); + } + }) + .with({ type: "ClientPurposeRemoved" }, async (msg) => { + const clientId = unsafeBrandId(msg.data.clientId); + const purposeIdToRemove = unsafeBrandId(msg.data.purposeId); + + const pk = makePlatformStatesClientPK(clientId); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + + if (clientEntry) { + if (clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const updatedPurposeIds = clientEntry.clientPurposesIds.filter( + (purposeId) => purposeId !== purposeIdToRemove + ); + const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ + clientId, + purposeId: unsafeBrandId(msg.data.purposeId), + }); + + // platform-states + await setClientPurposeIdsInPlatformStatesEntry( + { + primaryKey: pk, + version: msg.version, + clientPurposeIds: updatedPurposeIds, + }, + dynamoDBClient + ); + + // token-generation-states + if (updatedPurposeIds.length > 0) { + await deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable( + GSIPK_clientId_purposeId, + dynamoDBClient + ); + } else { + await convertEntriesToClientKidInTokenGenerationStates( + GSIPK_clientId_purposeId, + dynamoDBClient + ); + } + } + } + }) + .with({ type: "ClientDeleted" }, async (msg) => { + const clientId = unsafeBrandId(msg.data.clientId); + const pk = makePlatformStatesClientPK(clientId); + await deleteClientEntryFromPlatformStates(pk, dynamoDBClient); + + const GSIPK_clientId = clientId; + await deleteEntriesFromTokenStatesByClient( + GSIPK_clientId, + dynamoDBClient + ); + }) .with( - { type: "ClientAdded" }, - { type: "ClientDeleted" }, - { type: "ClientPurposeAdded" }, - { type: "ClientPurposeRemoved" }, - { type: "KeyDeleted" }, { type: "KeyRelationshipToUserMigrated" }, - { type: "KeysAdded" }, { type: "RelationshipAdded" }, { type: "RelationshipAdded" }, { type: "RelationshipRemoved" }, @@ -24,3 +416,22 @@ export async function handleMessageV1( ) .exhaustive(); } + +export const parseKey = (keyV1: KeyV1 | undefined, eventType: string): Key => { + if (!keyV1) { + throw missingKafkaMessageDataError("key", eventType); + } + + return fromKeyV1(keyV1); +}; + +const parseClient = ( + clientV1: ClientV1 | undefined, + eventType: string +): Client => { + if (!clientV1) { + throw missingKafkaMessageDataError("client", eventType); + } + + return fromClientV1(clientV1); +}; diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index 4fd99439fa..b4cc629627 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -30,7 +30,6 @@ import { deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable, readClientEntry, readClientEntriesInTokenGenerationStates, - cleanClientPurposeIdsInPlatformStatesEntry, deleteClientEntryFromTokenGenerationStatesTable, extractKidFromTokenEntryPK, extractAgreementIdFromAgreementPK, @@ -38,6 +37,7 @@ import { upsertPlatformClientEntry, upsertTokenClientKidEntry, upsertTokenStateClientPurposeEntry, + setClientPurposeIdsInPlatformStatesEntry, updateTokenDataForSecondRetrieval, createTokenClientPurposeEntry, } from "./utils.js"; @@ -346,9 +346,8 @@ export async function handleMessageV2( }); // platform-states - await cleanClientPurposeIdsInPlatformStatesEntry( - pk, - msg.version, + await setClientPurposeIdsInPlatformStatesEntry( + { primaryKey: pk, version: msg.version, clientPurposeIds: [] }, dynamoDBClient ); diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index 9659d82c7c..1b81553deb 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -716,9 +716,16 @@ export const readClientEntriesInTokenGenerationStates = async ( return await runPaginatedQuery(GSIPK_clientId, dynamoDBClient, undefined); }; -export const cleanClientPurposeIdsInPlatformStatesEntry = async ( - primaryKey: PlatformStatesClientPK, - version: number, +export const setClientPurposeIdsInPlatformStatesEntry = async ( + { + primaryKey, + version, + clientPurposeIds, + }: { + primaryKey: PlatformStatesClientPK; + version: number; + clientPurposeIds: PurposeId[]; + }, dynamoDBClient: DynamoDBClient ): Promise => { const input: UpdateItemInput = { @@ -730,7 +737,9 @@ export const cleanClientPurposeIdsInPlatformStatesEntry = async ( }, ExpressionAttributeValues: { ":clientPurposesIds": { - L: [], + L: clientPurposeIds.map((purposeId) => ({ + S: purposeId, + })), }, ":newVersion": { N: version.toString(), diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts new file mode 100644 index 0000000000..6da35e8c62 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -0,0 +1,2733 @@ +import { fail } from "assert"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockClient, + getMockTokenStatesClientEntry, + getMockTokenStatesClientPurposeEntry, + readAllPlatformStateItems, + readAllTokenStateItems, + getMockPurpose, + getMockPurposeVersion, + writePlatformPurposeEntry, + getMockAgreement, + writePlatformAgreementEntry, + getMockDescriptor, + writeCatalogEntry, + getMockKey, + writeTokenStateEntry, +} from "pagopa-interop-commons-test"; +import { + AuthorizationEventEnvelope, + Client, + ClientAddedV1, + ClientComponentStateV1, + ClientDeletedV1, + ClientId, + ClientPurposeAddedV1, + ClientPurposeRemovedV1, + Descriptor, + generateId, + itemState, + KeyDeletedV1, + KeysAddedV1, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKKid, + makePlatformStatesAgreementPK, + makePlatformStatesClientPK, + makePlatformStatesEServiceDescriptorPK, + makePlatformStatesPurposePK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, + PlatformStatesClientEntry, + PlatformStatesPurposeEntry, + Purpose, + PurposeId, + purposeVersionState, + TenantId, + toClientV1, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, + toKeyV1, +} from "pagopa-interop-models"; +import { handleMessageV1 } from "../src/consumerServiceV1.js"; +import { + clientKindToTokenGenerationStatesClientKind, + readClientEntry, + writeClientEntry, + writeTokenStateClientEntry, +} from "../src/utils.js"; +import { config } from "./utils.js"; + +describe("integration tests V1 events", async () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + const mockDate = new Date(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + describe("ClientAdded", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const client = getMockClient(); + + const payload: ClientAddedV1 = { + client: toClientV1(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + }); + + it("should add the entry if it doesn't exist", async () => { + const messageVersion = 1; + + const client = getMockClient(); + + const payload: ClientAddedV1 = { + client: toClientV1(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: messageVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: client.purposes, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformClientEntry); + }); + + it("should update the entry", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const client = getMockClient(); + + const payload: ClientAddedV1 = { + client: toClientV1(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: messageVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: client.purposes, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformClientEntry); + }); + }); + + describe("KeysAdded", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const key = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [key], + }; + + const payload: KeysAddedV1 = { + clientId: client.id, + keys: [ + { + keyId: generateId(), + value: toKeyV1(key), + }, + ], + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeysAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [generateId()], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: key.kid, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(key.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should do no operation if the existing table entry doesn't exist", async () => { + const messageVersion = 2; + + const key = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [key], + }; + + const payload: KeysAddedV1 = { + clientId: client.id, + keys: [ + { + keyId: generateId(), + value: toKeyV1(key), + }, + ], + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeysAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: key.kid, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(key.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should update platform-states entry and insert token-generation-states client-kid-purpose entries if the client contains at least one purpose", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + consumerId, + keys: [oldKey, addedKey], + purposes: [purpose1.id, purpose2.id], + }; + + const payload: KeysAddedV1 = { + clientId: client.id, + keys: [ + { + keyId: generateId(), + value: toKeyV1(addedKey), + }, + ], + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeysAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id, purpose2.id], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose2.id, + }); + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry1.GSIPK_consumerId_eserviceId, + agreementId: agreement1.id, + agreementState: platformAgreementEntry1.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + descriptorState: previousDescriptorEntry1.state, + descriptorAudience: previousDescriptorEntry1.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry1.descriptorVoucherLifespan, + GSIPK_purposeId: purpose1.id, + purposeState: platformPurposeEntry1.state, + purposeVersionId: platformPurposeEntry1.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }), + updatedAt: new Date().toISOString(), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(4); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and update token-generation-states client-kid-purpose entries", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + consumerId, + keys: [oldKey, addedKey], + purposes: [purpose1.id, purpose2.id], + }; + + const payload: KeysAddedV1 = { + clientId: client.id, + keys: [ + { + keyId: generateId(), + value: toKeyV1(addedKey), + }, + ], + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeysAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id, purpose2.id], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose2.id, + }); + const tokenClientKidPurposePK3 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK4 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }); + + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const gsiPKClientIdPurposeId3 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId4 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + const tokenClientPurposeEntry3: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK3), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId3, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry4: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK4), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId4, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry3, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry4, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry1.GSIPK_consumerId_eserviceId, + agreementId: agreement1.id, + agreementState: platformAgreementEntry1.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + descriptorState: previousDescriptorEntry1.state, + descriptorAudience: previousDescriptorEntry1.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry1.descriptorVoucherLifespan, + GSIPK_purposeId: purpose1.id, + purposeState: platformPurposeEntry1.state, + purposeVersionId: platformPurposeEntry1.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }), + updatedAt: new Date().toISOString(), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(4); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and insert token-generation-states client-kid entry if the client does not contain purposes", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [oldKey, addedKey], + }; + + const payload: KeysAddedV1 = { + clientId: client.id, + keys: [ + { + keyId: generateId(), + value: toKeyV1(addedKey), + }, + ], + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeysAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: oldKey.kid, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }), + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + publicKey: addedKey.encodedPem, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry, expectedTokenClientEntry]) + ); + }); + + it("should update platform-states entry and update token-generation-states client-kid entry", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [oldKey, addedKey], + }; + + const payload: KeysAddedV1 = { + clientId: client.id, + keys: [ + { + keyId: generateId(), + value: toKeyV1(addedKey), + }, + ], + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeysAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: oldKey.kid, + }); + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }); + const tokenClientEntry1: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + }; + const tokenClientEntry2: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK2), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); + await writeTokenStateClientEntry(tokenClientEntry2, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }), + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + publicKey: addedKey.encodedPem, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry1, expectedTokenClientEntry]) + ); + }); + }); + + describe("KeyDeleted", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + const kidToRemove = "removed kid"; + + const payload: KeyDeletedV1 = { + clientId: client.id, + keyId: kidToRemove, + deactivationTimestamp: new Date().toISOString(), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeyDeleted", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: kidToRemove, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kidToRemove), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should insert platform-states entry and delete token-generation-states entries for that kid", async () => { + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + const kidToRemove = "removed kid"; + + const payload: KeyDeletedV1 = { + clientId: client.id, + keyId: kidToRemove, + deactivationTimestamp: new Date().toISOString(), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeyDeleted", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kidToRemove, + purposeId, + }); + + const tokenClientPurposeEntryWithKid: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId: client.id, + }; + const tokenClientPurposeEntryWithOtherKid: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(), + GSIPK_clientId: client.id, + }; + + await writeTokenStateEntry( + tokenClientPurposeEntryWithKid, + dynamoDBClient + ); + await writeTokenStateEntry( + tokenClientPurposeEntryWithOtherKid, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformStatesEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntryWithOtherKid, + tokenClientPurposeEntryWithKid, + ]) + ); + }); + + it("should update platform-states entry and delete token-generation-states entries for that kid", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + const kidToRemove = "removed kid"; + const otherKid = "other kid"; + + const payload: KeyDeletedV1 = { + clientId: client.id, + keyId: kidToRemove, + deactivationTimestamp: new Date().toISOString(), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeyDeleted", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purposeId], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kidToRemove, + purposeId, + }); + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: otherKid, + }); + + const tokenClientPurposeEntryWithKid: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId: client.id, + }; + + const tokenClientEntryWithOtherKid: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(otherKid), + }; + + await writeTokenStateEntry( + tokenClientPurposeEntryWithKid, + dynamoDBClient + ); + await writeTokenStateClientEntry( + tokenClientEntryWithOtherKid, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntryWithOtherKid]); + }); + }); + + describe("ClientPurposeAdded", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeAddedV1 = { + clientId: client.id, + statesChain: { + id: generateId(), + purpose: { + purposeId, + state: ClientComponentStateV1.ACTIVE, + versionId: generateId(), + }, + }, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should do no operation if the entry doesn't exist in platform-states", async () => { + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeAddedV1 = { + clientId: client.id, + statesChain: { + id: generateId(), + purpose: { + purposeId, + state: ClientComponentStateV1.ACTIVE, + versionId: generateId(), + }, + }, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should update platform-states entry", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeAddedV1 = { + clientId: client.id, + statesChain: { + id: generateId(), + purpose: { + purposeId, + state: ClientComponentStateV1.ACTIVE, + versionId: generateId(), + }, + }, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = + getMockTokenStatesClientPurposeEntry(); + const tokenClientEntry: TokenGenerationStatesClientEntry = + getMockTokenStatesClientEntry(); + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [purposeId], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientPurposeEntry, tokenClientEntry]) + ); + }); + + it("should update platform-states entry and convert client-kid entries to client-kid-purpose entries in token-generation-states table", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const client: Client = { + ...getMockClient(), + consumerId, + purposes: [purpose.id], + }; + + const payload: ClientPurposeAddedV1 = { + clientId: client.id, + statesChain: { + id: generateId(), + purpose: { + purposeId: purpose.id, + state: ClientComponentStateV1.ACTIVE, + versionId: generateId(), + }, + }, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose.versions[0].id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry, dynamoDBClient); + + const agreement = getMockAgreement(); + const platformAgreementEntry: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + await writePlatformAgreementEntry(platformAgreementEntry, dynamoDBClient); + + const descriptor: Descriptor = { + ...getMockDescriptor(), + id: agreement.descriptorId, + }; + const previousDescriptorEntry: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose.eserviceId, + descriptorId: descriptor.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const kid1 = "KID1"; + const kid2 = "KID2"; + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: kid1, + }); + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: kid2, + }); + + const tokenClientEntry1: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK1), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + }; + const tokenClientEntry2: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK2), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + }; + const tokenClientPurposeEntryWithOtherClient = + getMockTokenStatesClientPurposeEntry(); + + await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); + await writeTokenStateClientEntry(tokenClientEntry2, dynamoDBClient); + await writeTokenStateEntry( + tokenClientPurposeEntryWithOtherClient, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [purpose.id], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const newTokenClientPurposeEntryData = { + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose.id, + }), + GSIPK_purposeId: purpose.id, + purposeState: platformPurposeEntry.state, + purposeVersionId: platformPurposeEntry.purposeVersionId, + GSIPK_consumerId_eserviceId: + platformAgreementEntry.GSIPK_consumerId_eserviceId, + agreementId: agreement.id, + agreementState: platformAgreementEntry.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose.eserviceId, + descriptorId: descriptor.id, + }), + descriptorState: previousDescriptorEntry.state, + descriptorAudience: previousDescriptorEntry.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientEntry1, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose.id, + kid: kid1, + }), + }; + + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientEntry2, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose.id, + kid: kid2, + }), + }; + + expect(retrievedTokenEntries).toHaveLength(3); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntryWithOtherClient, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and add client-kid-purpose entries to token-generation-states table", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + const client: Client = { + ...getMockClient(), + consumerId, + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientPurposeAddedV1 = { + clientId: client.id, + statesChain: { + id: generateId(), + purpose: { + purposeId: purpose2.id, + state: ClientComponentStateV1.ACTIVE, + versionId: generateId(), + }, + }, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const kid1 = "KID1"; + const kid2 = "KID2"; + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid1, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid2, + purposeId: purpose1.id, + }); + + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientEntryWithOtherClient = getMockTokenStatesClientEntry(); + + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenStateClientEntry( + tokenClientEntryWithOtherClient, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [purpose1.id, purpose2.id], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const newTokenClientPurposeEntryData = { + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid1, + }), + }; + + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid2, + }), + }; + + expect(retrievedTokenEntries).toHaveLength(5); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientEntryWithOtherClient, + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and update client-kid-purpose entries to token-generation-states table", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + const client: Client = { + ...getMockClient(), + consumerId, + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientPurposeAddedV1 = { + clientId: client.id, + statesChain: { + id: generateId(), + purpose: { + purposeId: purpose2.id, + state: ClientComponentStateV1.ACTIVE, + versionId: generateId(), + }, + }, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const kid1 = "KID1"; + const kid2 = "KID2"; + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid1, + purposeId: purpose1.id, + }); + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid2, + purposeId: purpose1.id, + }); + const tokenClientKidPK3 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid1, + purposeId: purpose2.id, + }); + const tokenClientKidPK4 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid2, + purposeId: purpose2.id, + }); + + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK1), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK2), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry3: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK3), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose2.id, + }; + const tokenClientPurposeEntry4: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK4), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose2.id, + }; + const tokenClientEntryWithOtherClient = getMockTokenStatesClientEntry(); + + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry3, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry4, dynamoDBClient); + await writeTokenStateClientEntry( + tokenClientEntryWithOtherClient, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [purpose1.id, purpose2.id], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const newTokenClientPurposeEntryData = { + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid1, + }), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid2, + }), + }; + + expect(retrievedTokenEntries).toHaveLength(5); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientEntryWithOtherClient, + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + }); + + describe("ClientPurposeRemoved", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeRemovedV1 = { + purposeId, + clientId: client.id, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeRemoved", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should do no operation if the purpose platform-states entry doesn't exist and the token-generation-states entries aren't associated to the purpose id in the message", async () => { + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeRemovedV1 = { + purposeId, + clientId: client.id, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeRemoved", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should update platform-states entry and delete token-generation-states entries for that purpose", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const purposeId1 = generateId(); + const purposeId2 = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId1], + }; + + const payload: ClientPurposeRemovedV1 = { + purposeId: purposeId2, + clientId: client.id, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeRemoved", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purposeId1, purposeId2], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const mockClientKidPurpose1 = "mockClientKidPurpose1"; + const mockClientKidPurpose2 = "mockClientKidPurpose2"; + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: mockClientKidPurpose1, + purposeId: purposeId1, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: mockClientKidPurpose2, + purposeId: purposeId2, + }); + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purposeId1, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purposeId2, + }); + + const tokenClientEntry = getMockTokenStatesClientEntry(); + + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(mockClientKidPurpose1), + GSIPK_purposeId: purposeId1, + }; + + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(mockClientKidPurpose2), + GSIPK_purposeId: purposeId2, + }; + + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [purposeId1], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry, tokenClientPurposeEntry1]) + ); + }); + }); + + describe("ClientDeleted", () => { + it("should delete platform-states entry and token-generation-states entries", async () => { + const messageVersion = 2; + + const purposeId = generateId(); + const client = getMockClient(); + + const payload: ClientDeletedV1 = { + clientId: client.id, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientDeleted", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + const otherClientId = generateId(); + + // platform-states + const pk1PlatformStates = makePlatformStatesClientPK(client.id); + const clientPlatformStateEntry1: PlatformStatesClientEntry = { + PK: pk1PlatformStates, + version: 1, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + + const pk2PlatformStates = makePlatformStatesClientPK(otherClientId); + const clientPlatformStateEntry2: PlatformStatesClientEntry = { + PK: pk2PlatformStates, + version: 1, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(clientPlatformStateEntry1, dynamoDBClient); + await writeClientEntry(clientPlatformStateEntry2, dynamoDBClient); + + // token-generation-states + const pkTokenStates1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: "kid", + purposeId, + }); + + const pkTokenStates2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: otherClientId, + kid: "kid", + purposeId, + }); + + const clientPurposeTokenStateEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(pkTokenStates1), + GSIPK_clientId: client.id, + }; + + const otherClientPurposeTokenStateEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(pkTokenStates2), + GSIPK_clientId: otherClientId, + }; + + await writeTokenStateEntry(clientPurposeTokenStateEntry, dynamoDBClient); + await writeTokenStateEntry( + otherClientPurposeTokenStateEntry, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntries = await readAllPlatformStateItems( + dynamoDBClient + ); + expect(retrievedPlatformStatesEntries).toEqual([ + clientPlatformStateEntry2, + ]); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([ + otherClientPurposeTokenStateEntry, + ]); + }); + }); +}); diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index 7d3e7b2b48..477a2bf32c 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -70,7 +70,7 @@ import { import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { z } from "zod"; import { - cleanClientPurposeIdsInPlatformStatesEntry, + setClientPurposeIdsInPlatformStatesEntry, convertEntriesToClientKidInTokenGenerationStates, deleteClientEntryFromPlatformStates, deleteClientEntryFromTokenGenerationStatesTable, @@ -470,7 +470,7 @@ describe("utils", () => { ); }); - it("cleanClientPurposeIdsInPlatformStatesEntry", async () => { + it("setClientPurposeIdsInPlatformStatesEntry", async () => { const clientEntry1: PlatformStatesClientEntry = { ...getMockPlatformStatesClientEntry(), clientPurposesIds: [generateId(), generateId()], @@ -483,9 +483,12 @@ describe("utils", () => { await writeClientEntry(clientEntry1, dynamoDBClient); await writeClientEntry(clientEntry2, dynamoDBClient); - await cleanClientPurposeIdsInPlatformStatesEntry( - clientEntry1.PK, - clientEntry1.version + 1, + await setClientPurposeIdsInPlatformStatesEntry( + { + primaryKey: clientEntry1.PK, + version: clientEntry1.version + 1, + clientPurposeIds: [], + }, dynamoDBClient ); From 4a4775ea3d1c9ab32ebe5af9bb00850491aa3e55 Mon Sep 17 00:00:00 2001 From: Stefano Perazzolo <13318704+beetlecrunch@users.noreply.github.com> Date: Mon, 11 Nov 2024 10:08:59 +0100 Subject: [PATCH 21/34] selfcare-onboarding-consumer: add new institution type (#1186) --- .../src/services/selfcareOnboardingProcessor.ts | 1 + .../test/selfcareOnboardingProcessor.test.ts | 4 ++-- packages/selfcare-onboarding-consumer/test/utils.ts | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/selfcare-onboarding-consumer/src/services/selfcareOnboardingProcessor.ts b/packages/selfcare-onboarding-consumer/src/services/selfcareOnboardingProcessor.ts index d236cbdd42..3a6e064af5 100644 --- a/packages/selfcare-onboarding-consumer/src/services/selfcareOnboardingProcessor.ts +++ b/packages/selfcare-onboarding-consumer/src/services/selfcareOnboardingProcessor.ts @@ -64,6 +64,7 @@ export function selfcareOnboardingProcessorBuilder( const origin = match(institution.institutionType) .with("SCP", () => `${institution.origin}-SCP`) .with("PRV", () => `${institution.origin}-PRV`) + .with("PT", () => `${institution.origin}-PT`) .otherwise(() => institution.origin); if (!allowedOrigins.includes(origin)) { diff --git a/packages/selfcare-onboarding-consumer/test/selfcareOnboardingProcessor.test.ts b/packages/selfcare-onboarding-consumer/test/selfcareOnboardingProcessor.test.ts index a815ef2c6a..ff21adafcb 100644 --- a/packages/selfcare-onboarding-consumer/test/selfcareOnboardingProcessor.test.ts +++ b/packages/selfcare-onboarding-consumer/test/selfcareOnboardingProcessor.test.ts @@ -264,10 +264,10 @@ describe("Message processor", () => { }) ); }); - it.each(["SCP", "PRV"])( + it.each(["SCP", "PRV", "PT"])( "should upsert tenant with institutionType %s correctly", async (institutionType) => { - const origin = "PDND_INFOCAMERE"; + const origin = "INFOCAMERE"; const message: EachMessagePayload = { ...kafkaMessagePayload, diff --git a/packages/selfcare-onboarding-consumer/test/utils.ts b/packages/selfcare-onboarding-consumer/test/utils.ts index ef4a19f5a5..2e4b766fe2 100644 --- a/packages/selfcare-onboarding-consumer/test/utils.ts +++ b/packages/selfcare-onboarding-consumer/test/utils.ts @@ -8,8 +8,9 @@ export const allowedOrigins = [ "IPA", "ANAC", "IVASS", - "PDND_INFOCAMERE-PRV", - "PDND_INFOCAMERE-SCP", + "INFOCAMERE-PRV", + "INFOCAMERE-SCP", + "INFOCAMERE-PT", ]; export const selfcareUpsertTenantMock = (): Promise => From b7877b818f46b97e9f0538a13f40785e3ee3552c Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Mon, 11 Nov 2024 11:28:24 +0100 Subject: [PATCH 22/34] IMN-518 pt1 - Case insensitive duplicate checks for catalog (#1137) --- .../src/services/catalogService.ts | 8 ++++++-- .../test/cloneDescriptor.test.ts | 17 ++++++++++++----- .../catalog-process/test/createEService.test.ts | 11 +++++++---- .../test/createRiskAnalysis.test.ts | 13 +++++++++---- .../catalog-process/test/updateDocument.test.ts | 8 ++++---- .../catalog-process/test/updateEservice.test.ts | 6 +++--- .../test/updateRiskAnalysis.test.ts | 14 ++++++++++---- .../catalog-process/test/uploadDocument.test.ts | 8 ++++---- 8 files changed, 55 insertions(+), 30 deletions(-) diff --git a/packages/catalog-process/src/services/catalogService.ts b/packages/catalog-process/src/services/catalogService.ts index 648122835c..e5ecf618e1 100644 --- a/packages/catalog-process/src/services/catalogService.ts +++ b/packages/catalog-process/src/services/catalogService.ts @@ -653,7 +653,10 @@ export function catalogServiceBuilder( if ( document.kind === "DOCUMENT" && - descriptor.docs.some((d) => d.prettyName === document.prettyName) + descriptor.docs.some( + (d) => + d.prettyName.toLowerCase() === document.prettyName.toLowerCase() + ) ) { throw prettyNameDuplicate(document.prettyName, descriptor.id); } @@ -811,7 +814,8 @@ export function catalogServiceBuilder( descriptor.docs.some( (d) => d.id !== documentId && - d.prettyName === apiEServiceDescriptorDocumentUpdateSeed.prettyName + d.prettyName.toLowerCase() === + apiEServiceDescriptorDocumentUpdateSeed.prettyName.toLowerCase() ) ) { throw prettyNameDuplicate( diff --git a/packages/catalog-process/test/cloneDescriptor.test.ts b/packages/catalog-process/test/cloneDescriptor.test.ts index 41dab1bda6..5031a026b6 100644 --- a/packages/catalog-process/test/cloneDescriptor.test.ts +++ b/packages/catalog-process/test/cloneDescriptor.test.ts @@ -242,7 +242,7 @@ describe("clone descriptor", () => { }) ).rejects.toThrowError(FileManagerError); }); - it("should throw eServiceDuplicate if an eservice with the same name already exists", async () => { + it("should throw eServiceDuplicate if an eservice with the same name already exists, case insensitive", async () => { const descriptor: Descriptor = { ...mockDescriptor, state: descriptorState.draft, @@ -251,15 +251,16 @@ describe("clone descriptor", () => { }; const eservice1: EService = { ...mockEService, + name: mockEService.name.toUpperCase(), id: generateId(), descriptors: [descriptor], }; await addOneEService(eservice1); const cloneTimestamp = new Date(); - const conflictEServiceName = `${ - eservice1.name - } - clone - ${formatDateddMMyyyyHHmmss(cloneTimestamp)}`; + const conflictEServiceName = `${eservice1.name.toLowerCase()} - clone - ${formatDateddMMyyyyHHmmss( + cloneTimestamp + )}`; const eservice2: EService = { ...mockEService, @@ -276,7 +277,13 @@ describe("clone descriptor", () => { serviceName: "", logger: genericLogger, }) - ).rejects.toThrowError(eServiceDuplicate(conflictEServiceName)); + ).rejects.toThrowError( + eServiceDuplicate( + `${eservice1.name} - clone - ${formatDateddMMyyyyHHmmss( + cloneTimestamp + )}` + ) + ); }); it("should throw eServiceNotFound if the eservice doesn't exist", () => { expect( diff --git a/packages/catalog-process/test/createEService.test.ts b/packages/catalog-process/test/createEService.test.ts index b7f7a30764..8b16fcfe35 100644 --- a/packages/catalog-process/test/createEService.test.ts +++ b/packages/catalog-process/test/createEService.test.ts @@ -120,12 +120,15 @@ describe("create eservice", () => { ); }); - it("should throw eServiceDuplicate if an eservice with the same name already exists", async () => { - await addOneEService(mockEService); + it("should throw eServiceDuplicate if an eservice with the same name already exists, case insensitive", async () => { + await addOneEService({ + ...mockEService, + name: mockEService.name.toUpperCase(), + }); expect( catalogService.createEService( { - name: mockEService.name, + name: mockEService.name.toLowerCase(), description: mockEService.description, technology: "REST", mode: "DELIVER", @@ -138,7 +141,7 @@ describe("create eservice", () => { logger: genericLogger, } ) - ).rejects.toThrowError(eServiceDuplicate(mockEService.name)); + ).rejects.toThrowError(eServiceDuplicate(mockEService.name.toLowerCase())); }); it("should throw originNotCompliant if the requester externalId origin is not allowed", async () => { diff --git a/packages/catalog-process/test/createRiskAnalysis.test.ts b/packages/catalog-process/test/createRiskAnalysis.test.ts index d4c68c1c85..595fde0328 100644 --- a/packages/catalog-process/test/createRiskAnalysis.test.ts +++ b/packages/catalog-process/test/createRiskAnalysis.test.ts @@ -281,7 +281,7 @@ describe("create risk analysis", () => { ) ).rejects.toThrowError(tenantKindNotFound(producer.id)); }); - it("should throw riskAnalysisDuplicated if risk analysis name is duplicated", async () => { + it("should throw riskAnalysisDuplicated if risk analysis name is duplicated, case insensitive", async () => { const producerTenantKind: TenantKind = randomArrayItem( Object.values(tenantKind) ); @@ -304,13 +304,18 @@ describe("create risk analysis", () => { state: descriptorState.draft, }, ], - riskAnalysis: [riskAnalysis], + riskAnalysis: [ + { + ...riskAnalysis, + name: riskAnalysis.name.toUpperCase(), + }, + ], }; await addOneEService(eservice); const riskAnalysisSeed: catalogApi.EServiceRiskAnalysisSeed = { ...buildRiskAnalysisSeed(riskAnalysis), - name: riskAnalysis.name, + name: riskAnalysis.name.toLowerCase(), }; expect( @@ -321,7 +326,7 @@ describe("create risk analysis", () => { logger: genericLogger, }) ).rejects.toThrowError( - riskAnalysisDuplicated(riskAnalysis.name, eservice.id) + riskAnalysisDuplicated(riskAnalysis.name.toLowerCase(), eservice.id) ); }); it("should throw riskAnalysisValidationFailed if the risk analysis is not valid", async () => { diff --git a/packages/catalog-process/test/updateDocument.test.ts b/packages/catalog-process/test/updateDocument.test.ts index 5da97514b6..9070d1c5ed 100644 --- a/packages/catalog-process/test/updateDocument.test.ts +++ b/packages/catalog-process/test/updateDocument.test.ts @@ -229,10 +229,10 @@ describe("update Document", () => { eServiceDocumentNotFound(eservice.id, descriptor.id, mockDocument.id) ); }); - it("should throw prettyNameDuplicate if a document with the same prettyName already exists in that descriptor", async () => { + it("should throw prettyNameDuplicate if a document with the same prettyName already exists in that descriptor, case insensitive", async () => { const document1: Document = { ...getMockDocument(), - prettyName: "test a", + prettyName: "TEST A", }; const document2: Document = { ...getMockDocument(), @@ -254,7 +254,7 @@ describe("update Document", () => { eservice.id, descriptor.id, document2.id, - { prettyName: document1.prettyName }, + { prettyName: document1.prettyName.toLowerCase() }, { authData: getMockAuthData(eservice.producerId), correlationId: generateId(), @@ -263,7 +263,7 @@ describe("update Document", () => { } ) ).rejects.toThrowError( - prettyNameDuplicate(document1.prettyName, descriptor.id) + prettyNameDuplicate(document1.prettyName.toLowerCase(), descriptor.id) ); }); }); diff --git a/packages/catalog-process/test/updateEservice.test.ts b/packages/catalog-process/test/updateEservice.test.ts index afe926ff53..bf454c4542 100644 --- a/packages/catalog-process/test/updateEservice.test.ts +++ b/packages/catalog-process/test/updateEservice.test.ts @@ -345,7 +345,7 @@ describe("update eService", () => { ).rejects.toThrowError(operationForbidden); }); - it("should throw eServiceDuplicate if the updated name is already in use", async () => { + it("should throw eServiceDuplicate if the updated name is already in use, case insensitive", async () => { const eservice1: EService = { ...mockEService, id: generateId(), @@ -364,7 +364,7 @@ describe("update eService", () => { catalogService.updateEService( eservice1.id, { - name: "eservice name already in use", + name: "ESERVICE NAME ALREADY IN USE", description: "eservice description", technology: "REST", mode: "DELIVER", @@ -376,7 +376,7 @@ describe("update eService", () => { logger: genericLogger, } ) - ).rejects.toThrowError(eServiceDuplicate("eservice name already in use")); + ).rejects.toThrowError(eServiceDuplicate("ESERVICE NAME ALREADY IN USE")); }); it("should throw eserviceNotInDraftState if the eservice descriptor is in published state", async () => { diff --git a/packages/catalog-process/test/updateRiskAnalysis.test.ts b/packages/catalog-process/test/updateRiskAnalysis.test.ts index df5d55ee22..fa00e2386b 100644 --- a/packages/catalog-process/test/updateRiskAnalysis.test.ts +++ b/packages/catalog-process/test/updateRiskAnalysis.test.ts @@ -359,7 +359,7 @@ describe("update risk analysis", () => { eServiceRiskAnalysisNotFound(eservice.id, riskAnalysisId) ); }); - it("should throw riskAnalysisDuplicated if risk analysis name is duplicated", async () => { + it("should throw riskAnalysisDuplicated if risk analysis name is duplicated, case insensitive", async () => { const producerTenantKind: TenantKind = randomArrayItem( Object.values(tenantKind) ); @@ -381,7 +381,13 @@ describe("update risk analysis", () => { state: descriptorState.draft, }, ], - riskAnalysis: [riskAnalysis_1, riskAnalysis_2], + riskAnalysis: [ + riskAnalysis_1, + { + ...riskAnalysis_2, + name: riskAnalysis_2.name.toUpperCase(), + }, + ], }; await addOneTenant(producer); @@ -389,7 +395,7 @@ describe("update risk analysis", () => { const riskAnalysisSeed: catalogApi.EServiceRiskAnalysisSeed = { ...buildRiskAnalysisSeed(riskAnalysis_1), - name: riskAnalysis_2.name, + name: riskAnalysis_2.name.toLowerCase(), }; expect( catalogService.updateRiskAnalysis( @@ -404,7 +410,7 @@ describe("update risk analysis", () => { } ) ).rejects.toThrowError( - riskAnalysisDuplicated(riskAnalysis_2.name, eservice.id) + riskAnalysisDuplicated(riskAnalysis_2.name.toLowerCase(), eservice.id) ); }); it("should throw riskAnalysisValidationFailed if the risk analysis is not valid", async () => { diff --git a/packages/catalog-process/test/uploadDocument.test.ts b/packages/catalog-process/test/uploadDocument.test.ts index 1ece2a4fa3..34c6ae6cd9 100644 --- a/packages/catalog-process/test/uploadDocument.test.ts +++ b/packages/catalog-process/test/uploadDocument.test.ts @@ -220,10 +220,10 @@ describe("upload Document", () => { ) ).rejects.toThrowError(interfaceAlreadyExists(descriptor.id)); }); - it("should throw prettyNameDuplicate if a document with the same prettyName already exists in that descriptor", async () => { + it("should throw prettyNameDuplicate if a document with the same prettyName already exists in that descriptor, case insensitive", async () => { const document: Document = { ...getMockDocument(), - prettyName: "test", + prettyName: "TEST", }; const descriptor: Descriptor = { ...mockDescriptor, @@ -242,7 +242,7 @@ describe("upload Document", () => { descriptor.id, { ...buildDocumentSeed(), - prettyName: document.prettyName, + prettyName: document.prettyName.toLowerCase(), }, { authData: getMockAuthData(eservice.producerId), @@ -252,7 +252,7 @@ describe("upload Document", () => { } ) ).rejects.toThrowError( - prettyNameDuplicate(document.prettyName, descriptor.id) + prettyNameDuplicate(document.prettyName.toLowerCase(), descriptor.id) ); }); }); From 955993ee3aabcd57d68dbe63494e3d682f8477aa Mon Sep 17 00:00:00 2001 From: Simone Camito <32327779+MalpenZibo@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:27:10 +0100 Subject: [PATCH 23/34] Turbo fixes (#1146) --- docker/docker-compose.yml | 3 +- package.json | 2 +- .../catalog/catalogItemEventNotification.ts | 2 +- .../catalogItemEventNotificationMappers.ts | 2 +- .../src/{gen => protobuf-models}/v1/events.ts | 0 pnpm-lock.yaml | 58 ++++++++-------- turbo.json | 69 +++++-------------- 7 files changed, 49 insertions(+), 87 deletions(-) rename packages/notifier-seeder/src/{gen => protobuf-models}/v1/events.ts (100%) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 7f338ba38e..1d08bc9c6e 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -3,7 +3,6 @@ version: "3" name: pagopa-interop services: - # Event Store Postgres DB event-store: volumes: @@ -79,7 +78,7 @@ services: - dynamodb-local restart: always ports: - - "8001:8001" + - "8002:8002" environment: - DYNAMO_ENDPOINT=http://dynamodb-local:8000 - AWS_REGION=eu-south-1 diff --git a/package.json b/package.json index 785be17b2b..e9fe706e5a 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "devDependencies": { "@tsconfig/strictest": "2.0.5", "@tsconfig/node-lts": "20.1.3", - "turbo": "2.0.4" + "turbo": "2.2.3" }, "config": { "protocVersion": "26.1" diff --git a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotification.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotification.ts index b5aaf4717f..2f2d626f4f 100644 --- a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotification.ts +++ b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotification.ts @@ -4,7 +4,7 @@ import { CatalogDocumentV1, CatalogItemRiskAnalysisV1, CatalogItemV1, -} from "../../gen/v1/events.js"; +} from "../../protobuf-models/v1/events.js"; /* The notification catalog event types was implemented to allow compatibility with old notifier consumers, diff --git a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMappers.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMappers.ts index 606bbab076..0e5ab3a584 100644 --- a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMappers.ts +++ b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMappers.ts @@ -13,7 +13,7 @@ import { eserviceMode, technology, } from "pagopa-interop-models"; -import { CatalogAttributeValueV1 } from "../../gen/v1/events.js"; +import { CatalogAttributeValueV1 } from "../../protobuf-models/v1/events.js"; import { CatalogDescriptorV1Notification, CatalogDocumentV1Notification, diff --git a/packages/notifier-seeder/src/gen/v1/events.ts b/packages/notifier-seeder/src/protobuf-models/v1/events.ts similarity index 100% rename from packages/notifier-seeder/src/gen/v1/events.ts rename to packages/notifier-seeder/src/protobuf-models/v1/events.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index daedbfc6b6..3fb2443e3b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: 2.0.5 version: 2.0.5 turbo: - specifier: 2.0.4 - version: 2.0.4 + specifier: 2.2.3 + version: 2.2.3 packages/agreement-email-sender: dependencies: @@ -7292,38 +7292,38 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - turbo-darwin-64@2.0.4: - resolution: {integrity: sha512-x9mvmh4wudBstML8Z8IOmokLWglIhSfhQwnh2gBCSqabgVBKYvzl8Y+i+UCNPxheCGTgtsPepTcIaKBIyFIcvw==} + turbo-darwin-64@2.2.3: + resolution: {integrity: sha512-Rcm10CuMKQGcdIBS3R/9PMeuYnv6beYIHqfZFeKWVYEWH69sauj4INs83zKMTUiZJ3/hWGZ4jet9AOwhsssLyg==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@2.0.4: - resolution: {integrity: sha512-/B1Ih8zPRGVw5vw4SlclOf3C/woJ/2T6ieH6u54KT4wypoaVyaiyMqBcziIXycdObIYr7jQ+raHO7q3mhay9/A==} + turbo-darwin-arm64@2.2.3: + resolution: {integrity: sha512-+EIMHkuLFqUdJYsA3roj66t9+9IciCajgj+DVek+QezEdOJKcRxlvDOS2BUaeN8kEzVSsNiAGnoysFWYw4K0HA==} cpu: [arm64] os: [darwin] - turbo-linux-64@2.0.4: - resolution: {integrity: sha512-6aG670e5zOWu6RczEYcB81nEl8EhiGJEvWhUrnAfNEUIMBEH1pR5SsMmG2ol5/m3PgiRM12r13dSqTxCLcHrVg==} + turbo-linux-64@2.2.3: + resolution: {integrity: sha512-UBhJCYnqtaeOBQLmLo8BAisWbc9v9daL9G8upLR+XGj6vuN/Nz6qUAhverN4Pyej1g4Nt1BhROnj6GLOPYyqxQ==} cpu: [x64] os: [linux] - turbo-linux-arm64@2.0.4: - resolution: {integrity: sha512-AXfVOjst+mCtPDFT4tCu08Qrfv12Nj7NDd33AjGwV79NYN1Y1rcFY59UQ4nO3ij3rbcvV71Xc+TZJ4csEvRCSg==} + turbo-linux-arm64@2.2.3: + resolution: {integrity: sha512-hJYT9dN06XCQ3jBka/EWvvAETnHRs3xuO/rb5bESmDfG+d9yQjeTMlhRXKrr4eyIMt6cLDt1LBfyi+6CQ+VAwQ==} cpu: [arm64] os: [linux] - turbo-windows-64@2.0.4: - resolution: {integrity: sha512-QOnUR9hKl0T5gq5h1fAhVEqBSjpcBi/BbaO71YGQNgsr6pAnCQdbG8/r3MYXet53efM0KTdOhieWeO3KLNKybA==} + turbo-windows-64@2.2.3: + resolution: {integrity: sha512-NPrjacrZypMBF31b4HE4ROg4P3nhMBPHKS5WTpMwf7wydZ8uvdEHpESVNMOtqhlp857zbnKYgP+yJF30H3N2dQ==} cpu: [x64] os: [win32] - turbo-windows-arm64@2.0.4: - resolution: {integrity: sha512-3v8WpdZy1AxZw0gha0q3caZmm+0gveBQ40OspD6mxDBIS+oBtO5CkxhIXkFJJW+jDKmDlM7wXDIGfMEq+QyNCQ==} + turbo-windows-arm64@2.2.3: + resolution: {integrity: sha512-fnNrYBCqn6zgKPKLHu4sOkihBI/+0oYFr075duRxqUZ+1aLWTAGfHZLgjVeLh3zR37CVzuerGIPWAEkNhkWEIw==} cpu: [arm64] os: [win32] - turbo@2.0.4: - resolution: {integrity: sha512-Ilme/2Q5kYw0AeRr+aw3s02+WrEYaY7U8vPnqSZU/jaDG/qd6jHVN6nRWyd/9KXvJGYM69vE6JImoGoyNjLwaw==} + turbo@2.2.3: + resolution: {integrity: sha512-5lDvSqIxCYJ/BAd6rQGK/AzFRhBkbu4JHVMLmGh/hCb7U3CqSnr5Tjwfy9vc+/5wG2DJ6wttgAaA7MoCgvBKZQ==} hasBin: true tweetnacl@0.14.5: @@ -14526,32 +14526,32 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - turbo-darwin-64@2.0.4: + turbo-darwin-64@2.2.3: optional: true - turbo-darwin-arm64@2.0.4: + turbo-darwin-arm64@2.2.3: optional: true - turbo-linux-64@2.0.4: + turbo-linux-64@2.2.3: optional: true - turbo-linux-arm64@2.0.4: + turbo-linux-arm64@2.2.3: optional: true - turbo-windows-64@2.0.4: + turbo-windows-64@2.2.3: optional: true - turbo-windows-arm64@2.0.4: + turbo-windows-arm64@2.2.3: optional: true - turbo@2.0.4: + turbo@2.2.3: optionalDependencies: - turbo-darwin-64: 2.0.4 - turbo-darwin-arm64: 2.0.4 - turbo-linux-64: 2.0.4 - turbo-linux-arm64: 2.0.4 - turbo-windows-64: 2.0.4 - turbo-windows-arm64: 2.0.4 + turbo-darwin-64: 2.2.3 + turbo-darwin-arm64: 2.2.3 + turbo-linux-64: 2.2.3 + turbo-linux-arm64: 2.2.3 + turbo-windows-64: 2.2.3 + turbo-windows-arm64: 2.2.3 tweetnacl@0.14.5: {} diff --git a/turbo.json b/turbo.json index da5a0c7bff..a3a38b7133 100644 --- a/turbo.json +++ b/turbo.json @@ -1,20 +1,14 @@ { "$schema": "https://turbo.build/schema.json", "ui": "stream", - "globalDependencies": [ - "tsconfig.json" - ], + "daemon": false, + "globalDependencies": ["tsconfig.json"], "globalEnv": ["CI"], "tasks": { "start": { "persistent": true, "cache": false, - "dependsOn": [ - "^build", - "generate-model", - "generate-protobuf", - "//#infra:start" - ] + "dependsOn": ["^build", "//#infra:start"] }, "//#infra:start": { "cache": false @@ -26,62 +20,31 @@ "cache": false }, "build": { - "dependsOn": [ - "generate-model", - "generate-protobuf", - "^build" - ], - "outputs": [ - "dist" - ] + "dependsOn": ["generate-model", "generate-protobuf", "^build"], + "outputs": ["dist"] }, "check": { - "dependsOn": [ - "generate-model", - "generate-protobuf", - "^build" - ], - "outputs": [ - "dist" - ] + "dependsOn": ["generate-model", "generate-protobuf", "^build", "^check"], + "outputs": ["dist"] }, "generate-model": { - "dependsOn": [ - "^generate-model" - ] + "dependsOn": ["^generate-model"], + "outputs": ["src/generated"] }, "generate-protobuf": { - "dependsOn": [ - "^generate-protobuf" - ], - "outputs": [ - "src/gen" - ] + "dependsOn": ["^generate-protobuf"], + "outputs": ["src/gen"] }, "test": { - "dependsOn": [ - "build" - ] + "dependsOn": ["build"] }, "lint": { - "dependsOn": [ - "generate-model", - "generate-protobuf", - "^build" - ], - "outputs": [ - "dist" - ] + "dependsOn": ["build", "^lint"], + "outputs": ["dist"] }, "lint:autofix": { - "dependsOn": [ - "generate-model", - "generate-protobuf", - "^build" - ], - "outputs": [ - "dist" - ] + "dependsOn": ["build"], + "outputs": ["dist"] }, "format:check": {}, "format:write": {} From 3542cdf50406535581040a0fee93003856883606 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Tue, 12 Nov 2024 10:22:32 +0100 Subject: [PATCH 24/34] Feature: Delegation process (#1173) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vittorio Caprio Co-authored-by: Sandro Tajè Co-authored-by: AsterITA Co-authored-by: Eric Camellini --- .../bff/delegation/Approve Delegation.bru | 20 + .../bff/delegation/Create Delegation.bru | 23 + .../bff/delegation/Get Delegation ById.bru | 20 + .../bff/delegation/Get Delegations.bru | 26 + .../bff/delegation/Reject Delegation.bru | 26 + .../bff/delegation/Revoke Delegation.bru | 20 + .../health/Health status endpoint.bru | 11 + .../producer/Approves a delegation.bru | 20 + .../producer/Delegation Creation.bru | 23 + .../producer/List producer delegations.bru | 26 + .../producer/Rejects a delegation.bru | 26 + .../producer/Retrieves a delegation.bru | 20 + .../producer/Revoke a delegation.bru | 16 + collections/environments/PagoPA local.bru | 1 + docker/event-store-init.sql | 19 + package.json | 2 + .../agreement-outbound-writer/package.json | 2 +- .../src/service/processor.ts | 4 +- packages/api-clients/open-api/bffApi.yml | 1133 +++++++++++++++-- .../api-clients/open-api/delegationApi.yml | 567 +++++++++ packages/api-clients/open-api/tenantApi.yml | 47 +- packages/api-clients/src/index.ts | 1 + packages/api-clients/template-bff.hbs | 2 + packages/api-clients/template.hbs | 2 + .../src/services/attributeRegistryService.ts | 6 +- ...tributeRegistryService.integration.test.ts | 10 +- packages/backend-for-frontend/.env | 1 + .../src/api/delegationApiConverter.ts | 108 ++ packages/backend-for-frontend/src/app.ts | 6 +- .../src/clients/clientsProvider.ts | 15 + .../backend-for-frontend/src/config/config.ts | 12 + .../backend-for-frontend/src/model/errors.ts | 23 +- .../src/routers/catalogRouter.ts | 2 + .../src/routers/delegationRouter.ts | 104 ++ .../src/routers/producerDelegationRouter.ts | 128 ++ .../src/routers/tenantRouter.ts | 20 +- .../src/services/catalogService.ts | 13 +- .../src/services/delegationService.ts | 287 +++++ .../src/services/tenantService.ts | 10 + .../src/services/validators.ts | 42 +- .../src/utilities/errorMappers.ts | 8 + packages/catalog-outbound-writer/package.json | 2 +- .../commons-test/src/eventStoreTestUtils.ts | 33 +- .../src/setupTestContainersVitest.ts | 4 + packages/commons-test/src/testUtils.ts | 85 ++ .../commons/src/config/kafkaTopicConfig.ts | 10 + .../commons/src/pdf-generator/pdfGenerator.ts | 4 +- .../src/repositories/ReadModelRepository.ts | 10 +- .../compute-agreements-consumer/src/index.ts | 3 +- packages/delegation-process/.env | 30 + packages/delegation-process/Dockerfile | 45 + packages/delegation-process/aws.config.local | 4 + packages/delegation-process/package.json | 52 + packages/delegation-process/src/app.ts | 31 + .../delegation-process/src/config/config.ts | 27 + packages/delegation-process/src/index.ts | 7 + .../src/model/domain/apiConverter.ts | 145 +++ .../src/model/domain/errors.ts | 144 +++ .../src/model/domain/models.ts | 14 + .../src/model/domain/toEvent.ts | 81 ++ .../resources/assets/font/Montserrat-Bold.ttf | Bin 0 -> 198612 bytes .../assets/font/Montserrat-Italic.ttf | Bin 0 -> 202912 bytes .../assets/font/Montserrat-Medium.ttf | Bin 0 -> 198616 bytes .../assets/font/Montserrat-Regular.ttf | Bin 0 -> 198552 bytes .../assets/font/Montserrat-SemiBold.ttf | Bin 0 -> 198720 bytes .../resources/assets/media/logo_pagopa.png | Bin 0 -> 4514 bytes .../assets/media/pictogram_pagopa.svg | 4 + .../templates/delegationApprovedTemplate.html | 87 ++ .../templates/delegationRevokedTemplate.html | 80 ++ .../templates/delegationTemplate.css | 130 ++ .../src/routers/DelegationProducerRouter.ts | 179 +++ .../src/routers/DelegationRouter.ts | 137 ++ .../src/routers/HealthRouter.ts | 8 + .../src/services/delegationContractBuilder.ts | 185 +++ .../src/services/delegationProducerService.ts | 280 ++++ .../src/services/delegationService.ts | 63 + .../src/services/readModelService.ts | 251 ++++ .../src/services/validators.ts | 144 +++ .../src/utilites/errorMappers.ts | 62 + .../delegation-process/test/.eslintrc.json | 7 + .../test/approveProducerDelegation.test.ts | 247 ++++ .../test/createProducerDelegation.test.ts | 564 ++++++++ .../test/getDelegationById.test.ts | 33 + .../test/getDelegations.test.ts | 68 + .../test/rejectProducerDelegation.test.ts | 127 ++ .../test/revokeProducerDelegation.test.ts | 371 ++++++ .../delegation-process/test/tsconfig.json | 4 + packages/delegation-process/test/utils.ts | 137 ++ .../test/vitestGlobalSetup.ts | 3 + .../delegation-process/tsconfig.check.json | 7 + packages/delegation-process/tsconfig.json | 7 + packages/delegation-process/vitest.config.ts | 11 + packages/delegation-readmodel-writer/.env | 13 + .../delegation-readmodel-writer/Dockerfile | 45 + .../delegation-readmodel-writer/package.json | 42 + .../src/config/config.ts | 16 + .../src/delegationConsumerServiceV2.ts | 38 + .../delegation-readmodel-writer/src/index.ts | 45 + .../test/consumerServiceV2.test.ts | 121 ++ .../test/tsconfig.json | 4 + .../delegation-readmodel-writer/test/utils.ts | 10 + .../test/vitestGlobalSetup.ts | 3 + .../tsconfig.check.json | 7 + .../delegation-readmodel-writer/tsconfig.json | 9 + .../vitest.config.ts | 11 + .../proto/v2/delegation/delegation.proto | 54 + .../models/proto/v2/delegation/events.proto | 21 + packages/models/proto/v2/tenant/events.proto | 4 + packages/models/proto/v2/tenant/tenant.proto | 5 + packages/models/src/brandedIds.ts | 10 + packages/models/src/constants.ts | 4 + packages/models/src/delegation/delegation.ts | 75 ++ .../models/src/delegation/delegationEvents.ts | 85 ++ .../src/delegation/protobufConverterFromV2.ts | 125 ++ .../src/delegation/protobufConverterToV2.ts | 87 ++ packages/models/src/index.ts | 9 + .../src/read-models/delegationReadModel.ts | 5 + .../src/tenant/protobufConverterFromV2.ts | 16 +- .../src/tenant/protobufConverterToV2.ts | 8 + packages/models/src/tenant/tenant.ts | 14 +- packages/models/src/tenant/tenantEvents.ts | 9 + packages/purpose-outbound-writer/package.json | 2 +- packages/tenant-outbound-writer/package.json | 2 +- .../src/converters/toOutboundEventV2.ts | 1 + .../src/model/domain/apiConverter.ts | 5 + .../tenant-process/src/model/domain/errors.ts | 11 + .../src/model/domain/toEvent.ts | 17 + .../src/routers/TenantRouter.ts | 25 + .../src/services/tenantService.ts | 52 +- .../tenant-process/src/services/validators.ts | 27 +- .../src/utilities/errorMappers.ts | 10 + .../test/addCertifiedAttribute.test.ts | 7 +- ...signTenantDelegatedProducerFeature.test.ts | 107 ++ .../test/revokeCertifiedAttributeById.test.ts | 3 +- .../test/selfcareUpsertTenant.test.ts | 6 +- .../src/tenantConsumerServiceV2.ts | 1 + .../test/converterV1.ts | 4 +- pnpm-lock.yaml | 287 ++++- 138 files changed, 8029 insertions(+), 212 deletions(-) create mode 100644 collections/bff/delegation/Approve Delegation.bru create mode 100644 collections/bff/delegation/Create Delegation.bru create mode 100644 collections/bff/delegation/Get Delegation ById.bru create mode 100644 collections/bff/delegation/Get Delegations.bru create mode 100644 collections/bff/delegation/Reject Delegation.bru create mode 100644 collections/bff/delegation/Revoke Delegation.bru create mode 100644 collections/delegation/Delegation Process Micro Service/health/Health status endpoint.bru create mode 100644 collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru create mode 100644 collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru create mode 100644 collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru create mode 100644 collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru create mode 100644 collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru create mode 100644 collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru create mode 100644 packages/api-clients/open-api/delegationApi.yml create mode 100644 packages/backend-for-frontend/src/api/delegationApiConverter.ts create mode 100644 packages/backend-for-frontend/src/routers/delegationRouter.ts create mode 100644 packages/backend-for-frontend/src/routers/producerDelegationRouter.ts create mode 100644 packages/backend-for-frontend/src/services/delegationService.ts create mode 100644 packages/delegation-process/.env create mode 100644 packages/delegation-process/Dockerfile create mode 100644 packages/delegation-process/aws.config.local create mode 100644 packages/delegation-process/package.json create mode 100644 packages/delegation-process/src/app.ts create mode 100644 packages/delegation-process/src/config/config.ts create mode 100644 packages/delegation-process/src/index.ts create mode 100644 packages/delegation-process/src/model/domain/apiConverter.ts create mode 100644 packages/delegation-process/src/model/domain/errors.ts create mode 100644 packages/delegation-process/src/model/domain/models.ts create mode 100644 packages/delegation-process/src/model/domain/toEvent.ts create mode 100644 packages/delegation-process/src/resources/assets/font/Montserrat-Bold.ttf create mode 100644 packages/delegation-process/src/resources/assets/font/Montserrat-Italic.ttf create mode 100644 packages/delegation-process/src/resources/assets/font/Montserrat-Medium.ttf create mode 100644 packages/delegation-process/src/resources/assets/font/Montserrat-Regular.ttf create mode 100644 packages/delegation-process/src/resources/assets/font/Montserrat-SemiBold.ttf create mode 100644 packages/delegation-process/src/resources/assets/media/logo_pagopa.png create mode 100644 packages/delegation-process/src/resources/assets/media/pictogram_pagopa.svg create mode 100644 packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html create mode 100644 packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html create mode 100644 packages/delegation-process/src/resources/templates/delegationTemplate.css create mode 100644 packages/delegation-process/src/routers/DelegationProducerRouter.ts create mode 100644 packages/delegation-process/src/routers/DelegationRouter.ts create mode 100644 packages/delegation-process/src/routers/HealthRouter.ts create mode 100644 packages/delegation-process/src/services/delegationContractBuilder.ts create mode 100644 packages/delegation-process/src/services/delegationProducerService.ts create mode 100644 packages/delegation-process/src/services/delegationService.ts create mode 100644 packages/delegation-process/src/services/readModelService.ts create mode 100644 packages/delegation-process/src/services/validators.ts create mode 100644 packages/delegation-process/src/utilites/errorMappers.ts create mode 100644 packages/delegation-process/test/.eslintrc.json create mode 100644 packages/delegation-process/test/approveProducerDelegation.test.ts create mode 100644 packages/delegation-process/test/createProducerDelegation.test.ts create mode 100644 packages/delegation-process/test/getDelegationById.test.ts create mode 100644 packages/delegation-process/test/getDelegations.test.ts create mode 100644 packages/delegation-process/test/rejectProducerDelegation.test.ts create mode 100644 packages/delegation-process/test/revokeProducerDelegation.test.ts create mode 100644 packages/delegation-process/test/tsconfig.json create mode 100644 packages/delegation-process/test/utils.ts create mode 100644 packages/delegation-process/test/vitestGlobalSetup.ts create mode 100644 packages/delegation-process/tsconfig.check.json create mode 100644 packages/delegation-process/tsconfig.json create mode 100644 packages/delegation-process/vitest.config.ts create mode 100644 packages/delegation-readmodel-writer/.env create mode 100644 packages/delegation-readmodel-writer/Dockerfile create mode 100644 packages/delegation-readmodel-writer/package.json create mode 100644 packages/delegation-readmodel-writer/src/config/config.ts create mode 100644 packages/delegation-readmodel-writer/src/delegationConsumerServiceV2.ts create mode 100644 packages/delegation-readmodel-writer/src/index.ts create mode 100644 packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts create mode 100644 packages/delegation-readmodel-writer/test/tsconfig.json create mode 100644 packages/delegation-readmodel-writer/test/utils.ts create mode 100644 packages/delegation-readmodel-writer/test/vitestGlobalSetup.ts create mode 100644 packages/delegation-readmodel-writer/tsconfig.check.json create mode 100644 packages/delegation-readmodel-writer/tsconfig.json create mode 100644 packages/delegation-readmodel-writer/vitest.config.ts create mode 100644 packages/models/proto/v2/delegation/delegation.proto create mode 100644 packages/models/proto/v2/delegation/events.proto create mode 100644 packages/models/src/constants.ts create mode 100644 packages/models/src/delegation/delegation.ts create mode 100644 packages/models/src/delegation/delegationEvents.ts create mode 100644 packages/models/src/delegation/protobufConverterFromV2.ts create mode 100644 packages/models/src/delegation/protobufConverterToV2.ts create mode 100644 packages/models/src/read-models/delegationReadModel.ts create mode 100644 packages/tenant-process/test/assignTenantDelegatedProducerFeature.test.ts diff --git a/collections/bff/delegation/Approve Delegation.bru b/collections/bff/delegation/Approve Delegation.bru new file mode 100644 index 0000000000..d82cf26b2c --- /dev/null +++ b/collections/bff/delegation/Approve Delegation.bru @@ -0,0 +1,20 @@ +meta { + name: Approve delegation + type: http + seq: 1 +} + +post { + url: {{host-bff}}/producer/delegations/:delegationId/approve + body: none + auth: none +} + +params:path { + delegationId: dd10132c-f3c9-45cf-994c-3a312bc4ab4e +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/bff/delegation/Create Delegation.bru b/collections/bff/delegation/Create Delegation.bru new file mode 100644 index 0000000000..b13d48c183 --- /dev/null +++ b/collections/bff/delegation/Create Delegation.bru @@ -0,0 +1,23 @@ +meta { + name: Create new delegation + type: http + seq: 1 +} + +post { + url: {{host-bff}}/producer/delegations + body: json + auth: none +} + +body:json { + { + "eserviceId": "2f19b864-0fdc-4d2d-b01f-52bfad74fd34", + "delegateId": "aada3e71-c544-4fa0-beb8-81274ae0addf" + } +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/bff/delegation/Get Delegation ById.bru b/collections/bff/delegation/Get Delegation ById.bru new file mode 100644 index 0000000000..3510272d24 --- /dev/null +++ b/collections/bff/delegation/Get Delegation ById.bru @@ -0,0 +1,20 @@ +meta { + name: Retrieves a delegation by ID + type: http + seq: 1 +} + +get { + url: {{host-bff}}/delegations/:delegationId + body: none + auth: none +} + +params:path { + delegationId: dd10132c-f3c9-45cf-994c-3a312bc4ab4e +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/bff/delegation/Get Delegations.bru b/collections/bff/delegation/Get Delegations.bru new file mode 100644 index 0000000000..d9dc4460ca --- /dev/null +++ b/collections/bff/delegation/Get Delegations.bru @@ -0,0 +1,26 @@ +meta { + name: List delegations + type: http + seq: 1 +} + +get { + url: {{host-bff}}/delegations?offset=0&limit=50 + body: none + auth: none +} + +params:query { + offset: 0 + limit: 50 + ~kind: DELEGATED_PRODUCER + ~states: WAITING_FOR_APPROVAL + ~delegatorIds: + ~delegateIds: + ~eserviceIds: +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/bff/delegation/Reject Delegation.bru b/collections/bff/delegation/Reject Delegation.bru new file mode 100644 index 0000000000..5dcd6cb2fe --- /dev/null +++ b/collections/bff/delegation/Reject Delegation.bru @@ -0,0 +1,26 @@ +meta { + name: Reject delegation + type: http + seq: 1 +} + +post { + url: {{host-bff}}/producer/delegations/:delegationId/reject + body: json + auth: none +} + +body:json { + { + "rejectionReason": "I'm not feel comfortable with this delegation" + } +} + +params:path { + delegationId: dd10132c-f3c9-45cf-994c-3a312bc4ab4e +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/bff/delegation/Revoke Delegation.bru b/collections/bff/delegation/Revoke Delegation.bru new file mode 100644 index 0000000000..1e75ef2e8d --- /dev/null +++ b/collections/bff/delegation/Revoke Delegation.bru @@ -0,0 +1,20 @@ +meta { + name: Revoke a delegation by ID + type: http + seq: 1 +} + +delete { + url: {{host-bff}}/producer/delegations/:delegationId + body: none + auth: none +} + +params:path { + delegationId: dd10132c-f3c9-45cf-994c-3a312bc4ab4e +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/delegation/Delegation Process Micro Service/health/Health status endpoint.bru b/collections/delegation/Delegation Process Micro Service/health/Health status endpoint.bru new file mode 100644 index 0000000000..30508a436a --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/health/Health status endpoint.bru @@ -0,0 +1,11 @@ +meta { + name: Health status endpoint + type: http + seq: 1 +} + +get { + url: {{host-delegation}}/status + body: none + auth: none +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru b/collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru new file mode 100644 index 0000000000..23bd4e2e56 --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru @@ -0,0 +1,20 @@ +meta { + name: Approves a delegation + type: http + seq: 4 +} + +post { + url: {{host-delegation}}/producer/delegations/:delegationId/approve + body: none + auth: none +} + +params:path { + delegationId: +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru b/collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru new file mode 100644 index 0000000000..fa36a2e7e5 --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru @@ -0,0 +1,23 @@ +meta { + name: Delegation Creation + type: http + seq: 3 +} + +post { + url: {{host-delegation}}/producer/delegations + body: json + auth: none +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} + +body:json { + { + "eserviceId": "", + "delegateId": "" + } +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru b/collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru new file mode 100644 index 0000000000..733ec7c274 --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru @@ -0,0 +1,26 @@ +meta { + name: List producer delegations + type: http + seq: 1 +} + +get { + url: {{host-delegation}}/delegations?offset=0&limit=50 + body: none + auth: none +} + +params:query { + offset: 0 + limit: 50 + ~kind: DELEGATED_PRODUCER + ~delegationStates: WAITING_FOR_APPROVAL + ~delegatorIds: + ~delegateIds: + ~eserviceIds: +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru b/collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru new file mode 100644 index 0000000000..b2b1477d62 --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru @@ -0,0 +1,26 @@ +meta { + name: Rejects a delegation + type: http + seq: 5 +} + +post { + url: {{host-delegation}}/producer/delegations/:delegationId/reject + body: json + auth: none +} + +params:path { + delegationId: +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} + +body:json { + { + "rejectionReason": "" + } +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru b/collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru new file mode 100644 index 0000000000..4e9425bd6d --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru @@ -0,0 +1,20 @@ +meta { + name: Retrieves a delegation + type: http + seq: 2 +} + +get { + url: {{host-delegation}}/delegations/:delegationId + body: none + auth: none +} + +params:path { + delegationId: 123e4567-e89b-12d3-a456-426614174000 +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru b/collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru new file mode 100644 index 0000000000..69741abb1d --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru @@ -0,0 +1,16 @@ +meta { + name: Revoke a delegation + type: http + seq: 6 +} + +delete { + url: {{host-delegation}}/producer/delegations/:delegationId + body: none + auth: none +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/environments/PagoPA local.bru b/collections/environments/PagoPA local.bru index db3d9de5bb..f0051f88bb 100644 --- a/collections/environments/PagoPA local.bru +++ b/collections/environments/PagoPA local.bru @@ -9,6 +9,7 @@ vars { host-tenant: http://localhost:3500 host-purpose: http://localhost:3400 host-authorization: http://localhost:3300 + host-delegation: http://localhost:3800 host-api-gw: http://localhost:3700/api-gateway/0.0 JWT-M2M: Bearer {{process.env.JWT-M2M}} } diff --git a/docker/event-store-init.sql b/docker/event-store-init.sql index acb43f819b..8b7c90b3c9 100644 --- a/docker/event-store-init.sql +++ b/docker/event-store-init.sql @@ -119,3 +119,22 @@ CREATE TABLE notification_event.producer_keys_events ( kid varchar NOT NULL, event_type varchar NOT NULL ); + +create schema delegation; +CREATE TABLE delegation.events( + sequence_num bigserial NOT NULL, + + stream_id uuid NOT NULL, + version bigint NOT NULL, + + correlation_id text, + + type text NOT NULL, + event_version int NOT NULL, + data bytea NOT NULL, + + log_date timestamptz NOT NULL DEFAULT now(), + + PRIMARY KEY (sequence_num), + UNIQUE (stream_id, version) +); \ No newline at end of file diff --git a/package.json b/package.json index e9fe706e5a..9abb632b87 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,8 @@ "start:ivass-certified-attributes-importer": "turbo start --filter pagopa-interop-ivass-certified-attributes-importer", "start:pn-consumers": "turbo start --filter pagopa-interop-pn-consumers", "start:one-trust-notices": "turbo start --filter pagopa-interop-one-trust-notices", + "start:delegation": "turbo start --filter pagopa-interop-delegation-process", + "start:delegation-readmodel-writer": "turbo start --filter pagopa-interop-delegation-readmodel-writer", "start:datalake-data-export": "turbo start --filter pagopa-interop-datalake-data-export", "test": "turbo test", "build": "turbo build", diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index 29c67eb697..c414df4e13 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.4-b", + "@pagopa/interop-outbound-models": "1.0.6-b", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/anac-certified-attributes-importer/src/service/processor.ts b/packages/anac-certified-attributes-importer/src/service/processor.ts index eb82b0e825..2499b31134 100644 --- a/packages/anac-certified-attributes-importer/src/service/processor.ts +++ b/packages/anac-certified-attributes-importer/src/service/processor.ts @@ -1,7 +1,7 @@ /* eslint-disable max-params */ import { parse } from "csv/sync"; import { Logger, RefreshableInteropToken, zipBy } from "pagopa-interop-commons"; -import { CorrelationId } from "pagopa-interop-models"; +import { TenantFeatureCertifier, CorrelationId } from "pagopa-interop-models"; import { AnacAttributes, AttributeIdentifiers, @@ -201,7 +201,7 @@ async function getAttributesIdentifiers( anacTenantId ); const certifier = anacTenant.features.find( - (f) => f.type === "PersistentCertifier" + (f): f is TenantFeatureCertifier => f.type === "PersistentCertifier" ); if (!certifier) { diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index dcce9750ca..2c9ab3e0e7 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -51,6 +51,16 @@ tags: externalDocs: description: Find out more url: "http://swagger.io" + - name: delegations + description: Delegation Module + externalDocs: + description: Find out more + url: "http://swagger.io" + - name: producerDelegations + description: Producer Delegation Module + externalDocs: + description: Find out more + url: "http://swagger.io" - name: tools description: Utility Module externalDocs: @@ -8312,6 +8322,66 @@ paths: schema: type: integer description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + /tenants/delegatedProducer: + post: + tags: + - tenants + summary: Assign delegated producer feature to tenant caller + operationId: assignTenantDelegatedProducerFeature + responses: + "204": + description: Delegated producer feature assigned + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "409": + description: Feature already assigned to tenant + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available /clients: parameters: - $ref: "#/components/parameters/CorrelationIdHeader" @@ -9442,7 +9512,7 @@ paths: minItems: 1 items: type: string - format: uuid + format: uuid responses: "200": description: Users added @@ -11005,7 +11075,7 @@ paths: minItems: 1 items: type: string - format: uuid + format: uuid responses: "204": description: Users added @@ -11955,143 +12025,780 @@ paths: schema: type: integer description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available - "/status": + /delegations: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" get: - security: [] tags: - - health - summary: Health status endpoint - description: Return ok - operationId: getStatus + - delegations + summary: List delegations + description: List delegations + operationId: getDelegations + parameters: + - in: query + name: offset + required: true + schema: + type: integer + format: int32 + minimum: 0 + - in: query + name: limit + required: true + schema: + type: integer + format: int32 + minimum: 1 + maximum: 50 + - in: query + name: states + description: comma separated sequence of delegation states to filter the results with + schema: + type: array + items: + $ref: "#/components/schemas/DelegationState" + default: [] + explode: false + - in: query + name: delegatorIds + schema: + type: array + items: + type: string + format: uuid + description: "The delegator ids to filter by" + - in: query + name: delegateIds + schema: + type: array + items: + type: string + format: uuid + description: "The delegated ids to filter by" + - in: query + name: kind + schema: + $ref: "#/components/schemas/DelegationKind" + description: "The delegation kind to filter by" + - in: query + name: eserviceIds + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false responses: "200": - description: successful operation + description: Delegations found + content: + application/json: + schema: + $ref: "#/components/schemas/CompactDelegations" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "400": + description: Bad Request content: application/json: schema: $ref: "#/components/schemas/Problem" -components: - parameters: - CorrelationIdHeader: - in: header - name: X-Correlation-Id - required: true - schema: - type: string - schemas: - RejectPurposeVersionPayload: - type: object - additionalProperties: false - description: models the reject payload for this purpose version. - properties: - rejectionReason: - type: string - required: - - rejectionReason - GoogleSAMLPayload: - type: object - additionalProperties: false - properties: - SAMLResponse: - type: string - description: SAML response - RelayState: - type: string - nullable: true - required: - - SAMLResponse - SAMLTokenRequest: - type: object - additionalProperties: false - properties: - saml2: - type: string - description: SAML - tenantId: - type: string - format: uuid - description: tenant id - required: - - saml2 - - tenantId - AccessTokenRequest: - type: object - additionalProperties: false - required: - - client_assertion - - client_assertion_type - - grant_type - properties: - client_id: - type: string - example: e58035ce-c753-4f72-b613-46f8a17b71cc - client_assertion: - type: string - format: jws - client_assertion_type: - type: string - grant_type: - type: string - PrivacyNotice: - type: object - additionalProperties: false - properties: - id: - type: string - format: uuid - userId: - type: string - format: uuid - consentType: - $ref: "#/components/schemas/ConsentType" - firstAccept: - type: boolean - isUpdated: - type: boolean - latestVersionId: - type: string - format: uuid - required: - - id - - userId - - consentType - - firstAccept - - isUpdated - - latestVersionId - ConsentType: - type: string - description: Consent Type - enum: - - PP - - TOS - PrivacyNoticeSeed: - type: object - additionalProperties: false - properties: - latestVersionId: - type: string - format: uuid - required: - - latestVersionId - RiskAnalysisFormConfig: - type: object - additionalProperties: false - properties: - version: - type: string - questions: - type: array - items: - $ref: "#/components/schemas/FormConfigQuestion" - required: - - version - - questions - FormConfigQuestion: - type: object - additionalProperties: false - properties: - id: - type: string + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "429": + description: Too Many Requests + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + /producer/delegations: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + summary: Producer delegation creation + operationId: createProducerDelegation + responses: + "200": + description: Delegation created + content: + application/json: + schema: + $ref: "#/components/schemas/CreatedResource" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "429": + description: Too Many Requests + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + tags: + - producerDelegations + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DelegationSeed" + description: payload for producer delegation creation + required: true + description: creates the producer delegation + "/producer/delegations/{delegationId}/approve": + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + tags: + - producerDelegations + summary: Approves a producer delegation + description: Approves a producer delegation + operationId: approveDelegation + parameters: + - name: delegationId + in: path + description: The identifier of the delegation + required: true + schema: + type: string + format: uuid + responses: + "204": + description: Delegation approved + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "429": + description: Too Many Requests + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "/producer/delegations/{delegationId}/reject": + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + tags: + - producerDelegations + summary: Rejects a producer delegation + description: Rejects a producer delegation + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/RejectDelegationPayload" + operationId: rejectDelegation + parameters: + - name: delegationId + in: path + description: The identifier of the delegation + required: true + schema: + type: string + format: uuid + responses: + "204": + description: Delegation rejected + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "429": + description: Too Many Requests + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + /delegations/{delegationId}: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + get: + description: Retrieves delegation + summary: Retrieves delegation + tags: + - delegations + operationId: getDelegation + responses: + "200": + description: Producer delegation retrieved + content: + application/json: + schema: + $ref: "#/components/schemas/Delegation" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + /producer/delegations/{delegationId}: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + delete: + description: Revokes a producer delegation + tags: + - producerDelegations + summary: Revokes a producer delegation + operationId: revokeProducerDelegation + responses: + "204": + description: Delegation revoked + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "/status": + get: + security: [] + tags: + - health + summary: Health status endpoint + description: Return ok + operationId: getStatus + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" +components: + parameters: + CorrelationIdHeader: + in: header + name: X-Correlation-Id + required: true + schema: + type: string + schemas: + RejectPurposeVersionPayload: + type: object + additionalProperties: false + description: models the reject payload for this purpose version. + properties: + rejectionReason: + type: string + required: + - rejectionReason + GoogleSAMLPayload: + type: object + additionalProperties: false + properties: + SAMLResponse: + type: string + description: SAML response + RelayState: + type: string + nullable: true + required: + - SAMLResponse + SAMLTokenRequest: + type: object + additionalProperties: false + properties: + saml2: + type: string + description: SAML + tenantId: + type: string + format: uuid + description: tenant id + required: + - saml2 + - tenantId + AccessTokenRequest: + type: object + additionalProperties: false + required: + - client_assertion + - client_assertion_type + - grant_type + properties: + client_id: + type: string + example: e58035ce-c753-4f72-b613-46f8a17b71cc + client_assertion: + type: string + format: jws + client_assertion_type: + type: string + grant_type: + type: string + PrivacyNotice: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + userId: + type: string + format: uuid + consentType: + $ref: "#/components/schemas/ConsentType" + firstAccept: + type: boolean + isUpdated: + type: boolean + latestVersionId: + type: string + format: uuid + required: + - id + - userId + - consentType + - firstAccept + - isUpdated + - latestVersionId + ConsentType: + type: string + description: Consent Type + enum: + - PP + - TOS + PrivacyNoticeSeed: + type: object + additionalProperties: false + properties: + latestVersionId: + type: string + format: uuid + required: + - latestVersionId + RiskAnalysisFormConfig: + type: object + additionalProperties: false + properties: + version: + type: string + questions: + type: array + items: + $ref: "#/components/schemas/FormConfigQuestion" + required: + - version + - questions + FormConfigQuestion: + type: object + additionalProperties: false + properties: + id: + type: string label: $ref: "#/components/schemas/LocalizedText" infoLabel: @@ -14093,11 +14800,17 @@ components: - results - pagination TenantFeature: - type: object - additionalProperties: false - properties: - certifier: - $ref: "#/components/schemas/Certifier" + oneOf: + - type: object + additionalProperties: false + properties: + certifier: + $ref: "#/components/schemas/Certifier" + - type: object + additionalProperties: false + properties: + delegatedProducer: + $ref: "#/components/schemas/DelegatedProducer" Certifier: description: Certifier Tenant Feature type: object @@ -14107,6 +14820,16 @@ components: type: string required: - certifierId + DelegatedProducer: + description: Delegated producer Tenant Feature + type: object + additionalProperties: false + properties: + availabilityTimestamp: + type: string + format: date-time + required: + - availabilityTimestamp CompactTenant: properties: id: @@ -14463,6 +15186,144 @@ components: format: uuid required: - id + DelegationKind: + type: string + description: Delegation State + enum: + - DELEGATED_PRODUCER + - DELEGATED_CONSUMER + DelegationState: + type: string + description: Delegation State + enum: + - WAITING_FOR_APPROVAL + - ACTIVE + - REJECTED + - REVOKED + DelegationTenant: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + name: + type: string + required: + - id + - name + DelegationEService: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + name: + type: string + description: + type: string + producerId: + type: string + format: uuid + producerName: + type: string + required: + - id + - name + - producerId + - producerName + Delegation: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + eservice: + $ref: "#/components/schemas/DelegationEService" + delegate: + $ref: "#/components/schemas/DelegationTenant" + delegator: + $ref: "#/components/schemas/DelegationTenant" + activationContract: + $ref: "#/components/schemas/Document" + revocationContract: + $ref: "#/components/schemas/Document" + submittedAt: + type: string + format: date-time + rejectionReason: + type: string + state: + $ref: "#/components/schemas/DelegationState" + kind: + $ref: "#/components/schemas/DelegationKind" + required: + - id + - eservice + - delegate + - delegator + - state + - kind + CompactDelegation: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + eserviceName: + type: string + delegate: + $ref: "#/components/schemas/DelegationTenant" + delegator: + $ref: "#/components/schemas/DelegationTenant" + state: + $ref: "#/components/schemas/DelegationState" + kind: + $ref: "#/components/schemas/DelegationKind" + required: + - id + - eserviceName + - delegate + - delegator + - state + - kind + CompactDelegations: + type: object + additionalProperties: false + properties: + results: + type: array + items: + $ref: "#/components/schemas/CompactDelegation" + pagination: + $ref: "#/components/schemas/Pagination" + required: + - results + - pagination + DelegationSeed: + type: object + additionalProperties: false + properties: + eserviceId: + type: string + format: uuid + delegateId: + type: string + format: uuid + required: + - eserviceId + - delegateId + RejectDelegationPayload: + type: object + additionalProperties: false + properties: + rejectionReason: + type: string + required: + - rejectionReason Problem: type: object additionalProperties: false diff --git a/packages/api-clients/open-api/delegationApi.yml b/packages/api-clients/open-api/delegationApi.yml new file mode 100644 index 0000000000..c87eeff3b2 --- /dev/null +++ b/packages/api-clients/open-api/delegationApi.yml @@ -0,0 +1,567 @@ +openapi: 3.0.3 +info: + title: Delegation Process Micro Service + description: This service is the delegation process + version: "v1" + contact: + name: API Support + url: "http://www.example.com/support" + email: support@example.com + termsOfService: "http://swagger.io/terms/" + x-api-id: an x-api-id + x-summary: an x-summary +servers: + - url: "/delegation-process/v1" + description: This service is the Delegation Process +security: + - bearerAuth: [] +tags: + - name: delegation + description: Delegation common operations + externalDocs: + description: Find out more + url: http://swagger.io + - name: producer + description: Lead organization + externalDocs: + description: Find out more + url: "http://swagger.io" + - name: health + description: Verify service status + externalDocs: + description: Find out more + url: http://swagger.io +paths: + /delegations: + get: + description: List delegations + summary: List delegations + tags: + - delegation + operationId: getDelegations + parameters: + - in: query + name: delegationStates + required: false + schema: + type: array + items: + $ref: "#/components/schemas/DelegationState" + default: [] + explode: false + - in: query + name: delegatorIds + required: false + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false + - in: query + name: delegateIds + required: false + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false + - in: query + name: kind + required: false + schema: + $ref: "#/components/schemas/DelegationKind" + - in: query + name: offset + required: true + schema: + type: integer + format: int32 + minimum: 0 + - in: query + name: limit + required: true + schema: + type: integer + format: int32 + minimum: 1 + maximum: 50 + - in: query + name: eserviceIds + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false + responses: + "200": + description: Delegations retrieved + content: + application/json: + schema: + $ref: "#/components/schemas/Delegations" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /delegations/{delegationId}: + get: + description: Retrieves a delegation + summary: Retrieves a delegation + tags: + - delegation + operationId: getDelegation + parameters: + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + responses: + "200": + description: Delegation retrieved + content: + application/json: + schema: + $ref: "#/components/schemas/Delegation" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /producer/delegations: + post: + description: creates the delegation + summary: Delegation Creation + tags: + - producer + operationId: createProducerDelegation + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DelegationSeed" + description: payload for delegation creation + required: true + responses: + "200": + description: Delegation created. + content: + application/json: + schema: + $ref: "#/components/schemas/Delegation" + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /producer/delegations/{delegationId}/approve: + post: + description: Approves a delegation + summary: Approves a delegation + tags: + - producer + operationId: approveProducerDelegation + parameters: + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + format: uuid + responses: + "204": + description: Delegation approved + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /producer/delegations/{delegationId}/reject: + post: + description: Rejects a delegation + summary: Rejects a delegation + tags: + - producer + operationId: rejectProducerDelegation + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/RejectDelegationPayload" + required: true + description: payload for delegation rejection + parameters: + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + format: uuid + responses: + "204": + description: Delegation rejected + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /producer/delegations/{delegationId}: + parameters: + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + delete: + description: Revokes a delegation + summary: Revokes a delegation + tags: + - producer + operationId: revokeProducerDelegation + responses: + "204": + description: Delegation revoked + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /status: + get: + description: Returns ok + summary: Health status endpoint + tags: + - health + operationId: getStatus + security: [] + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" +components: + schemas: + Delegations: + type: object + additionalProperties: false + properties: + results: + type: array + items: + $ref: "#/components/schemas/Delegation" + totalCount: + type: integer + format: int32 + required: + - results + - totalCount + CreatedResource: + type: object + additionalProperties: false + description: contains the id of the created resource + properties: + id: + type: string + format: uuid + required: + - id + Delegation: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + delegatorId: + type: string + format: uuid + delegateId: + type: string + format: uuid + eserviceId: + type: string + format: uuid + createdAt: + type: string + format: date-time + submittedAt: + type: string + format: date-time + approvedAt: + type: string + format: date-time + rejectedAt: + type: string + format: date-time + rejectionReason: + type: string + revokedAt: + type: string + format: date-time + state: + $ref: "#/components/schemas/DelegationState" + kind: + $ref: "#/components/schemas/DelegationKind" + activationContract: + $ref: "#/components/schemas/DelegationContractDocument" + revocationContract: + $ref: "#/components/schemas/DelegationContractDocument" + stamps: + $ref: "#/components/schemas/DelegationStamps" + required: + - id + - delegatorId + - delegateId + - eserviceId + - createdAt + - submittedAt + - state + - kind + - stamps + DelegationState: + type: string + enum: + - WAITING_FOR_APPROVAL + - ACTIVE + - REJECTED + - REVOKED + DelegationKind: + type: string + enum: + - DELEGATED_PRODUCER + - DELEGATED_CONSUMER + DelegationContractDocument: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + name: + type: string + prettyName: + type: string + contentType: + type: string + path: + type: string + createdAt: + type: string + format: date-time + required: + - id + - name + - prettyName + - contentType + - path + - createdAt + DelegationStamp: + type: object + additionalProperties: false + properties: + who: + type: string + format: uuid + when: + type: string + format: date-time + required: + - who + - when + DelegationStamps: + type: object + additionalProperties: false + properties: + submission: + $ref: "#/components/schemas/DelegationStamp" + activation: + $ref: "#/components/schemas/DelegationStamp" + rejection: + $ref: "#/components/schemas/DelegationStamp" + revocation: + $ref: "#/components/schemas/DelegationStamp" + required: + - submission + DelegationSeed: + type: object + additionalProperties: false + properties: + eserviceId: + type: string + format: uuid + delegateId: + type: string + format: uuid + required: + - eserviceId + - delegateId + RejectDelegationPayload: + type: object + additionalProperties: false + properties: + rejectionReason: + type: string + required: + - rejectionReason + Problem: + properties: + type: + description: URI reference of type definition + type: string + status: + description: The HTTP status code generated by the origin server for this occurrence of the problem. + example: 503 + exclusiveMaximum: true + format: int32 + maximum: 600 + minimum: 100 + type: integer + title: + description: A short, summary of the problem type. Written in english and readable + example: Service Unavailable + maxLength: 64 + pattern: "^[ -~]{0,64}$" + type: string + correlationId: + description: Unique identifier of the request + example: "53af4f2d-0c87-41ef-a645-b726a821852b" + maxLength: 64 + type: string + detail: + description: A human readable explanation of the problem. + example: Request took too long to complete. + maxLength: 4096 + pattern: "^.{0,1024}$" + type: string + errors: + type: array + minItems: 1 + items: + $ref: "#/components/schemas/ProblemError" + additionalProperties: false + required: + - type + - status + - title + - errors + ProblemError: + properties: + code: + description: Internal code of the error + example: 123-4567 + minLength: 8 + maxLength: 8 + pattern: "^[0-9]{3}-[0-9]{4}$" + type: string + detail: + description: A human readable explanation specific to this occurrence of the problem. + example: Parameter not valid + maxLength: 4096 + pattern: "^.{0,1024}$" + type: string + required: + - code + - detail + securitySchemes: + bearerAuth: + type: http + description: A bearer token in the format of a JWS and comformed to the specifications included in [RFC8725](https://tools.ietf.org/html/RFC8725). + scheme: bearer + bearerFormat: JWT diff --git a/packages/api-clients/open-api/tenantApi.yml b/packages/api-clients/open-api/tenantApi.yml index d4dc2b3864..4bed5af187 100644 --- a/packages/api-clients/open-api/tenantApi.yml +++ b/packages/api-clients/open-api/tenantApi.yml @@ -975,6 +975,27 @@ paths: application/json: schema: $ref: "#/components/schemas/Problem" + /tenants/delegatedProducer: + post: + tags: + - tenant + summary: Assign delegated producer feature to tenant caller + operationId: assignTenantDelegatedProducerFeature + responses: + "204": + description: Delegated producer feature assigned + "409": + description: Feature already assigned to tenant + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" /status: get: security: [] @@ -1119,11 +1140,17 @@ components: - results - totalCount TenantFeature: - type: object - additionalProperties: false - properties: - certifier: - $ref: "#/components/schemas/Certifier" + oneOf: + - type: object + additionalProperties: false + properties: + certifier: + $ref: "#/components/schemas/Certifier" + - type: object + additionalProperties: false + properties: + delegatedProducer: + $ref: "#/components/schemas/DelegatedProducer" Certifier: description: Certifier Tenant Feature type: object @@ -1133,6 +1160,16 @@ components: type: string required: - certifierId + DelegatedProducer: + description: Delegated producer Tenant Feature + type: object + additionalProperties: false + properties: + availabilityTimestamp: + type: string + format: date-time + required: + - availabilityTimestamp CertifiedAttribute: type: object additionalProperties: false diff --git a/packages/api-clients/src/index.ts b/packages/api-clients/src/index.ts index ed8c86e0f6..2a5174bbba 100644 --- a/packages/api-clients/src/index.ts +++ b/packages/api-clients/src/index.ts @@ -9,4 +9,5 @@ export * as selfcareV2ClientApi from "./generated/selfcareV2ClientApi.js"; export * as tenantApi from "./generated/tenantApi.js"; export * as apiGatewayApi from "./apiGatewayApi.js"; export * as notifierApi from "./generated/notifierApi.js"; +export * as delegationApi from "./generated/delegationApi.js"; export * from "./selfcareClients.js"; diff --git a/packages/api-clients/template-bff.hbs b/packages/api-clients/template-bff.hbs index c53bf62143..a194e18ce6 100644 --- a/packages/api-clients/template-bff.hbs +++ b/packages/api-clients/template-bff.hbs @@ -48,6 +48,8 @@ export const {{@key}}Endpoints = makeApi([ schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(EServiceDescriptorState).optional().default([])), z.array(EServiceDescriptorState).optional().default([])]) {{else if (and (eq type "Query") (eq schema "z.array(PurposeVersionState).optional().default([])"))}} schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(PurposeVersionState).optional().default([])), z.array(PurposeVersionState).optional().default([])]) + {{else if (and (eq type "Query") (eq schema "z.array(DelegationState).optional().default([])"))}} + schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(DelegationState).optional().default([])), z.array(DelegationState).optional().default([])]) {{else}} schema: {{{schema}}}, {{/if}} diff --git a/packages/api-clients/template.hbs b/packages/api-clients/template.hbs index 3c21e0ece0..04d8e3669b 100644 --- a/packages/api-clients/template.hbs +++ b/packages/api-clients/template.hbs @@ -48,6 +48,8 @@ export const {{@key}}Endpoints = makeApi([ schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(EServiceDescriptorState).optional().default([])), z.array(EServiceDescriptorState).optional().default([])]) {{else if (and (eq type "Query") (eq schema "z.array(PurposeVersionState).optional().default([])"))}} schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(PurposeVersionState).optional().default([])), z.array(PurposeVersionState).optional().default([])]) + {{else if (and (eq type "Query") (eq schema "z.array(DelegationState).optional().default([])"))}} + schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(DelegationState).optional().default([])), z.array(DelegationState).optional().default([])]) {{else}} schema: {{{schema}}}, {{/if}} diff --git a/packages/attribute-registry-process/src/services/attributeRegistryService.ts b/packages/attribute-registry-process/src/services/attributeRegistryService.ts index 4c4b9a37f0..dd0ecc681a 100644 --- a/packages/attribute-registry-process/src/services/attributeRegistryService.ts +++ b/packages/attribute-registry-process/src/services/attributeRegistryService.ts @@ -15,6 +15,7 @@ import { AttributeId, AttributeKind, ListResult, + TenantFeatureCertifier, } from "pagopa-interop-models"; import { attributeRegistryApi } from "pagopa-interop-api-clients"; import { toCreateEventAttributeAdded } from "../model/domain/toEvent.js"; @@ -315,7 +316,10 @@ async function getCertifierId( } const certifier = tenant.features - .filter(({ type }) => type === "PersistentCertifier") + .filter( + (feature): feature is TenantFeatureCertifier => + feature.type === "PersistentCertifier" + ) .find(({ certifierId }) => certifierId.trim().length > 0); if (certifier) { diff --git a/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts b/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts index b33fee3535..9a3e5720e9 100644 --- a/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts +++ b/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts @@ -6,6 +6,7 @@ import { decodeProtobufPayload, getMockAttribute, getMockAuthData, + getTenantOneCertifierFeature, } from "pagopa-interop-commons-test"; import { genericLogger } from "pagopa-interop-commons"; import { @@ -263,7 +264,7 @@ describe("database test", () => { code: "code", kind: attributeKind.certified, creationTime: new Date(writtenPayload.attribute!.creationTime), - origin: tenant.features[0].certifierId, + origin: getTenantOneCertifierFeature(tenant).certifierId, }; expect(writtenPayload.attribute).toEqual( toAttributeV1(expectedAttribute) @@ -367,7 +368,7 @@ describe("database test", () => { { name: mockAttribute.name, code: "code", - origin: tenant.features[0].certifierId, + origin: getTenantOneCertifierFeature(tenant).certifierId, description: mockAttribute.description, }, { @@ -397,7 +398,7 @@ describe("database test", () => { code: "code", kind: attributeKind.certified, creationTime: new Date(writtenPayload.attribute!.creationTime), - origin: tenant.features[0].certifierId, + origin: getTenantOneCertifierFeature(tenant).certifierId, }; expect(writtenPayload.attribute).toEqual( toAttributeV1(expectedAttribute) @@ -405,6 +406,7 @@ describe("database test", () => { expect(writtenPayload.attribute).toEqual(toAttributeV1(attribute)); }); it("should throw attributeDuplicate if an attribute with the same name and code already exists, case insensitive", async () => { + // This test is the same as the previous one, but with a different method const attribute = { ...mockAttribute, name: mockAttribute.name.toUpperCase(), @@ -428,7 +430,7 @@ describe("database test", () => { { name: attribute.name.toLowerCase(), code: attribute.code.toLowerCase(), - origin: tenant.features[0].certifierId, + origin: getTenantOneCertifierFeature(tenant).certifierId, description: attribute.description, }, { diff --git a/packages/backend-for-frontend/.env b/packages/backend-for-frontend/.env index 22387901fa..33fe161b6b 100644 --- a/packages/backend-for-frontend/.env +++ b/packages/backend-for-frontend/.env @@ -12,6 +12,7 @@ CATALOG_PROCESS_URL="http://localhost:3000" ATTRIBUTE_REGISTRY_PROCESS_URL="http://localhost:3200" PURPOSE_PROCESS_URL="http://localhost:3400" AUTHORIZATION_PROCESS_URL="http://localhost:3300" +DELEGATION_PROCESS_URL="http://localhost:3800" TENANT_ALLOWED_ORIGINS="IPA" SAML_AUDIENCE=selfcare.dev.interop.pagopa.it diff --git a/packages/backend-for-frontend/src/api/delegationApiConverter.ts b/packages/backend-for-frontend/src/api/delegationApiConverter.ts new file mode 100644 index 0000000000..3fa376b4b4 --- /dev/null +++ b/packages/backend-for-frontend/src/api/delegationApiConverter.ts @@ -0,0 +1,108 @@ +import { + bffApi, + catalogApi, + delegationApi, + tenantApi, +} from "pagopa-interop-api-clients"; +import { + DelegationKind, + delegationKind, + delegationState, + DelegationState, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; + +export type DelegationsQueryParams = { + delegatorIds?: string[]; + delegateIds?: string[]; + delegationStates?: delegationApi.DelegationState[]; + kind?: delegationApi.DelegationKind; + eserviceIds?: string[]; +}; + +export function toDelegationState( + state: DelegationState +): bffApi.DelegationState { + return match(state) + .with(delegationState.active, () => bffApi.DelegationState.Values.ACTIVE) + .with( + delegationState.rejected, + () => bffApi.DelegationState.Values.REJECTED + ) + .with(delegationState.revoked, () => bffApi.DelegationState.Values.REVOKED) + .with( + delegationState.waitingForApproval, + () => bffApi.DelegationState.Values.WAITING_FOR_APPROVAL + ) + .exhaustive(); +} + +export function toDelegationKind( + kind: DelegationKind +): delegationApi.DelegationKind { + return match(kind) + .with( + delegationKind.delegatedConsumer, + () => bffApi.DelegationKind.Values.DELEGATED_CONSUMER + ) + .with( + delegationKind.delegatedProducer, + () => bffApi.DelegationKind.Values.DELEGATED_PRODUCER + ) + .exhaustive(); +} + +export function toBffDelegationApiDelegation( + delegation: delegationApi.Delegation, + delegator: tenantApi.Tenant, + delegate: tenantApi.Tenant, + eservice: catalogApi.EService, + producer: tenantApi.Tenant +): bffApi.Delegation { + return { + id: delegation.id, + eservice: { + id: eservice.id, + name: eservice.name, + description: eservice.description, + producerId: eservice.producerId, + producerName: producer.name, + }, + delegate: { + id: delegate.id, + name: delegate.name, + }, + delegator: { + id: delegator.id, + name: delegator.name, + }, + activationContract: delegation.activationContract, + revocationContract: delegation.revocationContract, + submittedAt: delegation.submittedAt, + rejectionReason: delegation.rejectionReason, + state: delegation.state, + kind: delegation.kind, + }; +} + +export function toBffDelegationApiCompactDelegation( + delegation: delegationApi.Delegation, + delegator: tenantApi.Tenant, + delegate: tenantApi.Tenant, + eservice: catalogApi.EService +): bffApi.CompactDelegation { + return { + id: delegation.id, + eserviceName: eservice.name, + delegate: { + name: delegate.name, + id: delegate.id, + }, + delegator: { + name: delegator.name, + id: delegator.id, + }, + state: delegation.state, + kind: delegation.kind, + }; +} diff --git a/packages/backend-for-frontend/src/app.ts b/packages/backend-for-frontend/src/app.ts index c0413917ee..0a5f810292 100644 --- a/packages/backend-for-frontend/src/app.ts +++ b/packages/backend-for-frontend/src/app.ts @@ -29,6 +29,8 @@ import { } from "./utilities/middlewares.js"; import clientRouter from "./routers/clientRouter.js"; import producerKeychainRouter from "./routers/producerKeychainRouter.js"; +import delegationRouter from "./routers/delegationRouter.js"; +import producerDelegationRouter from "./routers/producerDelegationRouter.js"; const serviceName = "backend-for-frontend"; const fileManager = initFileManager(config); @@ -89,7 +91,9 @@ app.use( tenantRouter(zodiosCtx, clients), clientRouter(zodiosCtx, clients), privacyNoticeRouter(zodiosCtx), - producerKeychainRouter(zodiosCtx, clients) + producerKeychainRouter(zodiosCtx, clients), + delegationRouter(zodiosCtx, clients), + producerDelegationRouter(zodiosCtx, clients) ); export default app; diff --git a/packages/backend-for-frontend/src/clients/clientsProvider.ts b/packages/backend-for-frontend/src/clients/clientsProvider.ts index 1c5bd07948..a67bc975e3 100644 --- a/packages/backend-for-frontend/src/clients/clientsProvider.ts +++ b/packages/backend-for-frontend/src/clients/clientsProvider.ts @@ -7,6 +7,7 @@ import { authorizationApi, selfcareV2ClientApi, selfcareV2InstitutionClientBuilder, + delegationApi, } from "pagopa-interop-api-clients"; import { config } from "../config/config.js"; @@ -32,6 +33,11 @@ export type PurposeProcessClient = ReturnType< typeof purposeApi.createPurposeApiClient >; +export type DelegationProcessClient = { + producer: ReturnType; + delegation: ReturnType; +}; + export type AuthorizationProcessClient = { client: ReturnType; producerKeychain: ReturnType< @@ -55,6 +61,7 @@ export type PagoPAInteropBeClients = { purposeProcessClient: PurposeProcessClient; authorizationClient: AuthorizationProcessClient; selfcareV2Client: SelfcareV2Client; + delegationProcessClient: DelegationProcessClient; }; export function getInteropBeClients(): PagoPAInteropBeClients { @@ -89,5 +96,13 @@ export function getInteropBeClients(): PagoPAInteropBeClients { selfcareV2Client: { institution: selfcareV2InstitutionClientBuilder(config), }, + delegationProcessClient: { + producer: delegationApi.createProducerApiClient( + config.delegationProcessUrl + ), + delegation: delegationApi.createDelegationApiClient( + config.delegationProcessUrl + ), + }, }; } diff --git a/packages/backend-for-frontend/src/config/config.ts b/packages/backend-for-frontend/src/config/config.ts index eaa6a49cda..04c1132655 100644 --- a/packages/backend-for-frontend/src/config/config.ts +++ b/packages/backend-for-frontend/src/config/config.ts @@ -99,6 +99,17 @@ export type AuthorizationProcessServerConfig = z.infer< typeof AuthorizationProcessServerConfig >; +export const DelegationProcessServerConfig = z + .object({ + DELEGATION_PROCESS_URL: APIEndpoint, + }) + .transform((c) => ({ + delegationProcessUrl: c.DELEGATION_PROCESS_URL, + })); +export type DelegationProcessServerConfig = z.infer< + typeof DelegationProcessServerConfig +>; + export const S3PrivacyNoticeConfig = z .object({ PRIVACY_NOTICES_CONTAINER: z.string(), @@ -185,6 +196,7 @@ const BffProcessConfig = CommonHTTPServiceConfig.and(TenantProcessServerConfig) .and(PurposeProcessServerConfig) .and(RedisRateLimiterConfig) .and(AuthorizationProcessServerConfig) + .and(DelegationProcessServerConfig) .and(TokenGenerationConfig) .and(SessionTokenGenerationConfig) .and(FileManagerConfig) diff --git a/packages/backend-for-frontend/src/model/errors.ts b/packages/backend-for-frontend/src/model/errors.ts index 2b6970754a..663f561192 100644 --- a/packages/backend-for-frontend/src/model/errors.ts +++ b/packages/backend-for-frontend/src/model/errors.ts @@ -45,9 +45,12 @@ export const errorCodes = { missingActivePurposeVersion: "0036", activeAgreementByEserviceAndConsumerNotFound: "0037", purposeIdNotFoundInClientAssertion: "0038", - clientAssertionPublicKeyNotFound: "0049", + delegationNotFound: "0039", organizationNotAllowed: "0040", cannotGetKeyWithClient: "0041", + clientAssertionPublicKeyNotFound: "0042", + eserviceDelegated: "0043", + delegatedEserviceNotExportable: "0044", }; export type ErrorCodes = keyof typeof errorCodes; @@ -417,3 +420,21 @@ export function cannotGetKeyWithClient( title: "Cannot get key with client", }); } + +export function delegationNotFound(delegationId: string): ApiError { + return new ApiError({ + detail: `Delegation ${delegationId} not found`, + code: "delegationNotFound", + title: "Delegation not found", + }); +} + +export function delegatedEserviceNotExportable( + delegatorId: string +): ApiError { + return new ApiError({ + detail: `Impossibile to export Eservice with a valid delegation for producer ${delegatorId}`, + code: "delegatedEserviceNotExportable", + title: "Delegated Eservice is not exportable", + }); +} diff --git a/packages/backend-for-frontend/src/routers/catalogRouter.ts b/packages/backend-for-frontend/src/routers/catalogRouter.ts index 02f4a12574..9a79390e1b 100644 --- a/packages/backend-for-frontend/src/routers/catalogRouter.ts +++ b/packages/backend-for-frontend/src/routers/catalogRouter.ts @@ -36,6 +36,7 @@ const catalogRouter = ( tenantProcessClient, agreementProcessClient, attributeProcessClient, + delegationProcessClient, }: PagoPAInteropBeClients, fileManager: FileManager ): ZodiosRouter => { @@ -48,6 +49,7 @@ const catalogRouter = ( tenantProcessClient, agreementProcessClient, attributeProcessClient, + delegationProcessClient, fileManager, config ); diff --git a/packages/backend-for-frontend/src/routers/delegationRouter.ts b/packages/backend-for-frontend/src/routers/delegationRouter.ts new file mode 100644 index 0000000000..06d3a10f86 --- /dev/null +++ b/packages/backend-for-frontend/src/routers/delegationRouter.ts @@ -0,0 +1,104 @@ +import { ZodiosRouter } from "@zodios/express"; +import { ZodiosEndpointDefinitions } from "@zodios/core"; +import { + ExpressContext, + ZodiosContext, + zodiosValidationErrorToApiProblem, +} from "pagopa-interop-commons"; +import { bffApi } from "pagopa-interop-api-clients"; +import { unsafeBrandId } from "pagopa-interop-models"; +import { PagoPAInteropBeClients } from "../clients/clientsProvider.js"; +import { fromBffAppContext } from "../utilities/context.js"; +import { delegationServiceBuilder } from "../services/delegationService.js"; +import { makeApiProblem } from "../model/errors.js"; +import { + getDelegationByIdErrorMapper, + getDelegationsErrorMapper, +} from "../utilities/errorMappers.js"; + +const delegationRouter = ( + ctx: ZodiosContext, + { + delegationProcessClient, + tenantProcessClient, + catalogProcessClient, + }: PagoPAInteropBeClients +): ZodiosRouter => { + const delegationRouter = ctx.router(bffApi.delegationsApi.api, { + validationErrorHandler: zodiosValidationErrorToApiProblem, + }); + + const delegationService = delegationServiceBuilder( + delegationProcessClient, + tenantProcessClient, + catalogProcessClient + ); + + delegationRouter + .get("/delegations", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + try { + const { + limit, + offset, + states, + kind, + delegateIds, + delegatorIds, + eserviceIds, + } = req.query; + + const delegations = await delegationService.getDelegations( + { + limit, + offset, + states, + delegatorIds, + delegateIds, + eserviceIds, + kind, + }, + ctx + ); + + return res + .status(200) + .send(bffApi.CompactDelegations.parse(delegations)); + } catch (error) { + const errorRes = makeApiProblem( + error, + getDelegationsErrorMapper, + ctx.logger, + ctx.correlationId, + `Error retrieving delegations` + ); + + return res.status(errorRes.status).send(errorRes); + } + }) + .get("/delegations/:delegationId", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + + try { + const delegation = await delegationService.getDelegationById( + unsafeBrandId(req.params.delegationId), + ctx + ); + + return res.status(200).send(bffApi.Delegation.parse(delegation)); + } catch (error) { + const errorRes = makeApiProblem( + error, + getDelegationByIdErrorMapper, + ctx.logger, + ctx.correlationId, + `Error retrieving delegation by id ${req.params.delegationId}` + ); + + return res.status(errorRes.status).send(errorRes); + } + }); + return delegationRouter; +}; + +export default delegationRouter; diff --git a/packages/backend-for-frontend/src/routers/producerDelegationRouter.ts b/packages/backend-for-frontend/src/routers/producerDelegationRouter.ts new file mode 100644 index 0000000000..97c8a86814 --- /dev/null +++ b/packages/backend-for-frontend/src/routers/producerDelegationRouter.ts @@ -0,0 +1,128 @@ +import { ZodiosRouter } from "@zodios/express"; +import { ZodiosEndpointDefinitions } from "@zodios/core"; +import { + ExpressContext, + ZodiosContext, + zodiosValidationErrorToApiProblem, +} from "pagopa-interop-commons"; +import { bffApi } from "pagopa-interop-api-clients"; +import { unsafeBrandId } from "pagopa-interop-models"; +import { PagoPAInteropBeClients } from "../clients/clientsProvider.js"; +import { fromBffAppContext } from "../utilities/context.js"; +import { emptyErrorMapper, makeApiProblem } from "../model/errors.js"; +import { delegationServiceBuilder } from "../services/delegationService.js"; + +const producerDelegationRouter = ( + ctx: ZodiosContext, + { + delegationProcessClient, + tenantProcessClient, + catalogProcessClient, + }: PagoPAInteropBeClients +): ZodiosRouter => { + const producerDelegationRouter = ctx.router( + bffApi.producerDelegationsApi.api, + { + validationErrorHandler: zodiosValidationErrorToApiProblem, + } + ); + const delegationService = delegationServiceBuilder( + delegationProcessClient, + tenantProcessClient, + catalogProcessClient + ); + + producerDelegationRouter + .post("/producer/delegations", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + try { + const delegationResource = await delegationService.createDelegation( + req.body, + ctx + ); + + return res + .status(200) + .send(bffApi.CreatedResource.parse(delegationResource)); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error creating delegation` + ); + + return res.status(errorRes.status).send(errorRes); + } + }) + .post("/producer/delegations/:delegationId/approve", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + try { + await delegationService.delegateApproveDelegation( + unsafeBrandId(req.params.delegationId), + ctx + ); + + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error approving delegation with id ${req.params.delegationId}` + ); + + return res.status(errorRes.status).send(errorRes); + } + }) + .post("/producer/delegations/:delegationId/reject", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + try { + await delegationService.delegateRejectDelegation( + unsafeBrandId(req.params.delegationId), + req.body, + ctx + ); + + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error rejecting delegation with id ${req.params.delegationId}` + ); + + return res.status(errorRes.status).send(errorRes); + } + }) + .delete("/producer/delegations/:delegationId", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + + try { + await delegationService.delegatorRevokeDelegation( + unsafeBrandId(req.params.delegationId), + ctx + ); + + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error revoke delegation with id ${req.params.delegationId}` + ); + + return res.status(errorRes.status).send(errorRes); + } + }); + + return producerDelegationRouter; +}; + +export default producerDelegationRouter; diff --git a/packages/backend-for-frontend/src/routers/tenantRouter.ts b/packages/backend-for-frontend/src/routers/tenantRouter.ts index a18175cbab..ef647d3b7f 100644 --- a/packages/backend-for-frontend/src/routers/tenantRouter.ts +++ b/packages/backend-for-frontend/src/routers/tenantRouter.ts @@ -405,7 +405,25 @@ const tenantRouter = ( return res.status(errorRes.status).send(errorRes); } } - ); + ) + .post("/tenants/delegatedProducer", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + const tenantId = ctx.authData.organizationId; + + try { + await tenantService.assignTenantDelegatedProducerFeature(tenantId, ctx); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error while assigning delegated producer feature to ${tenantId}` + ); + return res.status(errorRes.status).send(errorRes); + } + }); return tenantRouter; }; diff --git a/packages/backend-for-frontend/src/services/catalogService.ts b/packages/backend-for-frontend/src/services/catalogService.ts index 8364212dc9..468a8d66dd 100644 --- a/packages/backend-for-frontend/src/services/catalogService.ts +++ b/packages/backend-for-frontend/src/services/catalogService.ts @@ -32,6 +32,7 @@ import { AgreementProcessClient, AttributeProcessClient, CatalogProcessClient, + DelegationProcessClient, TenantProcessClient, } from "../clients/clientsProvider.js"; import { BffAppContext, Headers } from "../utilities/context.js"; @@ -57,7 +58,10 @@ import { } from "../model/types.js"; import { getAllAgreements, getLatestAgreement } from "./agreementService.js"; import { getAllBulkAttributes } from "./attributeService.js"; -import { assertRequesterIsProducer } from "./validators.js"; +import { + assertNotDelegatedEservice, + assertRequesterIsProducer, +} from "./validators.js"; export type CatalogService = ReturnType; @@ -189,6 +193,7 @@ export function catalogServiceBuilder( tenantProcessClient: TenantProcessClient, agreementProcessClient: AgreementProcessClient, attributeProcessClient: AttributeProcessClient, + delegationProcessClient: DelegationProcessClient, fileManager: FileManager, bffConfig: BffProcessConfig ) { @@ -977,6 +982,12 @@ export function catalogServiceBuilder( }); assertRequesterIsProducer(requesterId, eservice); + await assertNotDelegatedEservice( + delegationProcessClient, + headers, + requesterId, + eserviceId + ); const zipFolderName = `${eservice.id}_${descriptorId}`; const zipFile = await createDescriptorDocumentZipFile( diff --git a/packages/backend-for-frontend/src/services/delegationService.ts b/packages/backend-for-frontend/src/services/delegationService.ts new file mode 100644 index 0000000000..3d127d8873 --- /dev/null +++ b/packages/backend-for-frontend/src/services/delegationService.ts @@ -0,0 +1,287 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { + bffApi, + catalogApi, + delegationApi, + tenantApi, +} from "pagopa-interop-api-clients"; +import { getAllFromPaginated, WithLogger } from "pagopa-interop-commons"; +import { DelegationId, delegationKind } from "pagopa-interop-models"; +import { + DelegationsQueryParams, + toBffDelegationApiCompactDelegation, + toBffDelegationApiDelegation, + toDelegationKind, +} from "../api/delegationApiConverter.js"; +import { + CatalogProcessClient, + DelegationProcessClient, + TenantProcessClient, +} from "../clients/clientsProvider.js"; +import { delegationNotFound } from "../model/errors.js"; +import { BffAppContext, Headers } from "../utilities/context.js"; + +// eslint-disable-next-line max-params +async function enhanceDelegation< + T extends bffApi.Delegation | bffApi.CompactDelegation +>( + tenantClient: TenantProcessClient, + catalogClient: CatalogProcessClient, + delegation: delegationApi.Delegation, + headers: Headers, + toApiConverter: ( + delegation: delegationApi.Delegation, + delegator: tenantApi.Tenant, + delegate: tenantApi.Tenant, + eservice: catalogApi.EService, + producer: tenantApi.Tenant + ) => T, + cachedTenants: Map = new Map() +): Promise { + const delegator = await getTenantById( + tenantClient, + headers, + delegation.delegatorId, + cachedTenants + ); + + const delegate = await getTenantById( + tenantClient, + headers, + delegation.delegateId, + cachedTenants + ); + + const eservice: catalogApi.EService = await catalogClient.getEServiceById({ + params: { eServiceId: delegation.eserviceId }, + headers, + }); + + // NOTE: If the delegation kind is DELEGATED_PRODUCER, the producer is the same as the delegator tenant. + // In the case of DELEGATED_CONSUMER, the producer can be different. + const producer = + delegation.kind === toDelegationKind(delegationKind.delegatedProducer) + ? await getTenantById( + tenantClient, + headers, + eservice.producerId, + cachedTenants + ) + : delegator; + + return toApiConverter(delegation, delegator, delegate, eservice, producer); +} + +export async function getDelegation( + delegationClient: DelegationProcessClient, + headers: BffAppContext["headers"], + delegationId: DelegationId +): Promise { + const delegation: delegationApi.Delegation = + await delegationClient.delegation.getDelegation({ + params: { delegationId }, + headers, + }); + + if (!delegation) { + throw delegationNotFound(delegationId); + } + return delegation; +} + +export async function getTenantsFromDelegation( + tenantClient: TenantProcessClient, + delegations: delegationApi.Delegation[], + headers: BffAppContext["headers"] +): Promise> { + const tenantIds = delegations.reduce((acc, delegation) => { + acc.add(delegation.delegateId); + acc.add(delegation.delegatorId); + return acc; + }, new Set()); + + const tenants = await Promise.all( + Array.from(tenantIds).map((tenantId) => + tenantClient.tenant.getTenant({ + params: { id: tenantId }, + headers, + }) + ) + ); + + return tenants.reduce((acc, tenant) => { + acc.set(tenant.id, tenant); + return acc; + }, new Map()); +} + +export async function getTenantById( + tenantClient: TenantProcessClient, + headers: BffAppContext["headers"], + tenantId: string, + tenantMap: Map = new Map() +): Promise { + return ( + tenantMap.get(tenantId) ?? + (await tenantClient.tenant.getTenant({ + params: { id: tenantId }, + headers, + })) + ); +} + +export async function getAllDelegations( + delegationProcessClient: DelegationProcessClient, + headers: BffAppContext["headers"], + queryParams: DelegationsQueryParams +): Promise { + return await getAllFromPaginated( + async (offset, limit) => + await delegationProcessClient.delegation.getDelegations({ + headers, + queries: { + ...queryParams, + offset, + limit, + }, + }) + ); +} + +export function delegationServiceBuilder( + delegationClients: DelegationProcessClient, + tenantClient: TenantProcessClient, + catalogClient: CatalogProcessClient +) { + return { + async getDelegationById( + delegationId: DelegationId, + { headers, logger }: WithLogger + ): Promise { + logger.info(`Retrieving delegation with id ${delegationId}`); + + const delegation = await getDelegation( + delegationClients, + headers, + delegationId + ); + + return enhanceDelegation( + tenantClient, + catalogClient, + delegation, + headers, + toBffDelegationApiDelegation + ); + }, + async getDelegations( + { + limit, + offset, + states, + kind, + delegateIds, + delegatorIds, + eserviceIds, + }: { + limit: number; + offset: number; + states?: bffApi.DelegationState[]; + kind?: bffApi.DelegationKind; + delegateIds?: string[]; + delegatorIds?: string[]; + eserviceIds?: string[]; + }, + { headers, logger }: WithLogger + ): Promise { + logger.info("Retrieving all delegations"); + + const delegations = await delegationClients.delegation.getDelegations({ + queries: { + limit, + offset, + delegatorIds, + delegateIds, + delegationStates: states, + kind, + eserviceIds, + }, + headers, + }); + + const involvedTenants = await getTenantsFromDelegation( + tenantClient, + delegations.results, + headers + ); + + const delegationEnanched = await Promise.all( + delegations.results.map((delegation) => + enhanceDelegation( + tenantClient, + catalogClient, + delegation, + headers, + toBffDelegationApiCompactDelegation, + involvedTenants + ) + ) + ); + + return { + results: delegationEnanched, + pagination: { + limit, + offset, + totalCount: delegations.totalCount, + }, + }; + }, + async createDelegation( + createDelegationBody: bffApi.DelegationSeed, + { headers }: WithLogger + ): Promise { + const delegation = + await delegationClients.producer.createProducerDelegation( + createDelegationBody, + { headers } + ); + + return { id: delegation.id }; + }, + async delegatorRevokeDelegation( + delegationId: DelegationId, + { headers }: WithLogger + ): Promise { + return delegationClients.producer.revokeProducerDelegation(undefined, { + params: { + delegationId, + }, + headers, + }); + }, + async delegateRejectDelegation( + delegationId: DelegationId, + rejectBody: bffApi.RejectDelegationPayload, + { headers }: WithLogger + ): Promise { + return delegationClients.producer.rejectProducerDelegation(rejectBody, { + params: { + delegationId, + }, + headers, + }); + }, + async delegateApproveDelegation( + delegationId: DelegationId, + { headers }: WithLogger + ): Promise { + return delegationClients.producer.approveProducerDelegation(undefined, { + params: { + delegationId, + }, + headers, + }); + }, + }; +} diff --git a/packages/backend-for-frontend/src/services/tenantService.ts b/packages/backend-for-frontend/src/services/tenantService.ts index db07d459cd..a545f32ef9 100644 --- a/packages/backend-for-frontend/src/services/tenantService.ts +++ b/packages/backend-for-frontend/src/services/tenantService.ts @@ -418,6 +418,16 @@ export function tenantServiceBuilder( headers, }); }, + async assignTenantDelegatedProducerFeature( + tenantId: TenantId, + { logger, headers }: WithLogger + ): Promise { + logger.info(`Assigning delegated producer feature to tenant ${tenantId}`); + await tenantProcessClient.tenant.assignTenantDelegatedProducerFeature( + undefined, + { headers } + ); + }, }; } diff --git a/packages/backend-for-frontend/src/services/validators.ts b/packages/backend-for-frontend/src/services/validators.ts index ef9e3e8381..1e201fff48 100644 --- a/packages/backend-for-frontend/src/services/validators.ts +++ b/packages/backend-for-frontend/src/services/validators.ts @@ -4,17 +4,30 @@ import { catalogApi, tenantApi, } from "pagopa-interop-api-clients"; -import { TenantId } from "pagopa-interop-models"; +import { + delegationKind, + delegationState, + EServiceId, + TenantId, +} from "pagopa-interop-models"; import { descriptorAttributesFromApi } from "../api/catalogApiConverter.js"; +import { + toDelegationKind, + toDelegationState, +} from "../api/delegationApiConverter.js"; import { tenantAttributesFromApi } from "../api/tenantApiConverter.js"; +import { DelegationProcessClient } from "../clients/clientsProvider.js"; import { + delegatedEserviceNotExportable, invalidEServiceRequester, notValidDescriptor, } from "../model/errors.js"; import { - catalogApiDescriptorState, agreementApiState, + catalogApiDescriptorState, } from "../model/types.js"; +import { BffAppContext } from "../utilities/context.js"; +import { getAllDelegations } from "./delegationService.js"; export function isRequesterEserviceProducer( requesterId: string, @@ -32,6 +45,31 @@ export function assertRequesterIsProducer( } } +export async function assertNotDelegatedEservice( + delegationProcessClient: DelegationProcessClient, + headers: BffAppContext["headers"], + delegatorId: TenantId, + eserviceid: EServiceId +): Promise { + const delegations = await getAllDelegations( + delegationProcessClient, + headers, + { + kind: toDelegationKind(delegationKind.delegatedConsumer), + delegatorIds: [delegatorId], + eserviceIds: [eserviceid], + delegationStates: [ + toDelegationState(delegationState.active), + toDelegationState(delegationState.waitingForApproval), + ], + } + ); + + if (delegations.length > 0) { + throw delegatedEserviceNotExportable(delegatorId); + } +} + export function isAgreementUpgradable( eservice: catalogApi.EService, agreement: agreementApi.Agreement diff --git a/packages/backend-for-frontend/src/utilities/errorMappers.ts b/packages/backend-for-frontend/src/utilities/errorMappers.ts index 55bc8557ef..20174d19c3 100644 --- a/packages/backend-for-frontend/src/utilities/errorMappers.ts +++ b/packages/backend-for-frontend/src/utilities/errorMappers.ts @@ -199,3 +199,11 @@ export const exportEServiceDescriptorErrorMapper = ( .with("notValidDescriptor", () => HTTP_STATUS_BAD_REQUEST) .with("invalidEserviceRequester", () => HTTP_STATUS_FORBIDDEN) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +const delegationNotFoundErrorMapper = (error: ApiError): number => + match(error.code) + .with("delegationNotFound", () => HTTP_STATUS_NOT_FOUND) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const getDelegationByIdErrorMapper = delegationNotFoundErrorMapper; +export const getDelegationsErrorMapper = delegationNotFoundErrorMapper; diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index a9909a6a41..1cd5b3c363 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.4-b", + "@pagopa/interop-outbound-models": "1.0.6-b", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/commons-test/src/eventStoreTestUtils.ts b/packages/commons-test/src/eventStoreTestUtils.ts index b7658f9355..a7910910df 100644 --- a/packages/commons-test/src/eventStoreTestUtils.ts +++ b/packages/commons-test/src/eventStoreTestUtils.ts @@ -2,25 +2,28 @@ import { MessageType } from "@protobuf-ts/runtime"; import { Event } from "pagopa-interop-commons"; import { AgreementEvent, + agreementEventToBinaryData, AgreementId, AttributeEvent, + attributeEventToBinaryData, AttributeId, AuthorizationEvent, + authorizationEventToBinaryData, + catalogEventToBinaryData, ClientId, + DelegationEvent, + delegationEventToBinaryDataV2, + DelegationId, EServiceEvent, EServiceId, ProducerKeychainId, + protobufDecoder, PurposeEvent, + purposeEventToBinaryData, PurposeId, TenantEvent, - TenantId, - agreementEventToBinaryData, - attributeEventToBinaryData, - authorizationEventToBinaryData, - catalogEventToBinaryData, - protobufDecoder, - purposeEventToBinaryData, tenantEventToBinaryData, + TenantId, } from "pagopa-interop-models"; import { IDatabase } from "pg-promise"; import { match } from "ts-pattern"; @@ -31,7 +34,8 @@ type EventStoreSchema = | "catalog" | "tenant" | "purpose" - | '"authorization"'; + | '"authorization"' + | "delegation"; export type StoredEvent = { stream_id: string; @@ -60,6 +64,8 @@ export async function writeInEventstore( ? StoredEvent : T extends '"authorization"' ? StoredEvent + : T extends "delegation" + ? StoredEvent : never, schema: T, postgresDB: IDatabase @@ -90,6 +96,9 @@ export async function writeInEventstore( .with('"authorization"', () => authorizationEventToBinaryData(event.event as AuthorizationEvent) ) + .with("delegation", () => + delegationEventToBinaryDataV2(event.event as DelegationEvent) + ) .exhaustive(), ] ); @@ -108,6 +117,8 @@ export async function readLastEventByStreamId( ? PurposeId : T extends '"authorization"' ? ClientId | ProducerKeychainId + : T extends "delegation" + ? DelegationId : never, schema: T, postgresDB: IDatabase @@ -125,6 +136,8 @@ export async function readLastEventByStreamId( ? PurposeEvent : T extends '"authorization"' ? AuthorizationEvent + : T extends "delegation" + ? DelegationEvent : never > > { @@ -147,6 +160,8 @@ export async function readEventByStreamIdAndVersion( ? PurposeId : T extends '"authorization"' ? ClientId | ProducerKeychainId + : T extends "delegation" + ? DelegationId : never, version: number, schema: T, @@ -165,6 +180,8 @@ export async function readEventByStreamIdAndVersion( ? PurposeEvent : T extends '"authorization"' ? AuthorizationEvent + : T extends "delegation" + ? DelegationEvent : never > > { diff --git a/packages/commons-test/src/setupTestContainersVitest.ts b/packages/commons-test/src/setupTestContainersVitest.ts index b2f6333ced..db9b8990e8 100644 --- a/packages/commons-test/src/setupTestContainersVitest.ts +++ b/packages/commons-test/src/setupTestContainersVitest.ts @@ -168,6 +168,7 @@ export async function setupTestContainersVitest( await readModelRepository?.keys.deleteMany({}); await readModelRepository?.producerKeychains.deleteMany({}); await readModelRepository?.producerKeys.deleteMany({}); + await readModelRepository?.delegations.deleteMany({}); await postgresDB?.none( "TRUNCATE TABLE agreement.events RESTART IDENTITY" @@ -181,6 +182,9 @@ export async function setupTestContainersVitest( await postgresDB?.none( 'TRUNCATE TABLE "authorization".events RESTART IDENTITY' ); + await postgresDB?.none( + "TRUNCATE TABLE delegation.events RESTART IDENTITY" + ); if (s3OriginalBucket && fileManagerConfig && fileManager) { const files = await fileManager.listFiles( diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index ff2f045b04..dbb0143273 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -1,3 +1,4 @@ +import { fail } from "assert"; import { generateMock } from "@anatine/zod-mock"; import { Agreement, @@ -47,6 +48,14 @@ import { AgreementId, PurposeVersionId, ProducerKeychain, + Delegation, + delegationKind, + DelegationId, + DelegationContractDocument, + DelegationContractId, + DelegationState, + TenantFeatureCertifier, + TenantFeature, DescriptorState, GSIPKConsumerIdEServiceId, PlatformStatesAgreementEntry, @@ -61,6 +70,7 @@ import { } from "pagopa-interop-models"; import { AuthData } from "pagopa-interop-commons"; import { z } from "zod"; +import { match } from "ts-pattern"; export function expectPastTimestamp(timestamp: bigint): boolean { return ( @@ -80,6 +90,31 @@ export function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } +export const getTenantCertifierFeatures = ( + tenant: Tenant +): TenantFeatureCertifier[] => + tenant.features.reduce( + (acc: TenantFeatureCertifier[], feature: TenantFeature) => + match(feature.type) + .with("PersistentCertifier", () => [ + ...acc, + feature as TenantFeatureCertifier, + ]) + .with("DelegatedProducer", () => acc) + .exhaustive(), + [] + ); + +export const getTenantOneCertifierFeature = ( + tenant: Tenant +): TenantFeatureCertifier => { + const certifiedFeatures = getTenantCertifierFeatures(tenant); + if (certifiedFeatures.length === 0) { + fail("Expected certifier feature not found in Tenant"); + } + return certifiedFeatures[0]; +}; + export const getRandomAuthData = ( organizationId: TenantId = generateId() ): AuthData => ({ @@ -331,6 +366,56 @@ export const getMockAuthData = (organizationId?: TenantId): AuthData => ({ selfcareId: generateId(), }); +export const getMockDelegationProducer = ({ + id = generateId(), + delegatorId = generateId(), + delegateId = generateId(), + eserviceId = generateId(), + state = "WaitingForApproval", + activationContract = undefined, + revocationContract = undefined, +}: { + id?: DelegationId; + delegatorId?: TenantId; + delegateId?: TenantId; + eserviceId?: EServiceId; + state?: DelegationState; + activationContract?: DelegationContractDocument; + revocationContract?: DelegationContractDocument; +} = {}): Delegation => { + const creationTime = new Date(); + + return { + id, + delegatorId, + delegateId, + eserviceId, + createdAt: creationTime, + submittedAt: creationTime, + state, + activationContract, + revocationContract, + kind: delegationKind.delegatedProducer, + stamps: { + submission: { + who: delegatorId, + when: creationTime, + }, + }, + }; +}; + +export const getMockDelegationDocument = ( + id?: DelegationContractId +): DelegationContractDocument => ({ + id: id ?? generateId(), + name: "Test document", + prettyName: "Test document", + contentType: "json", + path: "path", + createdAt: new Date(), +}); + export const getMockTokenStatesClientPurposeEntry = ( tokenStateEntryPK?: TokenGenerationStatesClientKidPurposePK ): TokenGenerationStatesClientPurposeEntry => { diff --git a/packages/commons/src/config/kafkaTopicConfig.ts b/packages/commons/src/config/kafkaTopicConfig.ts index 4ada6a4df2..da4918ddb6 100644 --- a/packages/commons/src/config/kafkaTopicConfig.ts +++ b/packages/commons/src/config/kafkaTopicConfig.ts @@ -54,6 +54,15 @@ export const AuthorizationTopicConfig = z })); export type AuthorizationTopicConfig = z.infer; +export const DelegationTopicConfig = z + .object({ + DELEGATION_TOPIC: z.string(), + }) + .transform((c) => ({ + delegationTopic: c.DELEGATION_TOPIC, + })); +export type DelegationTopicConfig = z.infer; + export const KafkaTopicConfig = z.union([ CatalogTopicConfig, AgreementTopicConfig, @@ -61,5 +70,6 @@ export const KafkaTopicConfig = z.union([ AttributeTopicConfig, PurposeTopicConfig, AuthorizationTopicConfig, + DelegationTopicConfig, ]); export type KafkaTopicConfig = z.infer; diff --git a/packages/commons/src/pdf-generator/pdfGenerator.ts b/packages/commons/src/pdf-generator/pdfGenerator.ts index 69e83c958c..e3055740ce 100644 --- a/packages/commons/src/pdf-generator/pdfGenerator.ts +++ b/packages/commons/src/pdf-generator/pdfGenerator.ts @@ -73,7 +73,9 @@ export async function initPDFGenerator(): Promise { ...context, "paged-pdf-polyfill": ``, }); - await page.setContent(htmlCompiled, { waitUntil: "networkidle2" }); + await page.setContent(htmlCompiled, { + waitUntil: "networkidle2", + }); return await page.pdf({ format: "A4", diff --git a/packages/commons/src/repositories/ReadModelRepository.ts b/packages/commons/src/repositories/ReadModelRepository.ts index c1a3ca6333..c19aec0511 100644 --- a/packages/commons/src/repositories/ReadModelRepository.ts +++ b/packages/commons/src/repositories/ReadModelRepository.ts @@ -9,6 +9,7 @@ import { genericInternalError, ProducerKeychainReadModel, ProducerJWKKey, + Delegation, } from "pagopa-interop-models"; import { Collection, @@ -43,6 +44,7 @@ export type ClientKeyCollection = GenericCollection; export type ProducerKeychainCollection = GenericCollection; export type ProducerKeyCollection = GenericCollection; +export type DelegationCollection = GenericCollection; export type Collections = | EServiceCollection @@ -53,7 +55,8 @@ export type Collections = | ClientCollection | ClientKeyCollection | ProducerKeychainCollection - | ProducerKeyCollection; + | ProducerKeyCollection + | DelegationCollection; type BuildQueryKey = `${TPrefix}.${TKey & string}`; @@ -164,6 +167,8 @@ export class ReadModelRepository { public producerKeys: ProducerKeyCollection; + public delegations: DelegationCollection; + private client: MongoClient; private db: Db; @@ -196,6 +201,9 @@ export class ReadModelRepository { this.producerKeys = this.db.collection("producer_keys", { ignoreUndefined: true, }); + this.delegations = this.db.collection("delegations", { + ignoreUndefined: true, + }); } public static init(config: ReadModelDbConfig): ReadModelRepository { diff --git a/packages/compute-agreements-consumer/src/index.ts b/packages/compute-agreements-consumer/src/index.ts index c23c8fb4fc..2fa68f8742 100644 --- a/packages/compute-agreements-consumer/src/index.ts +++ b/packages/compute-agreements-consumer/src/index.ts @@ -94,7 +94,8 @@ async function processMessage({ "MaintenanceTenantDeleted", "TenantMailDeleted", "TenantMailAdded", - "MaintenanceTenantPromotedToCertifier" + "MaintenanceTenantPromotedToCertifier", + "TenantDelegatedProducerFeatureAdded" ), }, () => Promise.resolve() diff --git a/packages/delegation-process/.env b/packages/delegation-process/.env new file mode 100644 index 0000000000..4f8a954421 --- /dev/null +++ b/packages/delegation-process/.env @@ -0,0 +1,30 @@ +HOST=0.0.0.0 +PORT=3800 +LOG_LEVEL=info + +EVENTSTORE_DB_HOST=localhost +EVENTSTORE_DB_NAME=root +EVENTSTORE_DB_USERNAME=root +EVENTSTORE_DB_PASSWORD=root +EVENTSTORE_DB_PORT=6001 +EVENTSTORE_DB_SCHEMA=delegation +EVENTSTORE_DB_USE_SSL=false +AWS_REGION="eu-south-1" + +READMODEL_DB_HOST=localhost +READMODEL_DB_NAME=readmodel +READMODEL_DB_USERNAME=root +READMODEL_DB_PASSWORD=example +READMODEL_DB_PORT=27017 + +WELL_KNOWN_URLS="http://127.0.0.1:4500/jwks.json" +ACCEPTED_AUDIENCES="dev.interop.pagopa.it/ui,refactor.dev.interop.pagopa.it/ui,refactor.dev.interop.pagopa.it/internal,dev.interop.pagopa.it/m2m,refactor.dev.interop.pagopa.it/m2m" + +S3_BUCKET=interop-local-bucket +S3_CUSTOM_SERVER=true +S3_SERVER_HOST=http://localhost +S3_SERVER_PORT=9000 + +AWS_CONFIG_FILE=aws.config.local + +DELEGATION_DOCUMENT_PATH=delegation diff --git a/packages/delegation-process/Dockerfile b/packages/delegation-process/Dockerfile new file mode 100644 index 0000000000..926e4fab45 --- /dev/null +++ b/packages/delegation-process/Dockerfile @@ -0,0 +1,45 @@ +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ + +COPY ./packages/delegation-process/package.json /app/packages/delegation-process/package.json +COPY ./packages/commons/package.json /app/packages/commons/package.json +COPY ./packages/models/package.json /app/packages/models/package.json +COPY ./packages/api-clients/package.json /app/packages/api-clients/package.json + +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile + +COPY tsconfig.json /app/ +COPY turbo.json /app/ +COPY ./packages/delegation-process /app/packages/delegation-process +COPY ./packages/commons /app/packages/commons +COPY ./packages/models /app/packages/models +COPY ./packages/api-clients /app/packages/api-clients + +RUN pnpm build && \ + rm -rf /app/node_modules/.modules.yaml && \ + rm -rf /app/node_modules/.cache && \ + mkdir /out && \ + cp -a --parents -t /out \ + node_modules packages/delegation-process/node_modules \ + package*.json packages/delegation-process/package*.json \ + packages/commons \ + packages/models \ + packages/api-clients \ + packages/delegation-process/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final + +COPY --from=build /out /app + +WORKDIR /app/packages/delegation-process +EXPOSE 3000 + +CMD [ "node", "." ] diff --git a/packages/delegation-process/aws.config.local b/packages/delegation-process/aws.config.local new file mode 100644 index 0000000000..34826a60e2 --- /dev/null +++ b/packages/delegation-process/aws.config.local @@ -0,0 +1,4 @@ +[default] +aws_access_key_id=testawskey +aws_secret_access_key=testawssecret +region=eu-south-1 diff --git a/packages/delegation-process/package.json b/packages/delegation-process/package.json new file mode 100644 index 0000000000..37e1b03a01 --- /dev/null +++ b/packages/delegation-process/package.json @@ -0,0 +1,52 @@ +{ + "name": "pagopa-interop-delegation-process", + "version": "1.0.0", + "description": "PagoPA Interoperability service for delegations management", + "main": "dist", + "type": "module", + "scripts": { + "test": "vitest", + "test:it": "vitest integration", + "lint": "eslint . --ext .ts,.tsx", + "lint:autofix": "eslint . --ext .ts,.tsx --fix", + "format:check": "prettier --check src", + "format:write": "prettier --write src", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", + "@pagopa/eslint-config": "3.0.0", + "@protobuf-ts/runtime": "2.9.4", + "@types/express": "4.17.21", + "@types/node": "20.14.6", + "date-fns": "3.6.0", + "pagopa-interop-commons-test": "workspace:*", + "pg-promise": "11.8.0", + "prettier": "2.8.8", + "testcontainers": "10.9.0", + "tsx": "4.19.1", + "typescript": "5.4.5", + "vitest": "1.6.0", + "puppeteer": "22.11.2", + "pdf-lib": "1.17.1" + }, + "dependencies": { + "@zodios/core": "10.9.6", + "@zodios/express": "10.6.1", + "dotenv-flow": "4.1.0", + "express": "4.19.2", + "mongodb": "6.7.0", + "openapi-zod-client": "1.18.1", + "pagopa-interop-api-clients": "workspace:*", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "ts-pattern": "5.2.0", + "zod": "3.23.8" + } +} diff --git a/packages/delegation-process/src/app.ts b/packages/delegation-process/src/app.ts new file mode 100644 index 0000000000..007a7b0706 --- /dev/null +++ b/packages/delegation-process/src/app.ts @@ -0,0 +1,31 @@ +import { + authenticationMiddleware, + contextMiddleware, + loggerMiddleware, + zodiosCtx, + buildJwksClients, +} from "pagopa-interop-commons"; + +import healthRouter from "./routers/HealthRouter.js"; +import delegationProducerRouter from "./routers/DelegationProducerRouter.js"; +import delegationRouter from "./routers/DelegationRouter.js"; +import { config } from "./config/config.js"; + +const serviceName = "delgation-process"; + +const app = zodiosCtx.app(); + +const jwksClients = buildJwksClients(config); + +// Disable the "X-Powered-By: Express" HTTP header for security reasons. +// See https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#recommendation_16 +app.disable("x-powered-by"); + +app.use(healthRouter); +app.use(contextMiddleware(serviceName)); +app.use(authenticationMiddleware(config, jwksClients)); +app.use(loggerMiddleware(serviceName)); +app.use(delegationRouter(zodiosCtx)); +app.use(delegationProducerRouter(zodiosCtx)); + +export default app; diff --git a/packages/delegation-process/src/config/config.ts b/packages/delegation-process/src/config/config.ts new file mode 100644 index 0000000000..1991caba74 --- /dev/null +++ b/packages/delegation-process/src/config/config.ts @@ -0,0 +1,27 @@ +import { + CommonHTTPServiceConfig, + ReadModelDbConfig, + EventStoreConfig, + S3Config, + FileManagerConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +const DelegationDocumentConfig = z + .object({ + DELEGATION_DOCUMENT_PATH: z.string(), + }) + .transform((c) => ({ + delegationDocumentPath: c.DELEGATION_DOCUMENT_PATH, + })); + +const DelegationProcessConfig = CommonHTTPServiceConfig.and(ReadModelDbConfig) + .and(EventStoreConfig) + .and(S3Config) + .and(FileManagerConfig) + .and(DelegationDocumentConfig); + +export type DelegationProcessConfig = z.infer; +export const config: DelegationProcessConfig = DelegationProcessConfig.parse( + process.env +); diff --git a/packages/delegation-process/src/index.ts b/packages/delegation-process/src/index.ts new file mode 100644 index 0000000000..160d2c4711 --- /dev/null +++ b/packages/delegation-process/src/index.ts @@ -0,0 +1,7 @@ +import { genericLogger } from "pagopa-interop-commons"; +import { config } from "./config/config.js"; +import app from "./app.js"; + +app.listen(config.port, config.host, () => { + genericLogger.info(`listening on ${config.host}:${config.port}`); +}); diff --git a/packages/delegation-process/src/model/domain/apiConverter.ts b/packages/delegation-process/src/model/domain/apiConverter.ts new file mode 100644 index 0000000000..fd6b0f713a --- /dev/null +++ b/packages/delegation-process/src/model/domain/apiConverter.ts @@ -0,0 +1,145 @@ +import { delegationApi } from "pagopa-interop-api-clients"; +import { + Delegation, + DelegationContractDocument, + DelegationKind, + DelegationStamp, + DelegationStamps, + DelegationState, + delegationKind, + delegationState, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; + +export const delegationToApiDelegation = ( + delegation: Delegation +): delegationApi.Delegation => ({ + id: delegation.id, + delegatorId: delegation.delegatorId, + delegateId: delegation.delegateId, + eserviceId: delegation.eserviceId, + createdAt: delegation.createdAt.toJSON(), + submittedAt: delegation.submittedAt.toJSON(), + approvedAt: delegation.approvedAt + ? delegation.approvedAt.toJSON() + : undefined, + rejectedAt: delegation.rejectedAt + ? delegation.rejectedAt.toJSON() + : undefined, + rejectionReason: delegation.rejectionReason || undefined, + revokedAt: delegation.revokedAt ? delegation.revokedAt.toJSON() : undefined, + state: delegationStateToApiDelegationState(delegation.state), + kind: delegationKindToApiDelegationKind(delegation.kind), + activationContract: delegation.activationContract + ? delegationContractToApiDelegationContract(delegation.activationContract) + : undefined, + revocationContract: delegation.revocationContract + ? delegationContractToApiDelegationContract(delegation.revocationContract) + : undefined, + stamps: delegationStampsToApiDelegationStamps(delegation.stamps), +}); + +export const delegationStateToApiDelegationState = ( + state: DelegationState +): delegationApi.DelegationState => + match(state) + .with( + delegationState.active, + () => delegationApi.DelegationState.Values.ACTIVE + ) + .with( + delegationState.rejected, + () => delegationApi.DelegationState.Values.REJECTED + ) + .with( + delegationState.revoked, + () => delegationApi.DelegationState.Values.REVOKED + ) + .with( + delegationState.waitingForApproval, + () => delegationApi.DelegationState.Values.WAITING_FOR_APPROVAL + ) + .exhaustive(); + +export const delegationKindToApiDelegationKind = ( + kind: DelegationKind +): delegationApi.DelegationKind => + match(kind) + .with( + delegationKind.delegatedConsumer, + () => delegationApi.DelegationKind.Values.DELEGATED_CONSUMER + ) + .with( + delegationKind.delegatedProducer, + () => delegationApi.DelegationKind.Values.DELEGATED_PRODUCER + ) + .exhaustive(); + +export const apiDelegationKindToDelegationKind = ( + kind: delegationApi.DelegationKind +): DelegationKind => + match(kind) + .with( + delegationApi.DelegationKind.Values.DELEGATED_CONSUMER, + () => delegationKind.delegatedConsumer + ) + .with( + delegationApi.DelegationKind.Values.DELEGATED_PRODUCER, + () => delegationKind.delegatedProducer + ) + .exhaustive(); + +export const delegationContractToApiDelegationContract = ( + contract: DelegationContractDocument +): delegationApi.DelegationContractDocument => ({ + id: contract.id, + name: contract.name, + prettyName: contract.prettyName, + contentType: contract.contentType, + path: contract.path, + createdAt: contract.createdAt.toJSON(), +}); + +export const delegationStampsToApiDelegationStamps = ( + stamps: DelegationStamps +): delegationApi.DelegationStamps => ({ + submission: delegationStampToApiDelegationStamp(stamps.submission), + activation: stamps.activation + ? delegationStampToApiDelegationStamp(stamps.activation) + : undefined, + rejection: stamps.rejection + ? delegationStampToApiDelegationStamp(stamps.rejection) + : undefined, + revocation: stamps.revocation + ? delegationStampToApiDelegationStamp(stamps.revocation) + : undefined, +}); + +export const delegationStampToApiDelegationStamp = ( + stamp: DelegationStamp +): delegationApi.DelegationStamp => ({ + who: stamp.who, + when: stamp.when.toJSON(), +}); + +export const apiDelegationStateToDelegationState = ( + state: delegationApi.DelegationState +): DelegationState => + match(state) + .with( + delegationApi.DelegationState.Values.ACTIVE, + () => delegationState.active + ) + .with( + delegationApi.DelegationState.Values.REJECTED, + () => delegationState.rejected + ) + .with( + delegationApi.DelegationState.Values.REVOKED, + () => delegationState.revoked + ) + .with( + delegationApi.DelegationState.Values.WAITING_FOR_APPROVAL, + () => delegationState.waitingForApproval + ) + .exhaustive(); diff --git a/packages/delegation-process/src/model/domain/errors.ts b/packages/delegation-process/src/model/domain/errors.ts new file mode 100644 index 0000000000..b5b0064bb6 --- /dev/null +++ b/packages/delegation-process/src/model/domain/errors.ts @@ -0,0 +1,144 @@ +import { + ApiError, + Delegation, + EServiceId, + makeApiProblemBuilder, + TenantId, + DelegationState, +} from "pagopa-interop-models"; + +export const errorCodes = { + delegationNotFound: "0001", + eserviceNotFound: "0002", + delegationAlreadyExists: "0003", + tenantNotFound: "0004", + invalidDelegatorAndDelegateIds: "0005", + invalidExternalOriginId: "0006", + tenantNotAllowedToDelegation: "0007", + delegationNotRevokable: "0008", + operationNotAllowOnDelegation: "0009", + operationRestrictedToDelegate: "0010", + incorrectState: "0011", + differentEserviceProducer: "0012", +}; + +export type ErrorCodes = keyof typeof errorCodes; + +export const makeApiProblem = makeApiProblemBuilder(errorCodes); + +export function delegationNotFound(delegationId: string): ApiError { + return new ApiError({ + detail: `Delegation ${delegationId} not found`, + code: "delegationNotFound", + title: "Delegation not found", + }); +} + +export function delegationAlreadyExists( + delegatorId: string, + eserviceId: string, + delegationKind: string +): ApiError { + return new ApiError({ + detail: `Delegation type ${delegationKind} already exists for EService ${eserviceId} by delegator ${delegatorId}`, + code: "delegationAlreadyExists", + title: "Delegation already exists", + }); +} + +export function eserviceNotFound(eserviceId: EServiceId): ApiError { + return new ApiError({ + detail: `EService ${eserviceId} not found`, + code: "eserviceNotFound", + title: "EService not found", + }); +} + +export function tenantNotFound(tenantId: TenantId): ApiError { + return new ApiError({ + detail: `Tenant ${tenantId} not found`, + code: "tenantNotFound", + title: "Tenant not found", + }); +} + +export function delegatorAndDelegateSameIdError(): ApiError { + return new ApiError({ + detail: `Error occurs because Delegator and Delegate have the same Id`, + code: "invalidDelegatorAndDelegateIds", + title: "Invalid Delegator and Delegate", + }); +} + +export function invalidExternalOriginError( + externalOrigin?: string +): ApiError { + return new ApiError({ + detail: `Delegator is not an IPA`, + code: "invalidExternalOriginId", + title: `Invalid External origin ${externalOrigin}`, + }); +} + +export function tenantNotAllowedToDelegation( + tenantId: string +): ApiError { + return new ApiError({ + detail: `Tenant ${tenantId} not allowed to delegation`, + code: "tenantNotAllowedToDelegation", + title: "Tenant not allowed to delegation", + }); +} + +export function delegationNotRevokable( + delegation: Delegation +): ApiError { + return new ApiError({ + detail: `Delegation ${delegation.id} is not revokable. State: ${delegation.state}`, + code: "delegationNotRevokable", + title: "Delegation not revokable", + }); +} + +export function delegatorNotAllowToRevoke( + delegation: Delegation +): ApiError { + return new ApiError({ + detail: `Requester ${delegation.id} is not delegator for the current delegation with id ${delegation.id}`, + code: "operationNotAllowOnDelegation", + title: "Requester and delegator are differents", + }); +} + +export function operationRestrictedToDelegate( + tenantId: string, + delegationId: string +): ApiError { + return new ApiError({ + detail: `Tenant ${tenantId} is not a delegate for delegation ${delegationId}`, + code: "operationRestrictedToDelegate", + title: "Operation restricted to delegate", + }); +} + +export function incorrectState( + delegationId: string, + actualState: DelegationState, + expectedState: DelegationState +): ApiError { + return new ApiError({ + detail: `Delegation ${delegationId} is in state ${actualState} but expected ${expectedState}`, + code: "incorrectState", + title: "Incorrect state", + }); +} + +export function differentEServiceProducer( + requesterId: string +): ApiError { + return new ApiError({ + detail: `Eservice producer if different from requester with id ${requesterId}`, + code: "differentEserviceProducer", + title: "Operation not allowed", + }); +} diff --git a/packages/delegation-process/src/model/domain/models.ts b/packages/delegation-process/src/model/domain/models.ts new file mode 100644 index 0000000000..216dba9c8f --- /dev/null +++ b/packages/delegation-process/src/model/domain/models.ts @@ -0,0 +1,14 @@ +import { + DelegationKind, + DelegationState, + EServiceId, + TenantId, +} from "pagopa-interop-models"; + +export type GetDelegationsFilters = { + eserviceId?: EServiceId; + delegatorId?: TenantId; + delegateId?: TenantId; + delegationKind?: DelegationKind; + states?: DelegationState[]; +}; diff --git a/packages/delegation-process/src/model/domain/toEvent.ts b/packages/delegation-process/src/model/domain/toEvent.ts new file mode 100644 index 0000000000..8241c4daf9 --- /dev/null +++ b/packages/delegation-process/src/model/domain/toEvent.ts @@ -0,0 +1,81 @@ +import { CreateEvent } from "pagopa-interop-commons"; +import { + CorrelationId, + Delegation, + DelegationEventV2, + WithMetadata, + toDelegationV2, +} from "pagopa-interop-models"; + +export function toCreateEventProducerDelegationSubmitted( + delegation: Delegation, + correlationId: CorrelationId +): CreateEvent { + return { + streamId: delegation.id, + version: 0, + event: { + type: "ProducerDelegationSubmitted", + event_version: 2, + data: { + delegation: toDelegationV2(delegation), + }, + }, + correlationId, + }; +} + +export function toCreateEventProducerDelegationRevoked( + delegation: Delegation, + version: number, + correlationId: CorrelationId +): CreateEvent { + return { + streamId: delegation.id, + version, + event: { + type: "ProducerDelegationRevoked", + event_version: 2, + data: { + delegation: toDelegationV2(delegation), + }, + }, + correlationId, + }; +} + +export function toCreateEventProducerDelegationApproved( + delegation: WithMetadata, + correlationId: CorrelationId +): CreateEvent { + return { + streamId: delegation.data.id, + version: delegation.metadata.version, + event: { + type: "ProducerDelegationApproved", + event_version: 2, + data: { + delegation: toDelegationV2(delegation.data), + }, + }, + correlationId, + }; +} + +export function toCreateEventProducerDelegationRejected( + delegation: WithMetadata, + correlationId: CorrelationId +): CreateEvent { + return { + streamId: delegation.data.id, + version: delegation.metadata.version, + event: { + type: "ProducerDelegationRejected", + event_version: 2, + data: { + delegation: toDelegationV2(delegation.data), + }, + }, + correlationId, + }; +} diff --git a/packages/delegation-process/src/resources/assets/font/Montserrat-Bold.ttf b/packages/delegation-process/src/resources/assets/font/Montserrat-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..55e0b1a5533e4d1474be0ed37b5c7935dafef6c0 GIT binary patch literal 198612 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%0A#iYtkm8@D`eW89r_55?_^ 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 < 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 VGt1clU7|DjWg`+aAKPR5{~Ih^NSy!x literal 0 HcmV?d00001 diff --git a/packages/delegation-process/src/resources/assets/font/Montserrat-Italic.ttf b/packages/delegation-process/src/resources/assets/font/Montserrat-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..be99e1c22cf75a0ca043d1091295795ba67a989b GIT binary patch literal 202912 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 &CB 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

l8tD<~9T^-M9vKxG6WJ-U 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 literal 0 HcmV?d00001 diff --git a/packages/delegation-process/src/resources/assets/font/Montserrat-Medium.ttf b/packages/delegation-process/src/resources/assets/font/Montserrat-Medium.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0f0fd1d55b3e9e32ac0f1b1ecdbc59612ca88bf5 GIT binary patch literal 198616 zcmce<34B!5^#}g$d&x|I014UmOeT}bI$0*OWZ(CF-y_m?hmB-;eK6!=+dGsTWRN{ZPPXn+da>;e@ zd@cN*I6b>%`)e;Ac^m%w7LJQ~W8H>vSJ}UAJj)SVE61%kG3(ksr$5L&4!>ssef)aY z?Bur7-@k?WTjw@x-m?DZhkwG6O#vL&l{&X+a&B686Y!t?CD7|Ro=fI9-pC*3 z1o(~H9B2qM83GN-KJQPsG<^P~@EOyfA1Ua#236(D z)}XTrZ8$?w68^A)f~LIaI#z=|snBL=&|M0eqd_|rG@YRf@%$O3HND=4{ESoEdsCv; z>plfB9+u_siBa&+w7AAXl3{#(&~AkHz}|G+Qf>toN^LI0=s=STf&UxA;7=g@A>{sv z-Xd{VgN`)ilPU3AOUT`#4>=`X!5{8PcW(eIe6Q;tJ@AVpoA4fOD+=vjAI}|!pFB4M z|9T!;3*?N{3vxn3f^|BhBPSI9Vl-QG?6%w_qtPb+WrpPM*kkR^Syeb_-+$f5^Bujt z9iP0}*xA|m3V+zunb({eo7z^`Ki=jnEONFv;UBR7tc7#@fAcSJ29B#ru<*9rV&0k) z8m3Q5GA69xLxN-BSGyxjrz78M-9PShPV8@OJD63LJdsy0S)YZh@@4fR#YeI6BIf_%yT~;8Ts+t^H23$Q(b*c z&r|&2-g~E~@9pg=-`3o+6)g=%$M;5In@}422QcsYr%#&&ezbOkN!s zoj#dT$AQlm1GUcQSxQBJrxpMi2U;gD#~+AMU`b1uB?TEVxIIs1#JiSGKs4J*Hg=02 zVibdDg&zJ38b`pwzdVvS0Bn!y*Sm3@g#4&F4>sUn4Ynk_=umN=4qQ$?iwn$dA&E(Yt2y_J5oHcM}0@E?DnI5p2 zS-=B0$UdtQlFW|KP@K$w24*^fW>QmgmBpMk;~1QnsNX9-KjCMNZ;g##w~-tYw@u$v zLyE|LaR>GYWFzfV$}VV4ua94Va1TND9NCLherY?QEn~ZMo%gwG#PCG;n_K2n{z=r6P_fw9Oz)7c1d>*|7 zZ3ueJ`GVyw=eTWVNYwQL46uPX$g{rW=1t-&&W|R@P2&3%r0qvnLcVmkSbP!fgf!QS z(>TJE2jx6PIhz_N3Leb#$xyxl6^696v^{EZhLD*Fausm9hg$y(`0Q!^FwC86fRmn^ z2;z{nEs7uvmEsEMltmDR zvWSf1`8@R|hDI-nHVmZ$1yRgN^JYC}=qR@yWcF397=sITAp$x;Vr;{P?C|oGD<;-s zXLeyvj-({j#u{(o?{lxeY8xF<7$Y|wBWy=cPRRX&G1AJa^V+Xu6rRo#;V@4GVXmjn zkFk(qui(u&#gKVhcsb=JKWIG&v*W~pw$}ZV4#(vF*8K6hl$5&h{DN^zy2t(Ou02gn zdt7#Tg6K=G8_Ulht4o%DqTDz)g;RUJmg${F%EhSJE1IZ5p90HZ97ZLyP{S8izdNX> zdws&CK`~Ebe8w~==4lKa*PxiEF*I9)V*F)jxCVV)L8CP2pA|GlLP2Ue*E0?vwHF=5 zYEX>2j5Z5;D2<`e#U*i&YpY{O=d4^hd*Q+iE$vH(b@B#Sg{4}u{rw7;uk2}!DT~=< zYt6XaDC3%HMWSg{-16`sOL$4O-e+ah;I=A+`%-GhAT6p*!9mX(t55dm&o?~A97hai z1%@u}J3|-uouSZoFMN|1p-fLk4T^IULvu7J=1&Yw*Pu9mGBndGYLWCGk0Z#X;lcp& z1zWD2E=`R}!i9R*>GZg8#Zkd^tFYL&{@Ym^RbmsC2I|}PPeKa1pE_CXj)si##Kgz| zgMZMfOv567JL=4ca^!|25h6Pr{_Hz69JDOS5)~1(RG+XYiejjJHO|$4B1+>GIb%HqI7g zith6{?F|j>IT~(3To~jGN}U7#LQ5bzqc6-z|MMJjc<#51qxi|huI`S8#`d0q*~Z2V z`MV&hV;L1<;d&vT#@__auJvB#s$pP4=(g4zuo=^0lUW>&ziBKjtxR4U>##>Q*fJ}{ zM;4jOCE@voKvO|lv?(*BFt7cClRBwS?^kU5!%+@fISM8CxN7w#B zG%iwNzD0D4;`!_jsz$Iu9F>QuwLQeFtp;syF=}Ec%)cNB*cGGYz0zhg6taCSEi8-_ zzUcjoLykn-?m?@o!3-k>RsfYSDzNTV^M5GASbWj9l|mF6X{;~0HWZm}OEo3$tSD+s z%xvf%Xt3mM8!XzMZpfI(ENC<`D1V1vwz=78iO!BoOpgkVO099)2OROSt+7$DIdP`+ z$dJg?YKLvW0n$TeuN?`n>DJtfc_QaM{(Jm=@Xywl&mw6vs5=bJ_Jm9f#A z*NE%MH-`??uZ#^Z`zT-MlQmpfIRt@PgyZ;cMR6UxXKT?&HQs;7}wA92D8hWPlTCW?o+sF2TQ(o5`-DNA9s7W^0 zj1?@&bJZlTN{p^DWQ0RBZ))1>g1|s<4~DOSQP!g$!jdP%82Inge8p*gvZu#=4{B7R z0PR0TZN_^36B(tJ#8A_DN(+@4;HJ~=z($)+U|ks&dVeB7)TjNM~*I; z)wMIZ@rHXRriMqRT;K7#_gk=5-g?ElbytuZ#7*D27bL||e~e1{0<-y1lyos__KKRk zqKRJ7Yz>N;8*43GLJN72UqLwyE&2eXjn$x-jWIM!Mui9g^9e-tx&<3b2n8YcmHhQMI}k_#Wc+1T{Dc@cA5Z^~+}uQVihm{~UA&=I+(b&mXHg#)fya%& z16I?nz%X2+0}n?aSrsP!X{|e792egRCH}=wSREnvi>r#oe?t%0Q?zFvfF!VX4)=y8 zYEbMIqYc-fXp0PuW+=?vAdP}T!sey5SPhE)z&K=SP_$--rfX2tAVZTO5hReYbNm+8 z_supE2F1w`Vm5@Nk++tKCf+8d`IA$Xe66{s()~*_tSEhGVX!<{g^4qu{XW4e&>*KThS2tY-Dcv4b-|B!loT zT}pgQ92O#P61Pv?SW6195J{fd1OGtgIu8AP3g$4Hxo(!HPdbybDb)-b7;iYxH*la~ zVs>b7y^*|24nje*=B8ce@YfzQ@hz7+GXKk9iqKOLnCf&thw4)65DxdnqQ<^7S z3ZJLwz7OLwra`fuz|e6Gs>+wGLD5$jZ8$?w5}KJY6g1^U*RdKDy@JtZX;8F9hUREc z^c9AtGZb=zKPj!@uFb79YO%4!=!?|2TRqOAFIpAdt!DUVT3i`l*yRBR0@n}bzKr_Z zN)C3GUATE8{m%r}=L=TJR)8Ua{0j1kxS6=b`^Y^P50MAOsBhi;%&*2)^M||MZJY1< zLucn7$iX}A>))_W+yVNaoUuIuaZ%o}1{_utzPGA-qGX@6CyKaHvsd{h-K!*=74ncx z;3XF7g=|e4CNECqO=w)7sw-7_GkR$3x(er@%{R9#t>~JO@+&i9GRN)1L-kd^_@%Wm zb$q(|nv{f$+x)Bp`Ht4KnDpjsTc15TvNa;CA}80`9iEe9uShaBC#8YZR0`S~$feM~ za&XKSqh_zDNkZfKO$vu3y^urw%j*+~3i<=IYQowamC!;DJ{;wvKCwyjgiGPG3H?Cg zGp0eo4ga3Lj(#qd)$4n zVA^ajc3E7bCNfE$+kQi2R7XT;Z@XARqPn|*2i+e99tO{vv$xY74x8Bq+ika3@1Dq8 zv@4s|FqE6W-kfSWu)%NC&oZ#DyJhc~P1=#1C@h<(G+MHY3k&^*mY);vMT?;AQh!LH z9<^v+7FN;dAB*;7B~;#*{etbwN+>^r`6T0u*26d~+Lx73bze3R`i}dukTrabeOab~ z+hAvQ&sS*yw`RXm5dyYLz5@|h+MC@5dAc4(j=-NsC;mk?iHFFh75x3~krh2XxVpq~ z?^(Fx|GFo-PTmv!OblXsqW=;@a2#m+bR4o-WcwA%fbRVNBm-I_XF#XLV3q+vvwR|E zKwjE5|H_+0iHI(oMLlWc%+}CiWMa~XYfy{|42{&FXc-KRX6Qm8>R&-Q4K2n&MjNX^ zFb2KQ%D@L2n(1m!9)|~(J*5hOt1KeM|gM1q-BY?YffAyK-nFMD{ZQ72D zWOQzww7+_?$Nj9dzj_C`kLn7Jq=6SDK7*~yEZl)}y|Rl~&d=ZrTuim4mIowOZ_|{_Y-y;wesjR zvZF;a*^}XH99Fz{(A^R}WPh13FWMY7Ye-m#*<-WB%iiNAVaO-@EA}7v-)4CARl{x9 zT)GXpxVBBWDyYX`JujJMd1h0XFFogR*=kE_ zzRTD0c)EJ-hqjkG7r#hC$%lBI4`5r;uvZolos+3XiuRP7AOi9r$u>&f^BJ0PD0x~P#u&} zSe^Fg5HG-caNkQQ_`qL`Hawg;d(|xytGfm^j*mvi@AJFs5V=pBK%*izyDQOoz(=sY z_j4-o0P~SSwEM-V*(+-DiY9tRvo$D2Z`NA4gckD94+Z5kv}nJKHdcdTL}qA~j0!64=Cy>hGUePtZH)W<-HrGt7iHpCsXU`oI7=Op>53X@!HfLn4n>)0u ztLd&oAJ0!u&u)5)CNby{v<+%W+nJvXctaC4D9X=h!!;<{0YjrDw2+6>F%pV1<65el z#qGsvc%U^h9$6X`ZHb}j8Wgp~&}0~P$o}D9J3v><=n6tpbt5)w7;OETv&b7*eY1-h zP#` zX&H~pS@?o1@L>>{A)9wW_APBY(G8{$Jh7wMrB?Uz$pgMMHJSZ!$tjylX19uG3;MEB zhFX63Ky6O3!``+kC}?ypkGzP%FaO+NLB0L>G9G9R)a6e)cAHE5fH4ydR`zKa_4Etyt#R6}J=l*f7o8!fj6{f&a2mC$%TRA~(&DEGhA%jk#;=?e9yDi2|^f_|o=n%MUy8#e?fsTHCKL%uX2 zog*PzDrZZ|X3LwmS66Qr?;c&M`yXptW@ei;2mh@r+TP#4y|8jeTicbDI(}Zf@2abA zX|{AZ^Exb+jyz`<6aopC3N1&1o?|>(4P#jwJFOz!NGWf-rndH)w%Uzxb&(Bex$Rk5 z?YU_Uk#%t!b=6n4v|L%0=gc&lfOB4_#bmZP3c&6dckJ&2%<~1*!eaE4gvO&JGOb>q zmXr!}bR^Xna)4^zm+(2M;g0sp+B^o22(-->s^K_q8a6LdZJ5nr2Bi~w(~j!u9ZePM z%1YK1QMKk+TeGrSkL#9>>ME~nYuiy-h-xnC)GD_5maDEJE_Wnt_Z;8NzsV(V*__Qo z$50rO?!e&ozq-+{5d{rcfE1o2)oiM$*j$%CT;^}m`xebLrPaj*#J6Y8+|yHg{eYt> zIjugsxgU-!Mv{9X{Tvfjl@pGXCg+Oi!l zBDt&N!BTlV&Ix+Z*pETGdff@K6ZFN#>0y3e#$giuCHGs37xJJ>3D@ai!9&q@4^$_i zU-;a>&%@e^^5(bm4?!Eu?()IzF12>*!uCsDnACJ#TT`{8v1w}sAz~5vhMA-{S#z4x zvwQP0n__HI>dyhWiDq!9%mKFFC1k_X$fJr=r1=czu;x**}+QLIX^R@ z$*{t22@85&jcsYbQj;@1IGUYQ^rE*jQcu31JpmtpDh<0C$09%8Z*;KP%MPb-s54-U zATiy$YpS+4HEshRAkT|)N$E{F*5)*vrkY}NjqCWZDvZU|Radp?1N7D5))wjm_BKnL zp_ux>!ifb3ImNHi>3qjA!b-nmjDCsw#Hq(IpM|plWFLP5^2Lx#D&iqwv?e|?&>5E( z9J4YhBsHD?HEHL|nuk^f)cG&ZwA7@FR`PsH3yZu?h=5_-Du{tObj-~1O2UC<@L-*J z(R8PYfOG~T6?F&GET(k@!@Wy=2X4B(X-7@Xj;6}Fii){BTYF|^yUo^-nb`p+$?M}y zz5R1DYrY}cZ`$|wvMr5Gn@b&=db&5|b*494K~ZV=?{qpMC>8el72505G0eU_(NkX0 zlPW5phcFJJic+g*=rIk7R?pD0Fx~oqB|G^$sb+B2hvQH%?_Z)Dn<>Qff78!T$(7>$ zUsFei%1HTS$%eYR4Mp~K9UbfKZK(~}Id!S2bvfA$so-t6xM_!Z6ArLZ8Cjy9Sw>E6UTesBDhw6l3xWgG~QYW92n(SxlaLoDg4KBY8RAVcO2;f=K&8Pd z19-G#MqeY*7r~9Eql%u*dZuT%`E&@!@*+INvN-lsHG68|fdwb`I(_OS?S*4u0)7&T zKLew)Lax{=7uCWHMImvqSxG6QJx2lp>LW5Oe7gJ1wh?RvxRF%acgC%fa)ay|ENL~^ zuzVlNL9BXqA4+hLAMqr6%g4EIjS|}*i;3Uvt4P>Z6 zL~Z~ojldUlfjHbqT26C$+S;oBTWer^v^B*t=QWlIqR5s>r8q;SK+6Pwz}}H@n!O>e z)15eu-q0(a=bomrQEELejT2IDRB>H8_c{k}Y2bRnp0xCmJ%Lxrc0l>acCc`Kp`BP% zK2RE@ezJhaB{LXK?@TRu;TY?a@CS%=W^Yl1F6A!S8{t1I9dnGgL4U{D#JWqI$GehzZy(j1X8LdI>iVKjo+vcA2P#J2cv; zHn`GDR9kfm1U1?QjcPcPh*bZ_Y1{$7;`i_;e3Ep3hLEo#BF%=;65FYTGTKv=7VW5= zN=Z>Y&rnK>p2=vBF_cC*>=C1-+m1L&$N{U3EK19pB-C#fDVTPxr(Jd=7P-|vKG8P9 z9E+675nSyn+|9a@Mg?pg?q=~nqETT0c1pB8&}n+0YUWHDbO~bh7knGYx)OJE7O!+K znfi-cch=VKY;D<9Q?skZ+Geq|S?K~ySAA78tU0T8v_iJyvG~F^^U=^I(439Cv_w-+>t+1J;^)nSI1h~*&>B!^_DgeJs9_=iN296oy*P5tX=h#w1% z3O2+z%R{1qtF>P(Av=7tyq zo~Z>^L_K@rBQjEhBBiTi7HzSHY2;TRW4GKN{Ze0;_92QOir#Kwwzblm_Re_i`{x?e zCG*w&TZ5{;`ybE~tx)-JQe3R|^fGS_<*Tv1kl^6Ut$2wXK(|*^Y}W=*p6|T6viu5K zPJwF5tw|&UswqwBSx`-BjH1f$F;=ZBDO*>dsWBc~svEp~jZr*JTA-lo27S|f zhCot;t9Dq;&=V#hNeT2i(ZJx6(CJ3qGT)|te$}msj{G4q*dv}pOU7LAern&D%)Wnv zD0nHHmO-m$=qZXqK8Y= zIalivBOAlwlM*7LS1b#Rb66m|j0kHCUzHLc6%(*5FxH-tRc%Hsu(nYPpbwbikH4&- z!i0kU^JUaN-cYU{*1yzn`t;nSq>`9wnFvA;5^7gSoRVJ68* zhQfGgX?YDT#&AY^OhOAiXgLik(?Tu-ebMRQ3A883(RM)fC!1QaRFlA&9$tV&i`LY% zl$&aJt6qLpmW~{VEec;5oDr8%p43;9*Vt8CQQI+EzOlA;+G%uF7-EYj@{4D6etvCw zT})nbQCSfi#ynooNl(JkS~M zq?M?Yv|rz-sYAJ+VO1!oXMrtU$6pUCN|@fkGv!kqSUkidf+VoDcQh?IBQ`d)NcXYjiu}F>ULnN=1CJZ{>wS{=DqmbpG%eh=unus>+YMT;LR--{eu}oT z5>BW=iwY{dPEi;)Ey`1L2}*~%o|J=LcXVNvgZFy?k3tV>SgcXBoqSirS@$}QSvQPX z9juw5QbQvTL^`@ZPv7vu&aLEEPUS(RCZ|4mbYNxVv@U0=nPiLaG&iTTI0-C4Z%MLY zorZBj+izmFPj7fEMo)W1PkBX8dPOg4P|R;xYeyxt5PKt|dPqIJNH(vcG=pOtE-2`I z{Es9Kut4Qb(X|Tp<38$7Js!O)1Qe+$s%cM4tG0I^ZI5x#wuf3{=ov+dI@%hO?Oz%c zeUG7T4T`xIL(i(H;8M_Y5*p7#%}PS?>`JuKyE@!?m3Vxr^3X`3k&R|?ti8_@!HyPS zWyfcM9l>IGGrJuo%pae4HS2$ekMpaQIK*qi~;V0-o7Au!B2YNe^%&^df+ObdBj0Fs2jnq zA(JjhFIdBj4y)h@GMdBmD~^%BtbH)$!I{63`PoOzKOxE4Bo zhSfA`fcla;^OW##g=_`yfbMQes*Owu&WbWxf(=XmI4mxN8We4Yp~o~R+5tuN9KJp4lGf%` zT57k9!vz(^ik`&xtOix(J@a>rF>tDo90ScPgHhL(c#6W0MM)llLtew}w7B%=7#AXV!f9i*|X>3UjhT_SQ+v4<{`lh zH_CASI|trXj3+rKzl#BVdSue(n4d#E;e#TZ|G?irSli1)ptrL z?i1tKBMKRY5i<<$PjqSc^hi(W^xYa1XGg}pM@1FxaB9aZdQw5ZNozBVL$8E-lTUr3 zMe{_T!lwoIU?oZVH7Hhd8214c)yQ{IgQ9O!THR3zMM=VB6gA~#1m;zgdNJd0K}Bh1 z%}}=nMc-!VSqX(VNGh%2+>t=%4m#p=?qJlPsyy)2j>O|L71i|a42-RMYNwXvSYPAR z4!Dl5+2rNO&euAzb9GDYmMUH>@gX-FQyX%uEf#BEo~1s{R==-kU3+N3m!8)ty`rtB zrXf4KNpnn~TRN-*+Gi(_(0cCz>%}?hSF)CTZr7Y>c$0q^{yRI-kO!+3(4}_e=g~d& zKoUF6u)V47ifSU3qx|WO)|?hN%m6#NF}X(hFhl8<)}AURWQVh$I}=V6rLz6YFZevF z|Nmm%=_6Z=7Cr41Jtd*>8V+!h!H0UM*C$RY=r-yTv`u}lgcf@6!520_O;mXzPxC~d z21PGoeEKygdJ#hhR8-T?iy9QYi1q%c2F3Y0$t&fGI|~iYYXyfke(I1*wi_ilCmPg&o>}Z*WJk1 zyML2r(fPFKbYM57uu?0>T{!+R78T@OIKW-F3rBIC?!-LZlZMqEv?8N={=2A2SY5L{ zIEjb62M4=V9@Lyf?Ia@ZxiD(jfyC!xhwur2QNlf! z3eWiTRkcNGgLXQE9w#amgd~tHjA~tAgc||Qm+5X z%A&W(424A=SEgi>yo%S18#;~v2hv4l5np`!^Or2)QTP79VH;h;KP9cskY1?>~If%DpLnFVvtPAA37m3$2tVegYbS_kL+2x zI-t6n-A4X7x^H*YwP~5gaYt`kaO8BEQlo%%Bb?&FLp_N-{gbh_{G6#K5-NTUyOJ$= zI1a2Olz^RFI-ckaWwi%Ji@jj9tjfSpoG}=BOyO{Zt~e0Y!`*=ys-ea0v1$NC>72oM zuxbFK#o2+OY%RZ-1FXZrs==ShKk2F}$a7s~HpBbo;r2j=c763u)OUXqai_Y<`j9k| zM$iW1g4)0{AW*UVD{5mgdP;+0zZvZ@hJvMn95Sls;IIad)49(8k8fy-#Z{IOie-jC zg6GvPJqrY{ZkiE4=FMf}P-m*j!4pBjQES<|oEGod!`pFw$Ib;o4S~;~yUH)4zTxE@ zE~;!<1TtUjfjs*^d0aMYkYUYTUJg$jDuv9*7)eoH(RB_32_vemc-AGHpZln-R4CPUEI1sdJIB<*Xu8p(-5_{aS>KD=@yJA0&3Ois`6^=a|d z@fmosjeQYg8eT)Ieh~xSP{Y26;WJ8n$38I3her};B=2+|;eFi~_BS;_oPd)){IuW) zT81Sxj+5T(1wE}nH_5d6PKLr60F?u*tDsZ^>ng8LbZOA56+Yb>ROQ~IK~?!qc|}hu z=#@gV!nc>9UgcAtnASYer|_8;#uYyO8g!e24rowSzKbf#WBg`XJ<3p&gm=m)YRZeQ z=T#0uqfD#2ph1@_s9Qz(4;A#R)LJ}$wbB}{s|wJLr4jE?s6SPC2zM*!XBrgq2-c4? zdb9K`j3ThKul^QB+gMkj_>xmc0&_@;_|{5N58rY5uJ|nPY)y8b1`B+)<%ezXizJs& zL@n_g`Vp*f?t~LMJO|zfw<4X8Ex6Y>DQnTi6nG@ zqL4{zxWjoJl@=orqtzW%Q32wJgdSrk%16hCNe%LO(bT+#1H>nZ!vzhh@^z~y#wUsP ztcnWz6!Z(P=y`_12pAN}AeM0uj}+M;K1rO<_?RV+-oTgQn-6I119Jp#=hvAFz%{Jq z&b$>K&<~4qe*LTM@rzI9CYW{&y+j1~RX z3;jaJVZv6uxa@ICh!?W!?WVUR)}QR0ynm7|>}trWZ9 z^dZP9bI|iCIL?eOL^O#nL^K)2HK&B0g*Ycr+Pk2)0`xYCz5NAB6kpM2(oc$dzO38# z!YfKI+jFo1j3+ot;a&usc`!iH8*@{ai#S{^@%Oy*^lA5zbLT*67y}+AK!|MVG=kO( zV}QEg)Hys4eMv{n>3y?h`|hV>Pf^{eY&mvm(US}X@6~d^?Q$v?_Z+nolq=BX!t@W@ zgH`~C*qlQ>sS1c|X-n`s&b|wiF#Gy&Wl(qj26z|lc8B4+#K5Cu3#VHe_^A%%m3d2x zC(KC+ZRz!$V+Y08T&rQDgRDF-^N`LE)ff@g(L+kb-(Iti_>g?@MRH&-{DZxOgpow5 zZNq7HXd4ohvw){+@Jb2)KHvc=4yV~EJ-&CEETOTE_Ca4E;l6-p(n!Pl_74f~hMvAm zqaeeNN%#<)K7N6Y1>?g34stHM1N48@v^y`+!%aHwKbWhbEg$OQ&bN}DP>w}#fVaCCueBe)Y_^4 zW%Eukq7A8mhMb|C>wy|=d^Oq`wg^x40dIz%;OZ83A`X=m;3na4qMqIZW7IMV-}r!Y z@Bf9=uwKGMu9L{|PO<1Zo5(=l={sPg;b{bWQOVp8$&Ec5|arO?W(q$f?ZBJj~5 z`gIRiE15O9TE*LvfKw@X1~@$RI*+e-gn90yqNu?j=zMBvbdt%Qk>VOenYW|B-w#^V5Noyb@osVb zTB?I{K9D`Y7+@=Up^|@69s>_abs9<0Pne`}zb?*U@=hW2b$jplHa>)(dD8v<<3dxV-?Df7~X| za(8Ob(_Yb&3i=SuA}I%buY?xzDhF7h+mO5Z#GRTa`V>BQ(!7)L>DQo`cQSN9gJSN_ zq`s)4G!`@TsEP_>3hf6PbW%Y-lu(eGFO^Z0+KZ0nH5@Pkv(_%y2WC%*WY zIQw{nd`z5eX@ttdXw1%t>Gbz2@?RNSk{OV^sx+g*y-YLD-ta}MJGJy!Ul?K1R?f~w=M z)BQ_>qDL~FxmA?TDGWWUqI3pj=oenm^ODx%si%C!32Pm^z*>E?$0bLscj4=+bIFPr zz2Zta-Hyuo#K=H{|Eg7)HjP)|7do7|!HGOCx!2yV^4X@~@G`Ufb&hnct5H4u4rJUY z_g&u*%xnu#Ix1JOQL)H6y_3$HtnXT#;_8K=pKCO7KtcblK~<~&59kT@I|Mr0;Sl>wcpnMDtw^*YSpRg@x-H9!)bnSl`rC|DcI?X7-hq z^k-TA{F0?OKfl<5kr~1>>|e5X8+-dI2hQ_9xeFQG^&Q6WxNE!(9xrOk>Mtqj&m8CT z+`pq)`t|UZM~tVj9`AsaZXQFlr=QZH-{iRT`i3^XPNmc}W~>QIO$~RZW=7Ya)EI_G zxZM8KQsN$uf8g) z3~UW`R!yzyI%ZWNQqJLlo?<9n{org(Y4tpPmt*H54i_0MU5eQmMnd%2CNrntr^V`hA`At)d?p&$d^${QBh91)zEz>qwUleSH@ zax>G)N2nEs-T=}w^q&-!PBiOuJnT&?R5;@>s#BV0;be$}ej=gq*hd-F3m)4~&=ZP>Yh{S2(nLl%c?Lu25J&yf$G zBgb1?MSOp{OaU$3jXKfSE#Mts`W)31MRliMLQVO=ULbH&XoZ(32k3zoh3y`h1D%T* zhoce)++R^SaEumq0=1|PHjLN_v?h=pc7$j}1-zWSZ43VbN0vLA=L%dc_4O?y1#^A{ z8(T<~_*6%CS0||upKRHP?_R+(Y)!Nm_p|X%!CovzPkTjAc|}ipMK7u-?J;ZZsDu{s z&lsMoU=y8RG>hfP26z!Br>{d}apELBViV8SSG4xytTIc{&*>IH8q`gv) zlO0e%@wh9cm;gC7QdwDC$WC)8AD-|Fh+N*Zkr@Rf=1+ERi;1)(1$umW!YJ?sbFH$i zsABvz9u0MpNrEU6nr%1hE?V{-p_pm zEgGsY!aYE@61;KIiQ#`dGx6U!j}1JM^~mruQ{RjFK0J1w*V8{o$h)EmY=%Bf$NSeX z-ndus7`21N_(?5J?UB(R)#4gH^N53Vu(IbfCFz1fq!Yt7I5BM1`=9-I_K}Q7*3Ex5 z_eA=yNL3|qoH!xAQY8N7{CRj1R{6Xgo?Hg~vV}<}rX3;w3c5J;N876t7XtrU0^fvv zlr)QzC4YsUPNqhBsYPpT;G1*1KxqBQ0)^cX{7b^2hCx7%17?(6Fh* z)lpa5VI;dqVb_*|f-PO*3#6{St*u>r;?A}J;%kTa+`k z#(#n}V$A!tTs_n7)cK6iojN>Ec#dBVo{$0AB~32r8<8-vmtKt|?LFWkQ1dc9yMteD z-cfz^HR%mmKIs*SwzX}JDO0qmBdwv%o_o*zc@<3^rrP2#XN+;AHm%eYl~9zNQEv$g zZVC>zrkIjjQqq#G%cE9-#8d(*@d@TX7tnteqo=*1rzA9*qxkrFPEHqbf?HHA9a{ zD9A_ck?{rjyl84(!vUi;<8VQPqV+S>t)kTW8G2SlX*GeNUwB2&U%<$Y@7D?iD=Md% zCs>YjD)s9c6TD?ntul}&9r$qvW0~Xn*p+D>guX*qNXt<&D^Ik5O|pRo$5^-`EmF-V7FpLxV=D+1_MUx51IL zwHo&(TW0)n28*7r`DuZ(LE4*4FqEcd$a|CQgz9-{&+BbP{1VZ?)myXs4hY#))Ku#brESXTBCCE7*%h!U#qBmNg?4)+n^-jMngiLdDU5Nv5Y^QHZVy~GE< zTm=G`2TAHih~r+qAMpd%E5s=tAx?kZ;C|1)tqoU@XmQdrI-SpQI-B6{H)Hg|-!aE| z`2VjRJt~RXvcBqJjJ%7{N<5u81B<`$451WN458oTg5AO z?YBG?qMo-f@nD}I{{HC9wL!6^snC-2-9P?%R%t}iN~QWRoq&{qSr z4kuf=w`D144jp`{6zszaTV}So8msHOT!T^YE~L_(bK*}(Yh!Cyv-kw>yna=2F=`U} z!g609h_z__w3@pZJ;hL{yV2~A(dzjOl>=G@<8X}8%9)}>OAocuJuA%nLCaKg@=0Sa zpM?PFhX&JHqtcSg`#gK``q=8Y=vvDflB?|u8!w*_zSM_FqW6jAf2!wKk?j)!JAyjjXISwKfL*08LO-Q>)T)P-|7q zG=?$`YOU%=SnZ-5@P_+2r8f2>+PhMZ-WBqUnxdNaw6tn_Kcek14x098q-3aC71Bzq z*2bDK>t(&uskO0Y8kbo=)Y@1x*2W}SsEyGvisvU3{oslXVwN&?uws*_)rwU!m5p)M z%7(KSLs`WNMqhXUEGU6mkf#Z0imXt7qaSCesbAxC)t8r5yQ<5}>RmhM>Sn?WVKaXA zsRq(3KHLERkZ$GY59hW!Z8oPQsqg@msTsxr*KhB0NvsDR_(?5J?S#=E)#4gH^N52d zbT2#vH!YmfZ4vp5(B;0*P+PY(CC%irx3$JaPB+e6$$X?_GpzFKU^}6EgMV@H&W+DJ zg|70rxUp+9eY+iPgW3{3&7eD;h-0%DJ*lEpenxv#MX4n)^q7Q}@pE)^5yd+e0UW$^ z=@QQK8XlNKFdi3Flv)!*&#EZZ7DLY@VrHS%tAemjr&g>?()Srs$dy>RdYe|R#Ddo& zpL{!l7O+Z1yAgKwNDIL+Rq9nfLm)TIai5?*7Ne&$DEa`SJ;qR&&1e;ip?dBNX8b1d zL(;+j3tr6zGez0r?=ytxX)Rd0YNvM?f_&4{;_^cxBb&{&O+PCxEvZ{Ko|b4f8ZG|I zmKKqpl>2WqCk6#%1c#NDKjmXjPR>n>2nmmf3`+}*$5!K^)jiNEtz2{@Trw}Q`|hxu zd)?Y5$W^O^K4v@O?YoaF<=j(2wLxq0vJf7{ddv){tUFpo@+ zuU_9$UUoI4NR$J9lV>HIIfFRx)u+;DNyerYnEh z5B7Es#18fKPqq{k6*(L8oq74-O(>%iTF_DM@Il(3S@H)W!k#aGxF^EB2m3{7@ar5t zQ#ktict}2^Cxu3~&jjZX;eO04*)S-6i$8qpNAMAIo?E^!CHMjFKzx_YWS~L6LeQH- zi+Q*#66gyrdxp&^Jzp%o;wvl-FHh8%}`Be(3_?x;S7~_!f&=nWDULPB)eD&4& z`L%D7z8vuilH1!Gw}K3~E6I&H;zMx9XM1lRnGt`0dWv88oUi4N;6D5|M=Z}?Bm|7{ zy$Tkd-VkM{LZo*It>BZA;*FLK>xY~@P2c`zMMFlDYjtLP>5Y44N^%E_e5$ANa{D&! z+|gK{m`Gv^)9ubSN4CitNupxf6JwTTw>WZ|Qlc{2?ba4sxL>j~Qt(w6ZV8Styd(iq zhCnhqPVOBX6J79$>|=biyONJ`e<*48C1ES*Yz{Q3*SB&k14LcEL^X`KP9KYJ+~8(m zgfoS$kS}he)KmO4{Pdv~^SDOGut(w`251|vE24(^O0kW^xt|z|{lI_l!dj|Dkfc)9 zq$J7Xw8bs7#rOi$=5okk(5J%WQ-q*LyCcW|T?#Wez``0=#;(!_1f=Ii_$(tQ$HK+$ zg!&1g=~aF{K7Bsbht?4@^!1v^sN>V$p3ar*Sm*|hE2(y%G65&w1TXf_q7y9~h$|)8%XCbrY_-2_CJ@9RC1YV2~&+%pAxo}|9 z(*ulvhznRa%|8s3{&?DQTd2K+hp(fvBCkeOp_eVA_@P`+GQv8{C2Mpmq37993?;PjhgMTpM5DUs!%^2*G zU6K<(NZwGeA$Rx9W24>Hwbqs6BswBu=jIRIZtjeX5aEO7_&s@u?h7u#1YGp^-W~KS zwC^xm7`S!_IM?;#E5mPi4T$gIC*76z{VKBOKeDyGL)+#i@eqsIK##2A-}t3rV-ch) zV(jAW;}JcP{59@tBk|vrz~owd7czL84}4t_~U!>6&`rlK;gm%?Y!>Red5tS5&M(f zg9ANJN_-}JR7T9^1^9wG^l3l#$>$dS0h;fEpZi=z@vRi!PyPZw>5S;Jo%S^pGg(>h zaDFM!q+HT+;2=tOA&f8<*Nu&dSCNmxUu)^^Zh0+yY%F|!^X>yXAWk1A;ShA1#Yb(C zHKipLQR2-2z8br3Y-%&cw`}mhM(Tl&_~Dq_;P(f4_QhAO3{Kw@4bRf3HU zW23{^Xi3)1q@cZ>9Q=|->Fmz^5%dfp40Sv^j#dP}fsK(gVHW;IvnTv-@a`d~l1rJ5 z=I=8TX(4fOAtAA`ufZSui97^dOlVk4Oju}){1fyX1Sf>Rf+1#3Mq!W9z92Tjj0I=y zH$!BHPD88{Xh}2#EF7=6>S^ff6h90PC|H}b{<^Wgrq1SxU@RTz)b8n+# zsLE`v8nWBjPcWD_`o)+0WOd3v`{Bz-1qEZZX89+`0q2VN_0TV@QNWwaTo5hf@M#D% zz)PD*ARzR-qLufLe7EbWvg+MkB*Ojjf3N-#nT~v=e{`VlPshUZXPa6#7Pu?Vor60L zSI2a;HFbj4RxF%_nFqB7HAz)#4hQ617>cQN;ERe^@M1X38hzsX;aB@7))tuxS6^F2 zMjpDR#y+r7)}T+v5+TN(ZZ8cEjqgwNPn&3}8pvLXK8P8V0B@04%H9=5wW+-(X*A+7 z!dJ)MfW&s+b5Hm5KJv8q0h~9V7ymvtyJ34vW8F6%udl~(UIm;sfyDIg@@5B&gjIk? z1)NF3!i;8k0fP}{b~J2#P?*)Rsw%E=$maWR{>lINI)jKgPlopGgPjBOCu*lZvF)SznHnZU}_Aq z+hMOkrx%DtJUaW~?Ma(l%ZjU_9|gPpGr41;E^KUNZX=wb0?8o-hELa_e}GD;PeOhR z*>f!URNR3X^flpx#5OdD|B&)%cxHBCn!gpE(cv9Xx*l)@hB-npBh(~c;RJuMV|mI@xvklRdF;)XtICrjP;jKvb4?>5X09`#J*ZXz4STYe0AF>r(mA^)-`N7P!-qP);Z z*1qhK8S*hTHSP~C$q#`e#)#BQIg&*jmlJra{em{xWEb-X{AKR%u+N6_! zpbwuM75AAUUstQ5aeqK730%*c5Qp9u#c-aw`y?PKA>6*3HXotm5$;qS3L;1)sJ zs7B8e$kRQCrt^k6nrqveZy0RY-kX`ym}_fJtKM<{;F6vPZkjHr-qqf^qawY1Zoi*n z^2Uy-M;(@&Z};{dXvps0SzkVCbBtFE+}s@+Ss%Wr#of5QXHQ#ea-+hCl1HP2o_ej;AbJpOiN<=dVtK%PL52UZYZ z$&k>ZWs-N{rvNc-RKS(R5{RJc++cJB%4w98fzbeGIuS88M)r@d3_JLm9M)i6t4YbA zB|EhAciPe()^l=e`0TK z?X}~10`HD3Xg2F2hlBkC(i8YY5yV_GXtxj7rld8D*zD`GYD@0$%Nl?SCf5z;OiACeJN#@$|!jkc7Q)*h718*V3fXpBp*w)Xe%#-8@l^MJWzJsFX z7!%k@pr&9oN?PV>`IgG%h6r7q5(M3{Fo;=yi>!J6gYaeO_}v>T`C% z%0CBfL4I2WFZYKS4H+iOtLahHB=UopNJnO@I5$2{ZXI*>j6YNtxMW3gJn0l)#T=64 zxMK<%;+9EcW}#!oqRJGHbrb<+qbb;IB7Gxmz_jR!I=o>BH$8ETJAG%mWu47v(3{fM)2^td`Qc1GSU_RwsbfrO8%jr{D0> z;XAP8RElODa)w22uirb7M|l2m_wC^%r3Nl5x$0BXo5mcD4LP-?cQw>sH&a+Jwa?)L#sD>0NBXF=#G5E3p9vg=|P z1<09TXGN|j(_7U+>aA*atvaRkmNb)Tdv(`P|49BnmWVUtgZI-6{aXu*uJp4cwHVSW z3!LRugiGqGT!_gTfbPo@%&c}oQIv9yNkdVZp->q?!6+kyX|xnt zQ-GeuV%4=w$^TjY8U_xnH&_G#lg>n8}vYx3iBo^6`*uoLS;R~ zP z_$u642omth-U^~SyZO0i)_kENB&H`8H>H&34%en;G>|FMCH|ZEi$5(E z-*{=|%Ie^Zk(VW`yE2f50t+h#DCitdTIt$bJlxCZ1y@L!x-R6VMPyBMrNkX>$ z{&!mlM@nFGX4j&^2WBOo!KMd9c((+G!>52!BI9)&f#!guAJo_71@%-i` zcv<PCAab7QqC1NQSUXmgMe(i%?~u624m#z(HT`Z71VT*;MpL>zp@ zYu)u5k45&Jrk*qDC1%Mbp`Jpaz8~nNZ9Oo#{xG=qVC4>ztH7& z@!Q-7`TNkGmV>BoQx04iJcG}X2T1O$_+g3o!6boX9_nW}n1?YcWG}oTtmRKZ95t(R zmYgX;eN>sWSb{_gmah(dYqp?Z_FF^4cdgIQUw_wd-4z|_=^a31T%LKzH7nT`Ek9pL32gJfxdwQ4U=n! z2WNjx1l^GPF+#pEaj?4j;Doq=+%r8jH7!okk->KUAKJbHu&wI)TldM9oWyawCCid! zZCRErFImg--h0n@#m-J3gAg(ZkOV>oVHQfu2%}||QD_Uaw5+lr&=zQ!VKhLQDVxU6 z-|yTfd1O%j|L^-ClKk$wNM|*k*>lUJ}>x4(7HOzHiBp*mbOcFw&pF(iA zg_rLh+);hew!oUwXvgwE@7R*QfyL~WU6+)e(XnZ_!PaZdSlH1uG~O?EW2{I*TS+vF zb;i*s4Z*Y9U3@$sH-?{izdsI*2bKVEDR1&S`6}Vj{}EQdFeG915U|_t2X@!3Z+AM| z*8{8R_cYC*vT$U3Q`7d5!tigey*8wnOw-z;?R`Ii_9h=z`VxVg>&dJ0<|y=}kCY>6sdT)j~Q0^{5TXD_Qqs>cgEjKz-9;;oq_kSZs?nY;`!cWAwr z1CIsL0?QM?W%)hjAD#mqyyTPPJB*2*_&NK6CgF$}cN|6uo`wcmNzsrcvC=>xKH@8> zE#&8kQ%SzU4)OFUiUaT-h#wuK!4Nt)j9Lts|*fqFGMff|N>hQc9KD<#d7XCx{MUKQTl#NU4-pN>$HDC0Zz@ zns_S5b`HZ4)Id`6pLq{*CO65|9M*QEL+-@Neq!c~K-N9U%HuO~-QLUCB8C+nM#A8k zeDNX9=Yjt=CpqvWD+(%5zg;4=U5vI@2w#W2D)5~S{7fR2MFioU%0_(qBw_d3%NN&L z)L9i>`Tvf2E79x6eo4;kvDIUN`t{{i+ZquK+1c#QYjtL|L^(Rn>aJYXS)4JiurDVf z*5DtkOh`;j%EU$s26W zQ^di8^=QF8V(QHY4{q5)@HpXXsEE(ruZ8L7wY0n}3JAt)b}T$5V~ zm`-4k0XwA=T4V@+xuxYMMm}GbuwM+2ATdslujbN+=K6>u}DlOiuo` zdUffNzKDX>oRU>l*@HQn%no}-&Lb47OEBC9TIy%MNWRIM@wtXbtIfu*P# zk`h_GQJrjvt+HD*w&YZGoeo zsZV&f0P!eNp^T19x5P&YbJ!IlD)IAVr99T)%bKraZA@_1EzEul{e4xdyR>@q!h*=q z2vJOA+P6tQiKssKB=W=!j=g99SAS3J%Z^tPFji-<^HKJfa}bhcS%ladD%*gv*v_Er zl&>_5Z}!ND_!lAm?YAdBeN*x$_dQN?KzoEaLU>+kQIgL~s0Ua{9|7c>un5u4%WAbq zzRrgb^KUyZ>72rQEnf#W@|q%09U=%xHNiFjTO3c^LM?X zj>FlNY~EaU!@a-xg)nChyXpQ1Zs}R?_pk2}qFL8kuWxOqFfVT?O1NOh_PuQ1_MQ7X zot?$Oc1LD=QLxj=#DB|gKH7`^VnlFPf#6&T_JtU!_=@23lK`#4iS*;QIr>~9r;gMV zS=(&Nk$)tr`iE}+rLQnCUVMW_5_JrMCS8g;W?51wrM)F4%Qym)lzTD5M?V*n`Om!71?ZLNs&z3c$+PW zwJ%D#ZDqp)?cEF6A3N_6|5fN7KS(@jQWTy-9XKJO{mm%N_z55q zmXC-#7B64F@~x3GN-HbMzzTrOCk71TgcU8{h1s@7qE4AI($1FP7c&NA#Q5R-7Q4rg z{RhWwX_+OdLW+ze;UB|G){cK5zCJR-%p*H}#XG|iMpUBC~$h?y% zStj`ZM|>rDFr0K7?0d{b3VH;|pKL84`5$woGjyWHPchiV;>+(sxnF-yN7Xu;*U+Bh z8z?9k@a41{ytZ{LqqbIjv1%)#C)YSLJ?Z+q9Gj;540b#!2JC(C%JeS3ZV_V&v4XsRv8-|u$!`*YgR)cOx=YM2WVCYrZaJ00G1 zeQvMU+mn}`?#ZMGCQTT>=i$X$!CvF^0$pVh!pqQiz0NVQA{0^ldc=r7$n$+n($FN{ zBJvZ(M~5!30uU+*LeN68guqcEm(c;5nY~aze#jduB|qf2w=(GSRp%{f77tFx3qLGg zO>w+Y+aP@Jmmo$$=(YyYNo<3Ypvw%8HP?Jk(-r9$b8zIq0k)xeQ+4&G=BCZn)tj60 znzOT;^Kx6Vvs;AtfAk&Vp{lo3R&Hsb_{FXcpRXf3o4#FWH3z*>0DtIg8Qem$gCHXk zXsciaVF_75{5FHnd?PDnmt|_lQ@A?Pg`@0toO?>*yGk<3+HP^e0 zM(ArE8|yHz1#23+2T7mkK6Ze(%m`f=msf8Kg#&j;!-epGt7nzc3M$`$<$DE+Hjfw)mQyx!%X+<$DR%bxhED{XG4e-qlhHEI=DDmZxzxw z$A!3WN+=doFp7X8>5~suJm=q=SWufkH_eh7yBCDez}6OeqvsDtDeMmM8m!t(RPhC; zL(p(ZC%@cuCzI_I|Hx(7M=L8I4au=YNhb4za^(MSHW7nti}<|CpIAOzV6SNFvSQDf*w;+{*ROfR}T#QIDt@m9-t-3Z6oJQBv}Dkb~Sz@xsL{fvITa`w8)q( zwGqw#*IiA271@aPC2j8=i^c1!j}_j;RZJ~@KsC}JG;_?IsnF6v5ZI~17q!)uH+YMSR@SzhUY_o^+KbXXqi5GCYR+BPljB=bSus}V zTYO$r-Hx7?^BfNQn$oh-!s0b`&N8#LGP`1NFgCtAVNOolDdjaQ0^UUxRj2pm0#+yb z200NDh>pX$L`G-hkOhF1lDrs8QfpXZ3`s*F!BMGNb5B@!T)QWw^UqXL0ol-t@u%68 zRq>UJxD22bOgOsu`P{z_P9%=z2<1E?OGNF#^*Kbr^QZt)JOxl zpV)t_?1^@UP#^dz`0-1S*NGoq2wmo3ZrO&J84a_Ku(pBsBy{e11>_Pl0QSb zA&z^p$mWO3%IfB(wkDX(?(cec*4J!pY1vZ61X!bAZD-l71qH1XaVWQyA`X36FyM{T z=j+91e=u}8Mj;Np22{YtHa^-*SQUO5jgIV(oJR0j92%j%AV2h7@NP=G6N#oa!>JeF zS%L?bVs;Yw`Bpluw-6hK@D#GjP>}v~YNvUn&!1{DTC#9h-cU8>w%3c7hPItg>S~j+ zZ5iXsvfchorxoVZ*`6d}^-xC#?d%Ch2+ttEhNV;NZ=o^<)PsPc4NxS{lmmd;P-F5< zAejH{2#cAfLs(4lSxBY@Bi&C*@i4!AlkW-<6BG3lE*zMqGpPSI)GxNtK>@eikV5s%@)5xt-DlTUZE9*fy@Ihv#WhxEQ(k^Ef|pUq zKDT+LpsLzTGG5h|Hf5}`TAkm*BMTO`N#V-?iPR>+J50S$97-VgBpjkt!Xcz?rBq2f zL#d#Dl=>QGbn@d#54_;TPoG4F4_QoZ$wosS_I&?A(=NNkxVE&ttG&3Qr|iNQ(kk zcq)x+hd`;2#HHQM`58Mu&VZLb#>d?+s>-1JF6|P zB`=C?P~^Ad<+bE1#J$S=RzW36&a`s3R5sdca&v0!;#PZY4t@c#)E+^>tsxYLxSwii zA493G@aRqH7N~bzm!SCOJ@h%GaiF|X>Y*8_Q+fx*N0b`cFF2A|7W1{oXJkcg9lB<%uw zvdy78_1Ct#%Nh2eonrZu@v$}Y7Ubk)Cv~2lSMOlgLkt&W^RVI*P`ebZDFKemJo!vT zv?qipM0~hC#=L`nUUkt$E5y~``u*BCTf8~T-c?!V>-45(*0BXl{@{ZXpFXLtojX4> zU01)nIasl*t; VFoG?JrRs(h1>6~=Mk2bQxTR)5KsQ6{O_k;Z@+0N)H`c#v;E4! z_OVPqGyFq%O?)RHzIZtD?GFGi!##rUpf%w-$dE)5vcmwTK+ac!ScdM*OkIAYj*!^G z?A51~kP@=ZRlK^=#g&j5_MlN)VD29cltBrZJN7?NLXy_b%e8js;*4FBbeU@EL{qx7 z2g13{P@JSobJ{a!D(~VXwO)df)OuYZQp&yztMMYHbbBv04FQ|7A*r)XU1+xy%n-TIy=N=m`^FHHZUt0J1|HgTnhs&1?^xl zhNKI#h4nCU%*~l2KcO9%lB;5#VA#Ao#8_K#I=!7dZeTL# zB(OL%M}^_@hDfxbRK-C%6V?iFO+=&;7p!*Htnhi4*VUd@S$Udx$wI~4doWY3HiQDi zH!CW9(xWKd)YZMIv~p8>+vbWt2!Fe9%T_qbi_Pk!%`t71JF~_d|Dlc4;mc%maxeZt6M&ba|2@obH)q3sZqjxMdtQFX=-RNsW%0-mI+7GP`d{ zMO^c0Usl~T-M?S!uc5{=#;5 zs>a92XguaH{PMS{6qlzQRJcdXqG#%1p#6(K>wxqR@ zP`0ed9IUMkn#%&ZVo#F6vD{rg?z0rt)fHLl{Pxx~tiA&R1tn(7* z*Q)g9+Jel&wD|dPx)iG=HYKK7XU3>yVEntdHut$n@Z}KCA}vgK&%|R@kpBGt(MiSO ziSNTF6*wRDu?L|6!=aOOPE0gF8u!rsQPN0Fj|IjB1P>^p_*22sES*~PSnM+vXRz9yiuF(E(`wWjW6)+POlvH2TMPB3y5!`XE}yf`rJO%^ zxVCt>kZ`9791l_*biPgBOg|HkIGfY`=4B^HRU8I;#99K)oVm+9{;c8kncK_b_LY)8 z!hwFYdjA=q%B8TiK{*q_!-l#?HTO zBLfRkmQ^>L5@%DNp@im%<%@rBYRPEvv4p8VCO+M3F7P)r#s=%r=jO=^BS=4Qjoc$a zBS-t_Yb(|{Buw-*jl)C4BfN$b;bQ~RDq%Und}`xJQd^*GVUA~6eRh$qqCB@UzB=lZ zQP%KMsyVB}9~{W5Gy0ABl{SORhpmE~G{wSZ)Gxu{pF9*Sq_ct+sB5RHdj?`Zjv;Y zc`aGl&AGX_-roXSy^bGLT4D@=KkQ@lks^+83C6(pF@x+v@x`2PxOj39>>ab9olGa) z5>^P8VkO@&klE5_?D?TkiwR*Vq^Q{^x#aB^QH#*WbkdcEsGp!xaqUGuG-~{kShB?E z!qX>df+4~)cgyA~N5gm^@{ARwK2{>WotoxfQPa{~v!$h=EeqSk{8p#4HL7e)t+zj@ ziCU01l$Tl9o)Dj$ov$~qYiL+s-j>;zo7b3$K*hO@bZ-PF(JU4cvVwic*Om>G5o-%; z?M%MgKpa(g<$#PSkcL~KK;%!1n6w5S1Xh5ZacvOM@mcsky(VU^golKE6R-WiVi3*_ z1}8TCs1?EEyAz}o$sIkJMbq(2?gEwmUqSg$u6JcaRn2}#j)vA@i~vQ0!e0F_$DOJ1jyowYgUrlU ztl2!0+FMW|^{d!e%P~lNk=-1%dPFdYLmFLnr@x4!k;zkLF}b{z?ADr^(7JEoEAXch z{_-0p5=|j$z*7~6_|7*EeCZxeNv=2%B~doFlUxv@H^e1pY6<@T#EuX3z{mIFcTx`& zhbRJ6h~AJ+fYTerH=5~G>X(#yh^J0Pb(YpJ(e|m8O?mmsR?LhoO>|63{fOt4)(GXj zNrGWH;c^^ilI&233QW!9N#zB8U^jl+86!!Zfr_DA-?ADA3{};+HSx9L5w_bn+=Mn_P-Mgnj5Ji{)!Rn~Vq^Wa7GGC#!Xv`pX0VitbeZlHm(wCX zM(gEUbY7xQq?MI*a1o*qHvuIXLPeKXAV9?#5|f;*9wD7ge4OWri>@pjDSQg?oK$hDLHs8?McL^iMduaXkiU#|J`Yv^7@sk2eUICrIF^+2{o1TWDQ(?Iykx;K zR=iyNr(OJKdMewadrhCG)8*;Y={sF#vFNlGGoOZ?6I<_WZ55hzxoK&ke>w+V+pIdB zg>VpPB;d9ktlF0XuLNjDza_g)XlOurB&lf@4AYxdl9HO+&uM5lr@f6%^4mPL_XcZo zxjLgd53E~vpi}zpD>|dE?~Eepn`Q{QE=2zec}$}7nYdqOJ_VzW%UnG$@MYu;1XA8( z+&0|J_-$X3tEYCX*rh3LpodInwajm<^Y$Am?Yr9QRt4hoT%wxYRLK&|9#?W-}yQQ5W@gQLRdsi z`GV`PeCYOS_Ow_S+kN}(-9HtVpwG0D$@9RmaN#aX0*Jqdt7rMnl*LPMm9%zS2ivZ- zz;)@*3Umd#mY?kpLssdt`St9So?P**oWkMaqOm||W}7jphG}`2&#Vf%G7K-!>I4R% zc)=Ajtb1^0Rp;fSc>@M(YL#{6wumU>g0ca3^{V3XHTBt5_U`(e+6-%bRQu1@Y`n2E z5q^nd&fSsZ*BY}+^aaCZ72}0%#$ZNqojGVT2Qf&DdWsuVoDGcT7M7tA zNAgDC*6W|bC0V(5{H=20eC&eBA8;?n%T!3h(6gKXH>cLeF(*MJ-A}z^WnEh<%eMCR zo>^YGt*di*xU*wusH1gFQOUZ7#&xAd>ssqNx~r=@+Hirgq_`(5t2bCOnAcVn@K;p$ z166EJW}7$Am7U!c@U~@^78SUQirfW77+)wJCs2FJOb4J!kx z{H!lD?7G1FI;Snk;kDWF;UliKz!)sH{5iLWYM#6d-tc;lTQ4VA$59P?va-!sW&9_;n15O%xjI z82bee^9Pw*z=X-=6NAoGvf_j_SGYlv5A%^c*<#liZ}N5ZY-9*3FeD{7cE%(uwh<| zOGB`;R`P>-c3akqV%fXS%vV^fBdez;OIYEe*)BrpKPq27Rx`h=c6kujd=jIiKz?pOhSEZQ8E zWYM&wJJNj?t1o?uHw25ui8s`voh=w&)3k*LzZ($Z4JTm^)7p|vL+97!4xZDRvk>H~ z+FHN9RH4w$D;xFKt|_fJt=U!Q$Y{uSHD}pdqFSz6+j;&-K~iFCLTvE#o@Bq)VJkMe zhfB-HJ;Wk#5Q8`ohT;sulRAvsR`h>1k2u6iG-5D+M97C}zziM{KrhbWnEQXrBL@4n z*Hms_xNt{R?e_k@rAzzz$H)7-PA@Ls*xI(SqGV%NOaH>A#(`eWBf7J*dn6v=4^&hH z{KxW$U_SAP{2<1e<`Z}Oq;ZybL`Wp%$80pQCtwm&N^4dT8fVu+lT}+|X;??&JhxmL z=M7CX&W(9FO&Dk4u~`GXu{$}C$_IL+Bs9<%bz`v&BM!KV0N2?+npX@V2zvvpO!&8_ zJ2$u6!@r~8Ze`(~X6d`fy{Ni+kz4wnUh}=julZZE{z7Zs`7)btclGqRgebn|Y0I(v zhj|Ar`BCPuS)&`9iU#$Kj`Mq)P7SCEvnORLc2D4aeWo?cJhI!|#$b>$58zcXXx`un zsPswK`?Bhup<~zk4{#4RX!1EH<{mTG`wZ?8UhkJ-z4!jVSnsP&y57M9LhBtg|L>fK z)B)bD$MKMv>ufd;39U4gf0geV$}pbmFjt&@{Qt%4kspoM=%;q_3U&#@B8mK5b`_r6 zCa+@aSOCvQ__G$zTlxEc^XF9LKa;=zf zczn9p|;$$^QVp8?paW@e?XC;N^b6W=gEDbSzueL7)8c@_c+E z*NrV&w{Fqcx}xTWhNh;5hGt+iZGG3VYmZ%B`7GLbiTKt5r+)7}(b@2|%QmI^X*EJ3 zL(gv{>znm%4?h!|W~gL6MLVqQ`_1Z{0&^fCSJUNd>(u33pTLr`t~c1?H5OGee5_XH zrFk9uHN3`D^yN%I0J(nptc}EOliM@x1%%Glo+=zp(W;xB-ok=H*Y0TXD^|8UJ*BO~ z-WsV@xf9hEz25A)G}WJVX;P)z7S+Br|{K$1TkTH;+&tq&xp zBxI>;yW|n^bDO={H5r+8`OboZoNRw`PUNc5szvVTXwTd^&DHY_v06hWIJdpXQ0Oq2 z91c^w5eUO5`UCLoTFxI~#v|>aq!h!d5=QcIu5Qwr7d^v4aZZ8hIVG5uXRl zVW9aK%)~hl@6YkL__Dtv%LzqarxRLZUohzN1dF`w{X@+y!vh^KugJ|QbVsInOjfs6 z>$aLaX>Pk-Z?oz3_A9&vc_k(J1)fY(s?KQCrkXK^=@`S^GsbY{UWwm?2?I!IBf%Hi ztNtRbC^;q3IW>G)aIRFJLj!AYVf?IFZg(UfzLqK)zH}#sFC$=Bxd;(?U&}ON{4$J2 z9yv_Q)a(-BVTBSjLdJANC76!EZxvFGgi^Ff*~;_$JCu@&QfKl!UxZSUk>^sL0ta5a z9?G+gr+gJku^?qXujlJfiW#LY1BhRHgguffy&Ma3dU6(!WH|x`^Hr?EWl=!`v z1>Op@?=}Uroa{20f_`~AYMhl0t|8?Y&Xw(muu?wEnpy_4c*HAEvls9y$f^J}{5oUC zVV}4Wb_XGE^qj+u#IH@TXsuSQ)@*(Q?ivm6S~d7nt0CP*)#MVCR7oW}U`-7>8jt`e zZ$V{mPgomoqpT>+++r>}_>MJIW3gybN%;nw>hIZi0%;Fn0lKlEI6@-4(|bkSex3=) zdm3A|J*Bats8?U-*xpmSBA~Hn*)_RZi-p5x^Jm*^PF+rs-B#cZWVU!6McHe`&X+SDJc$hN`_jUai7YOs-ZtLsSeIz64+Xz86=;S!TcQdSt=85klQ>- z_zV1#47ScZ|9t(Q^?VYLZ2a=egi zfMla!=j8A-62D|=#xCuy=@!d`<>I`anx3Cy8vrtKZ(ZHJ^vnKKhixI7d=IkLxpJ~! zlhr`Ys%7;(;%~?0cO0cNqRChBc8m13ik#YP!OI@JCA@898rfgi4G{{3VQwRTj$n-a z9?!IrtQh+>Ox%-an+4C7n_|QrtN`Q<2hbhV( zWH(?h{=&VoBvKL+A6B~I8<2FD`AICjp;4WL>y_%p24{-J5^vIKP4N~>$|DVRhc)U} zwdOEFP1~)nnQRG>(GiI@(`#0H3qhBFwk%RxJyVSodXnIzAlzxI|6xZ5+6kpA6bc{c zDRG7)>`PWa`8Loh`SM6bMJQeV!F0O(gHXDQl?xZJb16Srj<|#&R*n*=88uM})C|qq ziCUU`gh3*pl3l!|VkuHr1@p32wihGe<0B(urGRKV>Iukbr0|S->A6VO0*L?jya*-P zTFzTwjsTugDSJ@jIqV=?gWD!nMJ-RS4W;L>mk^`vW2A#PAb%aD1A=UJP<9XI7TmBy zX`8`C3O$}X@}hNMKsu{DO6d{~KjSxpje;GMb_i zoCI~kKF%9u_3UN#6yRy(@HEOE;_w@h{xnY~D<7qziP8y9q$jhd=vLWA8!m9F{&>+v zy%$_i|EGr@`V+mUa&trPO&(jmYBS#UUPSLP?j`IFR!j1U0tk&-TV%iCwIW>xnUrGy z_-~b>Q;D20;=fVGE4z{%B3nL1J)cV_p8v+58~8K5Pv_5#Q|}Y`bJJX!+k7sQY(7>d zS)XdLq=JPhrr7zEBtS6|Pb3M%u8ybOYm&1ag2PHB=C&hnWZlc z<~FBRST{74j=0lZIq8{cCQ}+dPnq*A25XAJrcL!&vJAyeEA}@I-7HvP2PK5t3rPWT zk^*2iPddEgq=3}YQFnTdD}5l`u(sT?p)b^+BE2Nj27nzpYT2TpmJ2Oe>{;p_b(1i3 z@;u0TXAy=TC+h_rAz4pQMALp z2YLG7)cUH%`YJ*C5KkYPT3@ADU)~e0uU|UC>r1J$3*Ni@CLIZUb7NED*GlWF8tbd% zgzKv)bDq`;S;#6^C^J|!z{lTKT~xd}f!mK{%hoUiRscMs9G+3x{h{@>1{hB1i+K7X zO6Tiq4ZJ!&IlaE_+P=N@?6cS0j{l_fwMO<;=sjOw>+rU92fgR(s~zi$MrEAWIzF|& z+OfU}uVo#Oh4}hvKYSSNnZb3^KzFd7d*l#n5}vr>hMudgdW8Lr{Y`Yj0}b-P7X{~m z$kUMkqg?rRo(G1M$Uqr^!#R`Z3%`;R%?C z?P_h`ol|BX_XQT$XJphb4*16HWjVXgkl%i^b4}Q1si&~Sks&!HtF>o@qa`@drbgI< zHX(dSZBA(lW}FCwQJQH=)oo|h)}GZ?yH;PPX~-;S&&g>o$ZXKm>DQhi7mkYSq}y{o zughkk8|TI8e-v0wR)wMBcPD?imzAFdKl>X#_x<<2{@4C0WP03xxEi*N zVViWW#q5B5E5|1?^gA7=A{^;}%T!ZmAeXH7oOYULy_fy`H1BEn=M^9JjMZmm){l9- zV+{^R!am;HBdOTNrP!W>`k?$w$@7=7W>wUV58a9&_B?S?c2C8 zc!txEHSP*DnxrKECJ8rkZOx`^ZLZ$zOifI6)OZUAJn3nzX{l*>dW%z&q;XVx+yfrE z|LmCD0&m=hLDLhU`E`(${jsO=k&qr>w>HaaMCJCt$44BuMk;I~2MA3StIA4NiH`|! z;-`6Z%OfwpH7BR_1I64Cc-6c>0UOG;O_fDw^!J|;Jj?6-Yj&4c@`d2-%5J`J%N8~| zp%I3?v<}lGUtxSMflnk)(au8tgZPvtq)faoq?EPnB459|TH5!xN;Ag%f$;{r9X7!J zv5ZpJo(ZpakJl@%{lT>}c_VIVy$6{=D@(`9Cu1YNOb{1gS~s{rkYhXOF({LaMG0n{ zaOXxg%o%If7sXrVo0A;*iaA1X?ucqGYo8Ok2_y_O55~sTMbCF-S2@Mx=0RawOAGZD zY;JvMCxyKM^%iPT10rwXJB{7)0YYe9F?Srk^BwJmO~oI2P>xpJy&`znN3{^3yU2#qAtF%I=d+Y zQKXl!2=O!Q0DC6C5ndDi469!a`rw|z8&5v>pF2faLbE4X4$S7=3MIR-Z*xi9{2Mfx zYI8$T)me63{$6pV0Fx(Sr}%{~M{jBfxR$syIqSW>XOzUwp95be+KkwFYJ*CjtIbGn zwq~*@KB=1d>U6a}FHN1&nyPgV_;i_Zbn}8nSCXDW`&4d!jgc^5LrD%+Velpvjj=-U z$&Ji&`sw0R8(FZtT!jBvY*U9s_^B1I57n~~8VR!Om%P?ezsQn!8YkepHq_ZPpDJCdoF zPkygxA}N==n96KImGBC2W5E-#0l$2{J?$kpO}^mh z5&43NAr?2Vqq=&>z^hK@E95Eo6(?INt#=rU`A=zVTpe(U6W`^vH#D^8g?c4CztfD;uYbXBi)^4&Hc>h)21#jt6T0l!ji>*v2S?`gwuqJgelP4;kMdrEL75P;j?!Z z1`72Pzh$o;gMTqSJ%sLrH`wDKz7p9tKxZ<_uh}6A7F8AaOep_cyhA8w-~0WdLfG#2 zqn|i^02Ji^o5vbPTnqy;BR-KOgs#k#B7TDqci$j38~c^m!G0BvKC!Im<)T`AB+QD~ zhgyjLKtDAq`CZ_?7wDD~%{-rd<5$ZL6@D=MUctL#U#@sX`-*r;#3}#52Us@krn+b1 zmJp_qg*4zJ&1DE@dE5?0Y9xX%RBMh({LKoMTEw(V#NQ^1PYZJD;BD~{Grys0*s)X+VN zZ{jNj545_BDWoe(F$y(CwZ>r3sEueTtQSK|kQgyH6|x=h_dwVM4ai2UJnF%Bo#NN* zp&fJq^d4{$CaYkDaPT;#E2v-(u|m8+`IWNk+3UDND3xa_3J;@P$pVGqV{SLDW4_Lc z{o*713+I4xv^R+MhQmFhH8@kd-H>`VW(ax}!0%J*E_m(Wpr= z@qRo62#-o|P_2>ppa!uNu>Pv0VhK?r1vxb@jio)hm8T$Mi7MRgEL7(1sn?uOYPs&es$s1?YDM{H?$mEB3w7EpHTfH00!;cb8F|V zR8IU)sISz{c(||Yk>3)UUmBOM`5d>j+|OlI3z>+{e4!kq z;1aY!g;r5{!c=y?xiHJFPPYmrLcN#&ZLqQuq0V=bbarN%H7z%zqYXcK86EBa(#?Eq z!)W?p102on<(lRHS(|K5npKzM=xoJLuA>u&J2W@v0z1y5{TBSrxeabX-X;D7?vf^d z4J>A|2HAdgRLI7h$TlXxKMUwX!`-cSH66$}u%FK~TtpJ~T4eq3NJrF#WLoHA*oiE$ zXUVe_Y3wcCL=lB5D=H%Y&P9$YyS>WcsL05u=r^#34PswlED#*^`$vQLquf#Hz-34~ zzN;X7kq5vl;f=?HljIWE!x^(9F)Q5a79l_)AtLb8S&Bg;3HWJ93ro~7k;1LsHeZ(A zRgz`#8w`F+R*B1=xjKRUtd8%pV!xyAA0M>TfOua<2d;S=+fXE zq^H>kHz6_BWScYWAVe5y8e9-x8y)S+nz){O37La|#|;5kW$-;bsHIH6Jp(-72MWeD zxZEc{*Wi*bNkiOnp?_RFJ(|0BbhmZEM?^?uUKp47Z02m+ORJIltRHq@Pr;#Fh&fEp5l;OEjOdmn-EA3V7JHecxiquXO#3qnZqr!dW1VnWj};FQZ#_w_ zQhRONAfjjd5U7y%h zGkgEP?x-)P%kS@+9+gn{1=)zq!@gxlFn7Sg4%p2k*~s(g$TQD9XKi8$9eek7>=X~M zHR44?H_njtA@^IM+#VIq<08SI4;218$6FwN%pwZKqwL0j>#M-$ud|4Nc$9ORis>?C zDkXGXD%jt$)qD5$?AURIcsYCPxq}CvyM@l!1lcB83;PCIa1IZ0043@zw&>X6L3SuJQ`pTRBDARPY~G0p;Adon_HEDRVxTmH{v?>?d#adhQ*868st7j z*2O+WY%<=)VxrO=@*mA7d+xcXk2JB{+Ap}E zeaFOi7qsu(*WXnu;KGB>CaZ|9-?2JQ8ofgV{ z-0W1$o}Cfd=|U22nDN;;!APGzRWp;NU{_x(Xfb}WjyXaKOJMQhr)&;x+J7RFm)hy~ zpybFQS9xrqTu(gl_~YT6EdJq#AEr@o$YR-3!a1mqxVAhYLFut6zdQKC!51#rw=b6Y znMZu;*=L`5hG_Q+90GsJo}sokf`keR zUsk~0lWJRRv9R}04pmBuOb$PS5_oSvkD=+TryOjLDT-=wvBr7@lw3?bzN=r|vsah8B zFZ1R$Wo0$x<~3zzHa%Ur`KGb4Uv4fh-EiG#*XD}KlFH!vbH$(9)3f`EJ9m$}eM|Ru zbzivDm!v9HsUNF2t-fJ>Szujz`?_FrW}C;`>d0*M6t*Jf58`ouC0q-hT!NBt zbVB&I@F&PMHd)oNIC^{(B3LtqrJ56+UtH9H$U}uAbd?d|8As1W zzn!in1%tuhU_queL#r_b9gd($qqSR|pr<*L;XbHvqpgr+|JiFTM&NAcn21VHorg3{ zNmF_(c5Dxv<5lB-IJOrD8oRq27qZ?Qw>zizILNR3+0RxT3;Zf}QGIb~?b-J^tX7OW zd}}hD9PvgVV?*CkCV7*WJj&n#EbtJ#5qh8B1)fZMEWoN*%&mqyeV$gE$Ii^G&(5L)m#pmi%tBmvO-FLB0jgf2?I%46NBg1EbBWJ%U}s4G(^=lB zijL5EIh~oK7TA)=9>Mwpmw_!w-10tHksJk)gywYW4e>U-Be`vPUtb?PpESWxpWz$% z^jMI03J>9Yz{Yn_Ru5z<>2!diN-Gs%-M2hUft2s%d6MMb_zqV>pEoN=akJ;Axt(=} zbm!tBUssksS?7+HBN&I>S;W+%14RMX@L<2Au_@nb2?kiEYkbUM)CVeQ6w!tITJ9?@YD;3)^M4O0--`e{4>uyGx8Mv z8+nGE@impBFB%EpwY(H-GoAc%&cbN;Tp^PlxLgV88kivu);>1LD>#S@f;`;UwtrPH zxN3h}+l4EG6X)WbBx8GZ>>iSb83~lC6)5ct|-f zvY?=}AlS%ODjtrRlU?X_MMOtEsDNI0Ih4ca0KZ6<(_^iXhI~o~P1>*%Fsv!+zr39t z%p~1(rm&b5T?6l+*=_mUw?{#4FqoTLjL4VM%IeR`$}V!$xm>gn!nXq!_b%LA%nFbW zkYdBUilZNe;$i43B)I5iytF?n0A>C!urREAka}*#!2Ojcft1TLS>T#{fYmw!teoOv zQFZ2pV@rAl*N%^94d)&QSRhLRjLo}ZKNjUx%a*QQE8;Baw%9JZPhgq+TFArMJfOlU z7OlvcERi_k)H3BZVVoh7UV`}H*G0pHEpRrXGMTJ)jh*Eci1#^+_S)Qvc0*mjHu1G0=FccK+5?7ipV3#KRoiVT*4&)I%A}4B z#Wap?>`3m1t&c)c7fH|MLXGTk`5>Na6E@=cWY#ddh3)L1@JCv`q(9uvs)g-EFo0sR z4Z<+nD7;N?${^}9-OZcN7lyZQr*|i(98C6mp`B&QouqRp3)8J6ewR9*Wv1urbouGH ziLVpd({j?%^3t3ZU8)6jvP_iwd8nLQx}59|>3LWtnQ0j4Ccn_`gei^1iION!u)Ga8 z-<9C(fRWJK=Wo85gGv4n&9XBl-{F)AtJ+=h`XB#DCEQGuk*1yZ8R_Lr#0x=om9U2W z0p}-7X9ZDSU>&2g)!<V&)$2i=6WzLppL5pn< zlWjuHZ=nX_Bc6Pf33er$&RuwJ&(7AJRQi-tj>!XjHK7fW=jb^Xc~-C94hVTY@+LrD z49FFLd?QNk5&GEks2!)p7-O;s4bx(0}hd@K39${f6;T7}QY+gYaE~%5y3ebHCOdOZ1LbQ_@46sfs&ipUc9yU8LpJb69Q7iegwC?0c$sM}*&iMVOe^BX5i*u6Y2B;A?0Y5yChm25|1 zT3TYFPWK2tiKyjaZE})Uo0O~#|5B^scf3{N5womFl}aWU=BQJ(Ds^gPRGRqRI&(^5 zq6WRyEP!zw0Fwo3faTbf#0kmW2;nKW^R==gZoA#YY{ zVL@_wqScz1k~&wJJ8wSf*)!>4&rjY-^NcaE<#V$}H%^ThQI8Uz!{I9t7QR^RH`(nb ze>F<%o`n1~c_Goc9*O7P5ey=I3A>E_oBf^Ai5D+{pWn;i`44XlY`?sgamq+<400RW zPbZPY%NScOMDQ|j_B4_SXB>?Ap7!HiaPk2mS0LLKctOZ#L3V#YtgRNVo*1Jw8=~)! z5I*2mf*~r1XNatLAELQoeJCt$&DNpm1cS0$?n2b_w5uYJY=LWYu$%5J$DJ?%sa=82cuEuV)Js^{BTDOAsQ$EAE9N_j!}7?k+;$n&32N(p*; zJBRRSC}j^)p67EhK`FrLaiH;+Y+rbdjI)baaC#+NjdNUDJy;2&PFCb(%=@_)rOL1p zK9mdLl~5-1H&6VsT3&nfKEj-v(4)JlN6MN|D<27Gu+_p>$4g!O0u=m6onh9bnoSxm zxytSUC20e%22YWwqNE)P?OIuUmdBB2vLtKb=fp+DsSmlGwl=HY>{F?=3Plc#lLRgm z-Y@+1zet|DI7QXzo=(!~l0Z`-dD2f(=n=m}5x#>AIE+)b<0Qtgyn1R_R;An*f=jJM zyfBO!m-0=Qt;RxKyO!Q@IZt{=lJ)ziufB}G3d>`U&XUK@>p!n|N9zt@cznE#&ax%? zA>|3lW0O2b?|I16+B%Lr63uz^M3PrUpQ1FP&~7h`C{^?cecn8^PN&ZE5?ow*5DpUM zCb?_synb44TbG_~O;9C6Tq(#*&$1;zT?w~S!gy+`Mx9%TZWO720CNVK7E-5)a#u9JIFbX}oxheBw;-W2c=GyeBuw)is0lt>~g3klE zCE%Rmkh8eu`p^X45T4Vux+L03(4+KA&|HF9dK@yx4Egtx)%~m6S5~i_l7DZ2?C=}p z0Ux5)!h8rqjWf_(Y&V(g#mzG%SSGs~GT`q~g2Tt{9+FJN#<|iO6)4!UWP>I-Ixeoz zX;Y<#{tC%|fs!OmVtT3S(D0>1OAX-3F$vx&7&;Mk4H^f0XWBRfAlQ+nZA2Dypa&YgS$m*LjZ8{%@g?9+NN z&L`YDL8lbhQ*^R3gb8|E#`hGB^MuY@Zb7;IxI6z8dpTU=eimSt;wBK@lnFh!CCo<> znztGIfj8?qSQ*RB$jDx5Z%YBqtFrbPWp}9hf3|kFy8ThmJHG1V4Um~JdDsON$+n&#H83N! zXDV;GY#HYhec&9^HI(waSnv!n9hRQ__=cKG%U z@6y_)(o0d>IUGXbX>Ws0cOr%OmBi&JrE6BoCrH8chj`j&fR*y}gi^p!0q28Aftr%% zp%e{I>6=agY=1?b{uwE6$=^rH0Ht7thA{^eahq@zs=z-Cs_wdr@Mj34e?O>GDEWoWVF=`EOpDP&)v|M=U%w^=oiAkBDLsC+1)WW zzMwhR-IO&i)zoWFR#z;os$Jit{VCZez^G6&agKDNz~eTfAEb?!RY8X`ebXXr@h<^k zGLo_j5lGgJ=eF)9i}CZ@T6Zn>coy$!^^cL}52oW|WF;}S>yk^(DjcOC@HiqE-QL{1 zebn7&uV3WzFCzPiMSkC+dOO+---4jXdl|%8!)Iw z>&QA~cAv8(yO|&AcO*{k^TC_t_#iro^1t+7yuUEt*>5kh#byRG`n~z9n_cin2YM2 z!?K&lahR!D#~E=FeIi4@GpxL?yz)vx@spqYWTxs|vcAO?3;ZLQ0&xxHh~NxMm~_(E zhgW-~Cz&5#&ZGr{>f{tbLSB~}c%?G^1E7J2aW<*kTr2#@;VSi>iaa0t#pJt50+ zvPLG$aWnZ2;5Oa-3edpvQ!sS9K@Q&LYO0;K6y?(+|FqS)-+qA zeL=c06f7F?ct%b+`vJOvOmjS0M|b(J0d%Z z(}s8)AZ9?YEn|&rBZJ{G`w)AhNnx&_7ED4mqTvidGCB=jtab^Pz=r;J!Xv_S!rz6% z!V%%9e6Bo6o{pOhh4M0aqr6AHNWNNrhJ1&7zx+!1FXVU0?~^|&e>Ea8LLcFXa7UCz zG(>bqTpn?E#2+G_i1N^Uv_|Gd7Dv`cc1JFXJSB2-oL|zklYvgYt zAC7!8@5A=&3lu+9+^2X<@w(yz#TN=uIS0V(IF?ZA4o9Es$_m6X*n)|o8Z_WMJ z+^^;p&MTYOIIn-+;(2T4ZJBr8yo={uHSeZ*znM1~T@u|IJrKPldTsR9=v~p5MBf$t z`{>7`UyOb|`u*sCM^DU;nx8OVKi@ImJ->8*!~E{~qw`nI-#CB!{DJjxy^+xq}^#$sms&7y~q5g~d4fO}=FH&+-ic)G) z+EYeSR;HYua!$&=lq*uMPq{1Q_bHF3yqNNO%KIt*PMOd|X%aMP8i&TMDb+M+eye#z zGnty5>PxLmZAl$WU6#5&^-e9*&ex`B&03c>pgmoCj&`5+3hghncWHmGeM(7W|z z`bK@Peq4X5ev5vW{!;z5`rGus(?6;|sDDlWzWxjSWcs{xRk|rXFTFUuK7Dce+VqRl zuTH-?{mJydr5{TFDE%9Q+_1oqYOopd45t`28x9z5GyK-@u;E$5D~5NB$ws3w%jh+( zFm5oOZQN_T+<2q$PU8c{KO0{#zGggZJYqa*QkvpT!=@Fc4W_eAdrjY&Bg_lT8neZm zW8QAQ!2DD54dy${_nRNHI4nyozp%VxnXpD#6Rdix!&+!9vo=}xS}(U=XT8(fk4>ZY#M!&Y(nTIv`|9|38X*@p@Y%|DWNH%G%Jb? zMO4Jz6+wLw1Qiu5fY>YQe!nSq_a@@&>;L(`@AvIK=T4tFbEcg+ckY>XFSI+{?nJv! z+kMmSxAyhh4{2Y~eq;L`?eA^>X!~c|A8LQR{pskC=%&#vqdP{&NB55I7d;|6C%QO# zX7r-y)zLRa-xhs;^yASlL?4bm5&c>8x6!|MsMnzd6ttvw7}{ZchbbLablBVBV24*a zywl;s4re?3(2;Zu?bxhimyTUK`Z^BoIHu$E9S_HZ#atfKI;L|>x0v*pAu(fPCdZV= z%!^qWvnggr%)K#>#ylJIM$EC8Phu{_{L+bais;m$Q@c*9I^EdmwoWHHecI`pPQP`o z-}$o6QJrHuCwETkJg9Sa=lssoI?w66wDY>oTRY#``Jv8F#x{*@89Op|PVDB`U9tDY zJ{J2z?BUpxv7g0$+hs(T>Mo1AtnPACm)p8L(B+9PFLwFaNu3eStDMo!an3?#g>$}h zg>$2GyYn9BKIb#e)6TD)zq*(!!sT@haE*55yKZsqcRlAijzW7Ks*Hh}5>sjWh@!aCM%d^*W(DRDt9nXiJvz{N~ zNqlJhgjNY-6Q(E3O<0ytlWvigy8)vb&P@CcTz)H0h(H^GQD?>&YFH6Oz5j{gdY>uSnjIye)Zm^8V!Kk`E;x zPd=UeRr0T0S=WfJExNYrTG4fW*A-nibluig)f8X8TqkfOZJ+AH%-6O6?&mNgQ zvU*JDQPN|6j}<*O^w`#8caKMUJl*599_M@f)Kl--pl9=**Yu3-ncOq2=b)ZrdKUDY z-g9oxWj$+p-qQ1~o_l*9?CI}Szt?5GqI!9H4eK?b*VJCsy%zP_-fMra=XxFLb-dT< zUSIY4wKwY>!CwRP?$x_*@8P{C_Fme1UGJ^E@9h0Z@27je*86Dhk9wc){gan@BfKrV z?Yu5;cW;JwsCS&V&|BeM;Jx0v(YxLIhWEJlOiH^HS4#JkjFjOi6H}(9%t~3DvLneMtJ) z^p)wG(|4xtN&g}vG^1HYtBg(=i5V#w12RTs%+6Snu{Pu8j5{(O%s7zoQpQ^u?`M3G z@qLEBPyIfZ^@-{e(&z?Sy^?9Mskv{KdHq5*-vn}**cgxJk z9GW>UvoNzFb93g-%srX=GoQ=+qi@H)?!LqNF7CUj?~c9?_5HB#XMMlz`+L9melz;5 z>UU$mUH$IwcckCR{;Yr7{+;{B_wU<(X#ec~W&LaV-_rl%{(lT;HK6-|>;VM><_*|3 z;Q0ZE2b>((WMGSdBL=P=xOL!51K%9@)1YpHrVP4u&~t+>4mJiy4emO4>fq|Ziw3V5 zynpaVLqdmKKIEDqU53mZvV6#fA@>hCJmln1Hnh>uu0zKTEgZUU=(9r)4SjD|#IS^6 zDZ@&J%^J33*xF$?4%;>ClVKNz{hGzH60*Em{j)}9ZO+=6wI^$T)^k~hvW{n+&iZ3t z=a1bscK6tK#(q1_F|OgbHsd;t^Ni~=ZZK3ZZyk5rxEIHLG|oRhbbRaa>EjE>uOGi{ z{43*+OmIx-J|S(w;0dE9ES~VxgfAwxnK*Fbl8HA=ym#XB6Mx9ja{A|t%DFb@?wq%B zKFB$n^W&u6llo68nzU%r&Pn%8Iy~uQF3k3MVW_T@d3_j=yD`HuX?`B&#h=TFR^nm;4|+Wb}doAY<(@5$ev|5?G61-%NE z6dWvgvEa<)@X5m_&z!t?@>_*X3R@HoE!!2lcttTy>05f zQy-rC)YPwv+ZK;4-c$U3Nz;;+B^^uLB|S>2N*0!^EZJ1DqvYO_M@yb9`K7dJY5&ry z(jBFrl%6ZQx~y~An6i~+ca|NS7Bww#+VW|Kr|Z)jPwzFoZ2HRS2c|zi{p9r1)6bVj zmM4@Kly543sQl9kcSUB!mWod+n^!t3$5*bd++F!W<)@W@RGC#ntFo&mRZXcXubN$T zUDfqfHC0=xc2>Pq_2GGqc{E_1)~NXZM~xX!f|-`Lj!AFPdF5d;9Etvk%YyVou9BS#$E| zRLz+;XUUu!=4_mE%beTiJUHjEIWNpPGUtOipU$~3=hwOV-1>7H&+Rg|+uV$~!{$z( zyJqgqbMKt{=-e0QzBTvM+|TEJJ@@B%WL~p*ar1`ETQTq1c^}R@H}9wU#{5?Ed(H1R zf8_kh^JmPzZvLA2JLd2G)Bno_*DT0i@Yq6aVZVhd7e2f2gKHzNoqp}cYhSqb>~&4A zOTI4ix&_zmzV5^#b5Z0X@1oL0n-^_c^!lQA7Jatp`^904lNR?|T(Ef7;u{vUvhD2+oheC#xL!&v}oyqrOTEcSr)Tw_p)b}omlqm@&?P>EbqBI zYkBGN)ywZ&{=)K;%fDL@vf_#rt`!+8vQ`wWn6YBfinS|lS@FP%=T{tCasK-J>mR@V z=*pfex3BzlRohh~SLLsoy=vL2n^(QJ>Z=>VZWw;UO*ibg;j7g>R{K^DSUq}m!RpG@ z*R8&F_2a8wTK)Fw57)3Yjn-Vfro)=vYx=LrUQ@89V$FgzE7xpWbMKmiYYwkDvF69M zt=A4;J9q8!wHwxMU%O}RV{2bndt~jm>&$gg>w2sky{>HChIJ3FJG$=InkF?ZYdX~= z*JRX;s2N{VP*Yknqh@~1(wfyZn`&;YxufR3nte3~YhJ23TywnUZd`bi<7s_HH<~;mk&JW2cQhH;&vmcjJnUJ2vjw`1r7oLDSlJhreT}LZz|eUxoN?sm76wi+O_HaP5U=Jv+31MZ*Mxe>CC1Jn||8#(`I9H z!_6%>ciB97^BtSd+!%FZ>5Wg^q}}AasoHSEwe$wy%UjV(_@8^Jbfkk*f=lm-$x{@Mx zA1N|YNPTV(nA|@VX>a{6LK$hPbC`p2Ar1RWXpgW?h|`UP2$S0tXbHgYFkYY};OnLY z|IdKW0r$Zj3TOzJ2$&4N(=dCmn`P1rTUZ~|<^KVo1?UrRp)LT94^9sj{aYv@9drd6?$5OB ze`#JMxrPh*_9OJK0Oy5r|09S*yi38~q}N}@7$_i7bwS@j8tBXXzx_wBj>PD70sYO0 z0#5$}pl*`Qfsau(0By9G#QoP`JnHoC#$hMogjTH>G2Uz@_tReX|0GRr{~PclE`XL| zoV_Li?OVjIMqh+^DWH6AvcmZIaS@+B0&VygU|fm1{1<`VL2~PY(S(fuPe8vJM%Y3CH*G+lfIIKM3=RF~b%2&b8tM<~ZuSP~+U5Rl0k^{51g0P6U;yIy-+}1_=(<~^F=6ik|KGH$ z{I^-47owa(qwgY3g~{y_LM*p_I~nmG!5+lB6#QNKF8)7^MZ~NNdNc{wyZF!kM-T$Ot}eJq zAAB>+=~rv~{I}fJxTa5{y#Mm})P|w$yaIYFa}2aB3qL(xm>o2){~OwsbktUo5!yx) zCQNP@;Qvv%`xwW`h`NCG&iap_3F)lY0s0{LjhUD?I7|WkSHheOKKOqCMzP<|0qb*) zo`@;+aqylv98O$L7y%>4h zK)Z{+|ptR73$RoGxY3xwi0UQJ?<@z_{{t{?BEvb*&(SXJF_-?gU@J-1>!D76HU7B&!=#h8n|tu0@$rKr=>l+eH0Q5?^1f+>qmODoVVwROK-@9^5j=``mx8}ZZ$Lv&UC?)7 zz5h?3Za;z>w_&u^c4Geh5qNP^LWlt3;}LUWL&J>tFs zvpe$n2!6Xr1G*J{bBPOIl*U7U!_^w%LuL^QjT}vkz6kG!@Drq~;U(cl4Ez?8#=LC( z7w|1pNF=`5Eurw|claA^ zaE%9);LD>D@EBKf(0U?0rwQt4bdmY!CsFUuQ3j_efHos0ryuCmfrCXu^v#w58gg1G z;B~auI2kZ4dpYJz_J^>kTqiuSOZxer_A~ORtj&Mj>eZ5t%@7GZ$Tj z_P`z%^lT#HDp>&EtMmN%{#1aq2etCzCh`z6m#@UVa%StEkPvbCukH`M56JHPXZvL-1 zpdDlMt4RoW6TYt+XY7%nFGc)bfCYr2KaMkQM}GU^ZjSg~*ne2=*g@i=gLalAR8o))cy2|Dq1;WU93qz1q%6l+*Bv_(A9G0-^H z*bRD)hu;&x@oxX^XiuikC;jzSBo=+S120p%02$-0WIzChBW`!Z9NnhZu?sAr&0=(E9-seKFIS6nhbD?Ln}7Gty#munl>ffjtv_zrZa zaalK)sY2GFm7_oR#MtC@zJ;`dy^&sowizMDV<*_J!uWQpcqZbRe;Mxp>4Cnm7SJ7l zZ}!m^2XOlR0OGp-iCYvt=;JF%H(}7RtqCO4tfu?OApvXNs87Luom;yQ>f!a$i*ju zM{fz~*HZp=}BcheaIjIO|*QN1sEF@;N>f@Cgeb^TMo%g=Fh}ghI<_13=fGp34WOH~Kc~N*PEr+2wb?Dd zx@K3z;gG6lkbW@R8aJbD*7)BSvS)(#NsC7)2%mW&%9S;)|aCX zeNV15T4Rm|fIpzxF4CE|GjDG#2K|e}JD{}_^Z|g5PWmw9#mIy1L4CU7g!={TEgPXf zBMr)00jLsvWfb%|jDl{4QRE}C2j{-;BQYl>LEmg5 z=?yo}(*mBiRhDI1VOH6ezhx5e5filLWIVz5r&>Jm(tE*6^7mnUoLl1^V_l4GjAP?4 zcmY0+u`lA|mcI)&UiLG*=8g1^`0YIVrOw%CerlTP<>>2(qx1G87@@+{#$&xpfIINp zPIP~75;e#27~UVmeVUMqx-5eZ*TSQSwwZ_POw3tP;_S5!4+GE?PhbR-)<23GnQ%CW z37-a}3v{B**B0Q8(Hd=s_JH=Nb_n;9oYTJ2TR;z4XFX0&)JNmI^#T1U{dN6)gBoE* zL!+_L!f0be8?lDVNHR7UcN+(cr|FgS8rqen(g`$|PNh}!dFp3P*ww59>k=6f*)a0* z$g3k;MYfHMiR==Y7}+f{Gjdqun8>2YGMq(S61gUFedOlITO)tLDbt9k%c8D~!i}s^ z?V_AfaZz2P(xQe&O^ccpwIXUq)NN6BM%^8CU(`cUk3>Bl^<=9it(vuJ-Kt%ym{zf^ zvRaLAbyI7-wb{BMt^mEP^_8t7TgSBS(|Tg-{I)yV?rr;M+b7#T)ApsdueSf<&Q%w6 ze09Zt4M-GLg|4`*jniSfc0cIwy!JM<`(4m})o}@c?o@PmOh2f*~#ifj|vF)}tXA+l>^ zN@V}Y?8xzv#gQ{37e=m*tPynB9_0WX8b>vcx+*G4(7_#*tmse~wGedJ6?F&ba8J~O zQ4dG$k2(;bLr2hISgSFu)`AWUbZ7)RToIr{Ufb<$A8Na=?SZyWw|xb4 z5yHPLI~k1mPi>#<{rGC0=F=`#$}>NHTJ`DXPn(>1_0z;N8}KhUgZcE#u`};mG2nXq z%oAsBKT{3c&eKnxevFXQI{<3|n9onoJ3amMz|&4bKEijR@^|XOsq-Jq!;m;GM=9Q9 z6D?A^gNGUujY)V;GG^dwf~CfC<5lBz<1Lxac*{5jZBL&XUmHJ5pFcm1^Z29(-=5Gu zbUs~3ucOQ9Z}_sH65kdq#@7YQ*=n|dZDKdFEo>{kG}z8|vIp2f_B1=nPO^{Ld3J&4 z$510?}F#a)O^|82yI*rN2s~ee9mm;2satcHT#*3 z&4y+ZGYa41=(t?yVqLtBB{>~Ddr?sjsGb~kv>-Qcb7Ako?$$ffQB-}5}_ z0zK+owWq+-yac}W5!@AbnDo&OfiHXu?zUdq7TT+Zwr&Rk9l8PTlnf4Pn^owMs<|ngtXam-H=bxuDaufEci}X;k zS#L>pLGF4Nc&*)dk9oK5A`j?EWFPiwk6`bx7wh5EdLQx>c+7*~G2g>}^{769ya+kV zaeWebM;}Ly={e*>ydyoMm*Q)m3)p#otdz znXj89L|cP<;RfSG&O0Pidy^DuxZz!I1J3?6aF^S#``Srv)!RXX>?U%Vb~CvDCrMB3 zIg+iN!W)6lz$G;%H|kO39zBli!5gSoaAN0Q*pWYq-TX6pCK(FdB&+nMWT)Pd+@N1Z z&gqrrd*-|5QS)E;lIMh@p`(HMp?S=F-#l)9;0SXxax`;X=74Hp^HY51^CiCY`N{m+ zJZ+vbFPh(&=gbSVn3m9uw22dl?ISr}`G^`jAM%r0ZCSZk~!*RZzu zR;dH)$f8*n=3()y0SjjdEQH0f&dkYN_>L)tbz*U>8CKvZ)}C%+iF7kdqBpW+dK2qP zD_B$dFIG*DvYE7q)u&rnH+nPcPPeii^cL2W-pYE>ZLBxl&b)L7OQAbiD&57>97T@l z^mdj`?_e49PS%Is#WLyLtS{Zo`q6t>e|j$)K=-hL^gcF-KEQ_12iZ{i5Z%kN=)-I{ z8$lmoBk4XiiayFl)BP-)4Wo~-G4ydZmOjD8(F1HeeUeR}2iZjW6w9Gcvq|(BmP?;y zdGtA!PoHN6^aVB<>uDi2+LSJX4^bJ-<53yLb`*Awu0dSNrf}4DSxHL#yw6{r? z_8uev?~<|F$7C9IpXJ)G(8T;ZnWGzIE~HrtbO*FuM3WuR)N!+Z4cVf%1_$mT_vr~_ zzup@Y3{sEgxJ97>bWSuR%k>EC6dIAGdN?#PTuC-S zio6k$tWD4yy$$n+Jo8%iBs*Z>? z513Drhp^ux`!)ZP2dH3Hnvx$mw@X8?j`6ij+WGC84=B4%!m$^W{su`Pr<9%A;xKZh z)=c@E=pjyJuLt{3We>60L&39GD*rH>y@AH(aamS4pmm7L21cfjv$6H^uqP!O%2WMy6qxQ%;Jo?iOO*<(fI2*ekhU%Ysin z%mIj1im(c}%JI(;@dm?IhP67MngjjXy;R3!KWDmJixfV7#sC5qV<83+#PmX6b+#HW6 z`0;YRWQed=f+A6Ms8fNu@V+?>DJoDRZEF4dCuesrnup(wWm zSeJssgS=9Wm`vc71FSgJf-%4h@t=Py#?vK!*u@h_SYOd}&A>{J@@ss zP%TVrpoN1QYe@EJ5n3ayF~)AL))exPwc2Iim)nuw@%BCsw*p@dX+wMM3fvNXrPcx$ zonNg*V&vs(Eir$z!VU1PwQICCT3fB1)}FkoMQa_%Yg$JwM(drP$4TFhE*|38?KGeMrt_Ore$kmw6W;DZgN-~ zM~-OYwF%lpEk~QA<>F*@o|dl_khitTxR|9#o1#tCinS80R4db_Y16fGNQc*9#=Kvv z(q?GYn49CEZ^nbQYo<0!o2|{!=4$h_`B=vmXbZJ#$!YC6auMHzFV>c5OR-;A4vsld zyIxxfrE)iDtHCKIk$1p{uOt7`YQQP3Cr6=uZlksdeEE&w(YI(fYg@Hjz@u*iuf7AE zV=}mDzU#Oh^K&_qMb=iLLG_ot8qJfJ;@oylJ9VeJuolm4i- zUwaJq(mtUb(4GXJ{}isKdPaLzdk*q~7ql0(m$a9mhv!x8HL;gDguAQ{Ye%%VAWL`$ zJDa20yV^1Bxb`0QI47|~_yBu^Q`jYZtbGDm!x`;U?KACjeD{795}5P2)8Yd5L0@yO z9lN3LwI8$}aaYIB+ArF#+C}V+e#buBuj9xJZhJ6v3fYgTJFwpf!G0qYJB|i=xZY5Y z&>KM`bQ8TPZh2^iy-0ItFTE1dovZY#A=zlDN9nEf*7(A{4fZDOuuF;7J3!tMgMC+L zvGa05>z*5OAdemo<(Y|klAf%0)w}84^&YqjqLGyW;nS*cfH zX6y^yVKZv8y z6_CTN#5=K7`VC|t?z~&0uf>V~b&!Fr*Ei@Jp~qn}cCdr>8_5v;CVh*3v%VE~%ifCn z1-9c1`Ap0nv-F+d5O?Xf>9^~5=y&2|&0PI%+@pFA_E`7od-VJC`}GI(2la>az52uY zBlUCs4A-B8yy+?ZY5f^!J9rMK8=lu+02grvrwo46 zU)EpIU)5j3Tj&*dJ3UfAq`#>j){p3K>2K@r;AV&U`oHv}WFb!QUyDtubrGKP3=c-AlV3;I|3*U+)> zE#3is2QKw{XcziX|4IK@|3&{*zo`GF|E~X``ymn24BapwA!Lw^IgENn2(&VULSERw z2#2gJ0@AX^MiZkcG&M9cE;pJRR~T19es+~{wGnBwg!YD3Mr%mZ+8AviQELzBVh5um zG&*!LIzztJ#lY!q!wt3V9wQ!F9ugsUOg6e2-Hh%=52L5i%jgZw4=F~fk!JXebR)y) zV`LhAjegJuF~AsT3^E2ALyV!uFe8gRO`b7^8zZ14Vw5r3$Tr3pV~ugfcw+*jJvor| z!5XFv9ScwqGga5tuU@PRvN2}8=#3|jj`5PXVe($At~HwY%(?*H$pqb7UO1P zt8t5QtFg`4ZtO628oP|!ptIr*<4)r)NLF_n_ZasYdyM;x`=Q6;LE|A~uko<)h_TOj z6k07FGafgdfb{iAUylA{+yllJziPdY6S-oK#GTt-}8%H3u zdfRvhlDeai*BvvCLxObzvb*<<4~!3uQ^rTe$Hpg+=beFE>oenX;|t@gaSjr`FO3Vv zSCDjlV|;6TXMAt`VEkzOWc+OWV*F}cG=4LFH~ujElu(W8)S#3yYElQSM?+|R8cM@x z0~$^n(g@m!Hl|IWhvPEZj9yNg(<>k~Zb7f2SJO!7=7^%LXlqE3+t9YO9c@peX$R=* zh@qWmXBtbpP$zX!H;tnn=Hf=N72zVn~tGl={Pzba_fmS2Qu5-z)ro87KzZi5od@~e03;eceVjf4EhbOWgY+r-G<}9XOP_<}{RR3WG@HCkU!kwk z*XZl?4SI;aNe|N_(01}ReTV*w9;NTnWAr$E4>GM==?QugT2MZqAJS9wBlN- z%wRMi!3bgXg%qO!WE7SpqX}yYX+|@4Icv_YfNZ=4y9#d#B3Vlo#agk}>>AdFwPo#C zdq_AsK+X{ZX+~#AH@Yb4Mx2m(B(Ox5#FAN8){S*%J)pa#7wgTuEQO`AH0ERJEQ9r7 znb7FckM(B**g!Uj4Q4~wP&SNZad`wA$wsl!kVA}NW7#-1o=spASq`+n+Fj6gHI=vl2*d%h)tFot3i+$Z)IJ3|7r%vRRPk&S7)eJT{*#U<=u`>^imx^4%qn z@GgUlcLlqitz@g%4UqP(VQbksXq8zHx$j0uem6t*dlRI;H$(n=3nakXAOqe3Dex|K z8@rv|!R};tvAdyx<{ox0+r#c-_d`DXAbW`IWe-Cu%|7-h+s__jkFzJ(0qmg!VcAA}GpR&)`=j;o1mYstv`b&0!eZ{_J->`4lckFxi1N)Kv#C~SK zuwU6l_8a@1{lWYuF*Q>+4U?LTOJ2=-W{6oInsdU;24*;qs-A}wmHTeYmPI=L(|VhGsm1{=9+nCzFA;ShODE= zoC4`{F=QU4kYr3Vr$Yu^AtW3#AnTZE&Vn|*?;-7&11)$z3Mt2Ya{=Vc4TZG)9%%a7 z1&PB>@*LiOHo)7s#-tbIBG*D9vIugKB_x!D;SJ}T}<|^_K z*$b^htIahe#as(1d<|slFOY}HBV-?Wl-x~rlY`_byj!dfiOB{?z&Alsej}vqTg;oy zt>!I|sceH(Wd~&9yCC7e9degD@!sHWyg9hXgmyGYU+#kp<^l6Tve z3aS0G=4X)Aeqo-4eD*wKwinE=AhZ3({1(#N@68{~A0fm2+5E-)6*Ao4%-_vFOuvIT zG>7gm9Mr)aro-W=2YpWU9ifl_H*kbQDjWexaAQXk=y`#A1s?YPF##?jW%&e7fx?dag>=!kK2 za&&gYI=VQV4wu92h;w)x@s0#Xq9e(X?C9#~=IHL|;ppk;<>>A3I#L{|jx>kQk?zQV zhN(qnCY11nC+P3nCqD5 zm~VO~l|%P|qpGyX<@5TanUbJPr!w8j^l;PZN==h)uW%z>s&cDz-X!Jriui7)%ExP& zB0s0w>2`Qaa&pVdN*&&^$z`SaQ|o)ni%KWw!;?GRp#X8=9i*U0wa%^mYai&V3e2T2w#`asqk`&dI9fv(@OP7>>nVu(-oIO z`=c5X*FaQ-MVu<#Bm0Ld)oTuvSq!vg;fa^2J)%8O`vk*RI$2UFHARIbnS%n=botU$ zQ>Lkc(o~bCsVveY&TiRZoNkMVaaI>lT_n}V1{Ic-m&!uY6uLeuHx(;Q5!2^vFsQH! z-MPG~q&TNaR2n4pIR@GLM!ZTLZ?&d3bWlZcPDP=u_j^>%9@&xI?o@NAB+O77Vd7OS zTng)Wt1DO~_{?FpOcO-qy@^SRW(f(Q!|F7u+ocGWpt4L*StTg{WQmX~DV=7a3z}Ir zbYxto+oi~qpem8zGlvJ-#_39n6HVvF*brmIXPF6Xc#zhzce#A&3K5@0dzGor*I@Xc zX^n9=+)nF6uVc9E#%wsBXw2cFGs}V#lgyEVJfS0lWC$6VSCn6#Ur|(Hj+|VcGb2BA zbTCk+OH$39?lDJ;DAGG!wMLvGS$dM0EwZnlU1uC8yCX71$b`PJ#bvq01N*0$-q?x? zrz_Me{FPl0P1wT(rMy0yBPGG^aM~SiyTcQ3@OfM~BD}#W1wt{a2cz;S-X3Bv%bR2m zk$7|b(97sA>wxO512s8_VDuwyL| zW|&%2q=O^rN)6VMFAyTcl(9qQ0wlab<-(!tqL&9Uars=5IuX805;Dc!PN{aj-N|mp%yJ$v=mum zGhO(Z8Nvw72zF7dV0=9m{t>p-ERcuO<(3t07+lu^fmo}8q~R-^2r)B-;mGVO%uqRM zg#EI*BS^&vA{3E%iN-+}lk^JDtVLTnz}&Lo8fFFttjJXcrm7*DS(nhM!2yBn71h-$ zD$}c_RG}E`rlzb{gl%mN6v!7;;V=gT{6H5;LUUli5AhUYa_JNSp>qASMe@j=E~|pe z!1-*W9I^w{!C;;HPxxus!nHMQlMtj0x7fYHDbmM#}&$!vpZcPY1EK;Ra9 zCAzv=&%KJz^jf*ogM8#6D!P+>cx5OIo%0MP_9U)0acx5aX)SShNysS52>gt*c@QWbvA z6uoGQqexY)NNsS60z4r)WMb%)+Qk7cnc5YX7&disd47IraZYJoQLb4m8q_Qfv;}rM zsk9iilcmS0O`a^><%?5%wLscdrKYp8AQzsdQ1+=ll%`^( zslMiOH7NTt7mof~X74_@Q$p~s@m52pgqHnj$D?xg$iD7&r0>00wv>_p5VCGiD zNmipHK~*6k-K+|<4Av*Hzd>}>5F$nbLe<+9{og zRBFFdCH6a2fxeCPB-yN%{jNGlgpg`$CO50a8W1`w7$}=9Ni}wQyg5rmk>2U5E#efx z(v!{EBK!KY>kRPZxQHSVGGU6f-)S&8zq|y)Xi{-SY(=h_Bd7Ts5lh(x32jrnS27uL z6-kl2k#e|f(NZKkphALN51;2nw1}Kw1#RiP!Kj$>j&XiktfbL8Z!v~tan4!Qf0 ztFI{|gxbPG<`6m*PJf}!V$vPgMc>{fne$Ak@n6MC(#cr7E_~0;wY2tWXc>u_*5hULgjoE$k*vg z2-23%Oo0%rUcnNU{^bfn9M2$eI8NTdRS=wo>f))G^tYLwZsd^!0jd$2Vc24^E%uE{bZBWpsT zqo7CxI0|I5QcOmx)H^pe}a zKDi(>HMQh2KCvK$HErb5NpRC#USh6le%KW8bHU?cKo=Ktf=c2!c~!z~tS_7mg4{1$ zG!G9mzp@ZUh-8MPjZ67qQ~3l5<+R#^^T&ulIf@|x3`4AG4Sp70G7mnb<%ZXq&{Xbx zI#X6YnOW9g6Z478^0it|PGY%Xwka&PfqeqAGUp@+u_iCe)7Gw{#K5$rBJfGevWgM| z6PA^OMG-k!317~Tt7M!fhm{ua2ln~7A(O3c9U@tE_=VfJbvQYhIKvKWgCNhY0>Fh^ z^x&L3d?{}-2j}2}QG(TS@#4d6tb8z%NQ^O<8;Ys2IKQHT+e0MB09%u~yn_g}un#Y? zuSlxeqML;k1?L<^BsWJQEU-y{Lv{r&>_CZXPP06%VTYFG;jCy1PoQ=W!WtMkA#x7^ zPs$^O+x8ILZz_*1sObx2itPk^8`^giJSyg(+z@MFfz94$<)V`7kV}d}Q)DAE=gW!Z zD{5pdCAqXD5aQwONg=1p4UxMLVXIw?hYuu48ZI}otQ=tz8xiG$0f#lt3KZSC)S4-1 z8A3wJ0&{}gs=zP2>`(2*H!p#F(t>s1f*qB6S`dpgYb2mqc%Kg|tJBPUZ=(`o!3gHe zh2KD_P9yUkQBUl6R2pDoC5OPrhJrP~5P>I>qWxRzP zMv!PPxhq1{aNGU}&X6*TuBuUjxOgmFP;KtZILnYrr{Phrl5a1#YdU}MVHqO#RKixf z$>W1$by;X(TwA07PhrEGmq{ylE^@a8n?x+ZYA3$u65;kzz;W?VuEYI9s%m#PA4i~`*!IDqx=^sT>A~8H4xu6hQ5BPT zM6HdW$_I$Z%E|3uAooPsf`OT;p4b;6I7Ds@gAoNyXTVLXd9cL|=7!he1#`n}oFHOZ z^`kR+%2_<6#R%qx*J1;6!)#2Tsyk-^m6bDqNNeRMtDbH(m}LE|r~*~6eYqhP(+8W? z(;C zFc1gZ&RS{YHZ+(A+?*Wt_?!@SH*f5K8#7P<$3}KWF%Sbe24aKm&IyP#;Q?+^IcrhA zNo~F|P>NzMg}ZSr4l@u%v6%t4;x&aE(q2`3gpF|&LE!=JQ8=4lf`wG^YK{CRwV6o~ zg*P#pFi`zQcD6DQ195E91bdd4NIij` z87g_lKwQN@2He0QfL(Zil@!4t(=C)j__8nj!qnO?9N_^5AdsEn0)lSI3IyCzG-4+K zmyXnO2ii2G$_fb&@CdTp0HYAdS8)r%-J~|l5R4=F2H|dGXC4AE6bB*P;Q=Qr5D8Y2E>b=i$TE{f++?f~Nv2#3hoR!Z%z+rZ*O zMC|YYGh$C1s6t3}fcgQpL_};-n>P`DjqD7H%ss%R2xrKwKyCq6MdntUUy-@lnHJ$A z=_NQ9n_I9i!W|yqVeG+y#u9@@2(s)wjmxs6JB0H!GNS-j*cO z(>p!#bQnze_Qm6pFX=okA-8vW@Y+(uOP1%+Jn~!?;*0kb2v`2{wI}@LIVX>NDG0YX z8U|CoCiAE_Or9ip8qFh*gCQUB5(e?)I~$KYh6%TL!vRwsE%UhK8z7H5?dVaj2R&|u zx7)&7#dpj1D;~GP*Dc>mc-#t4x5CeD;b-Nq@KI;~JnD^@N4`!3z6yV*MF*=Kg|AcP z?^NZL-s_Iz4Vx9uB_>I;&%Ho&=SDqVi8v`KyUr`_G~9|}>b0y#zNUr0qO3f<>Ga4`n=n=R z>eQx3p6Z0Zs!5unggl4o^!QYdmFG0!R_T3;lIrBDN1iD}xJvI+JxZQAbb8WN{nJ%? zb;8e+u392pm7lKCr_1#5YQ)8>kruB;K)luC- zXzFd3N)tR6gEW}R0%?-GHV2p21VU`@C@@Cs7^q`nGHWh=45V@TYz|N%7~)Q~JM4vW zVNfswJ8srdEio$9u`pSv{gjf&b`la@&W>mrq^p6fxr89-kgwTfZfSPvq}g%v*)ve5 zkz^WmYDhY4CyjWgw3VZd%!p{{X0{M6egQgiAj@OTPLF)q4^wq)`Rdl`kuT_-o@6V& z>hN)D>QhIRJ?aI%N1fyLs1pvJI5i;R)D#=92C_V9jPle#iB|(fo`itEI4^WRJp2ofG$X)DjS{;>(laPLDh<0aHyd@*D-+3O|p9pOwEV-=pyJsHt0>XYr^5 z)t-2Z4vH=wg|9k*?NP@PJn^a=bu7W7j&FO^p$3mU`t9_n69k?FRbHa1PogR(*;J=6O777o@7F}O7BezsR}Fs%2sCyP&uiyQXci; zf+s~`qD~COTZ?Xr!bl!Q!j!HsliyUpttcqJxqw?yELF8Ys%jB=CJWy%0k^6@+;S?9v#%d<_9Y|EzSPFsqw&`Sft4lBzL><>m$M}MLWgz?rt#QwQzwWe zI(*L`$U5G>x`NQb5RZLj@zg5EjzPRVn*BfmhOsR-bzoOwz~AfyFyQZYf)0Du{GCi7 zguj&uI_w$nU27nOZ+U|bdm43eKw^+2NhMawIK_0wshKiPvb1iO+#JC4)|;JQUKU$c zQQ*M$Yr?Fo7VePBLL3g{KJ^O9s>-FMXoieaQ8bf#gjC?$yHa7xFPdCfDI%2?sfGc_Lh935(1T5k!WS1tBs&j*!TaTST6))K|H3 zoMgVjCUWNSMBY4}%AH$P{yd^A0X9xG9H|%UG)ytu!W8@kOfhT16tg}|F?+%kj5tg& z3&Rwv0L)~Gb6#m#iNIM{IL_RnaOPHtv$S!XMLdqPh$nFtR*AESC~y`wL6_31l5$ao zu<$atMU}y=vJ7eCWr%pZ3=vP3A*`|t5mA)EZIUjDl7Dfk)zYapRL&Gd7rEtgJLS3$ zQ_&?+(IrvQB~h#j5E;pIu6R!fT2z!<7$#a$+ABhN-%$22fueFWnU-H(RF=m-pv2cr zd9k@U75Sl)s)~#AD}}v9op3%0rh2nPSbcdQi6;r4l#@F(=xlcBRN>Qd%5h3Izd+{Q zP&ms)f0voyEo6Q$coKI8gCn?mYCcW^sPv7LM=**Qt4?oP{eq&&Rj3X=o|6QZG7G%- zaE2Dn!l~L)S<(Db9!1$p0`|s}t8h9czl5JUP-Qg))LxI_UjA%%wuZ6F+0P(+|wD!5U=qgIq)Eh}h%$?Z9%r~+=;ubnuTojO#uU`l+0+%g=zEkOsb%qgEm^Kb~4 z`l`yyRJdE2YD)2@(c+@=9OWsvNF3J?TqM55Nie77S0Eo1tfnW)q#~LsUdl5NU2G>{ zN`G}U7_V<+nHYcd@=K;w&O(kNLzfhMxZO$=CftI?$}1^E zc=2XeKFQLDw~X>hkUo>jDhs8pYFb`tn95poV;ujq@)hpzK&CdQXlp*=M7Hsg=XS@Z zCWaL7exEm~Y^DsszM-Qq+UzcyU34vkCn~>0*gYZQ zB~ETx$t30L6u#oXfX&xsOULb2UMVV7YHUuiXl8_oZUr;d3gc-xrC>@?Rh-kS!o2Wv zg^1TZw$kF@o75!WJE1N%Jlw*-qjG}4H$Vk$x2VAFt_Fo=Wm9t|mCZmqmP}H($h}OQ z+RMZ#u~wWbB@FM~Cgm5GRR<$0X=9v{3C5|7VVsh2#VM(1oDxOEDG@`Q5-G$fkwRQt zlHksOs^~8fwpOd_;i~K533M@3DbXG(T2gp~2b$9!F4|Ov2S{lT7p+^@qgHV#B2?k- zwmVYo=~4rjsi+AeG(6Ch_Q;}@g-7#1c(AGK#;k>obSUbHmbXV2&0p6;lCyEGjv(SF zOw(-+92K%gvlC5ecSyR+G}yH8Xia1zdSXk(Y$g0cq+eoek+iv!VvDTC7F(|onZ7LL zA@`2lqj9kHIHw}Kf(>SQsbq2sYRgCNA#EOV6Do2E5V&sPa-+xNG^%dA$PP9p7 zsCdWChiLAs(7b${zADYjFVCF?Mhm+U=`Y$M!WJkzn$`7?SvRtKi^9~@lE9}Bo86jP zxPP+EKTg?ko+J>T+pYNAZpG*JIMtFOe~GHBFZXcP^+>Mk5oh-hW!Ckmm4_(3ZVZ_R z9XPyy|Hd=&%PWg=bHJE^iXvizT3!t~eX??jidAU+pj*tS9&bY9A!2VDn=;g!+Rv92 z>q>O;1_MtLAM1+XY`QHdxnV7qDAv_DuMDTE%k5EAYIm$l+Pn#|g4=7il5xx&x3b_C0Nf%0?M50j>W52Zsm6TRYv9us&<=kc`t)O9?}WdO`vQEhXF&U~ z2EDmc;ZqD9u^R5bnGSn7w9aZmudWWQigRI~kH1b9KyQ);T|^J!xtBbS=K*MQ)W~t@ z+|i(8=Oa8n!5uyt`3~AwHLal*L3FJVG^c7>2Q3cIcr6vrG%XGOK4?DGv_8;%szdkb z0N4jco5I0jk9>3GtMFAdCGN6Gm4gjcD|N)M$ssuLla*#>>X)G z*r9My6o$sTT`1)?dK>I_(7kx>qxxHM6p|?Pr9C`~h%AvuL#??cH(b)w z!R!D{T%F8LcrJkEFdZ7N&cXhrqdqa9bt)duOh-RFvmCj27CDO0=lSS^=5WAOu$uvG zR?uaI5GwK@(1i5|2}2%k5-CzAi}6cv6Gkk_CPrF%|18opcUE~ZX<`BZXc$<&gm(1Xn90RBUNLRmZ}N`w|NlY~Fg=g`7KYY%Nd^bq{e zhe`bq^bP10=J^(fgh7+;%Z>~7f4!@Mf5-EG{)Z&gn`Hk}UHeqe(!Qp#&^&8G%WXSo ztnCY3v*YW{)mK3)Wpf(KnmZl}nHVzBaY5!C5{43yw{&sekT8+w+wR7CYvInW_hU#o{yfYKApmY>hlGVB;6GUS%XAY%%2C5&XtrI4+3HE5k?m>n z40#qB-%etl`T$zNPGPqC1UHGC!Mzxtk0eJ{IA5UVG{0QxZ6#*T4 zZJ}AOGwxDx32k{?OI|uO_xFQNJgx)J(t5`=_b=kQ=b(pwH8jZ`fnK(w=#TA)X~v*+ z9cEhs9en^Nsb{(Y9{BYp(dPBgNv*RX{?FM^|L0H)c^>!TeF?yQcMN@x%_5P6(n(}6 z%_W1)mPm088B7fLM8k)>qJ=9Gu1L5dMIPzMBOQ69Bad|Ck+mjRjqngej} zEo%X|3eX177SImR9uN)a2tb((fDM4VNw~R_ggd5#OW-}CDX7*IRBMXTn<8aX;oBa* z?cv)VzU|@Lp6lzz1oJPzQS_j9VIBh<2fPP30r&tn;CzVNaZZt0Xu0lax$bDORJ2w* zw3HLA)Ezm-X#7^UuK~E{Px}tQ@8SCa@FUAegXUnz&%!aXFwcoc1(qd+m~>I zCed+UlKu!_AK+2IegN*8(jNyr0eBK{5bzY>X}~jpX93Rvp2v+&xZ#a?0fPX80mA@` z0IM+{-l5%wyEktK+yS@~a2Mciz;3`jfO`RZ0QUjz2Rs0H5bzLSFW_OoBY=H?M*;f* zj{zPBJOMZWcoJ|B@D$)_z%zhn0nY)R2fP4y5%3b=Wxy+dHvoqKZvqYjjsV^QybXBA z|DGNKs1FDQgaH}=!T}8d5r9U3#(*Y(rhv-;%>b7Jnggx?TnT6axC(GJAQI3L5Cv!j zXbrdq&<4;J&<@ZZ5Dn-6=m>}b!~*64<^vW076PsXTnAVLSPWPKSPEDMSPobLxE`<) zunKSkU^QS3U@c%Bpa!rWumP|UunBMzU<=@8z*fL5fLj6E0NVjO06PJ@0BBnsZL6bg zb+oOHw#EIx@9Ah;9c`O%0JcI4kgYdEmJC6PyzNj{<87 ztSRtx=yCoZMV&53%&SB!8CnmaS0d+o{YNkp))VDjM4AsVBc1|$Ca4<;7409ub_#c7 zoxxhFZ;0Uh*T&x4zlMI+g}|cL&yRWW&p$tIK=otxl|Mh`QU9Bk%WnFA@?#$Le_6{9 zw@=!Ce$0}8_w)Y(`u*(>u`pw+pW1y|&VOh6G2j2UeilZ-GW@s=@Spxrv!8-F{L|Eb zj_=1x_fLN4apD&={TKb8p=B=;hYI=F{~bmw@&7F2@Ot}y^k49w^`FQ8E5P>%agf3O zZ{Yil|6_!sZ}HGSV}g_cUq4o88z>2G3qREw{zJhS2vf8~n~OF;@BP|;R;52HZM;Wt zpT8dea_sQiST7AxKSy5c?!`17BJNv>S%Q&Dc0s_v) z459)8qM{&zC?XL=q685^5phR?M2QPggG-DHDj{kV4Iu`X7=nn1s6bFrksvA}Dk1_Z z_j}JeBcjy9PjB#Jh=te3q)J7f z*-Cs9#u(*JT6?*+qwMMSVXxmiMP2%|pCfRAa8chL)+(X{Uo(4RrYm&82IWem_uy$K zPEAX1O&Qqi@>@vINqH*%hWVylk|*Wc@k_gKw9Di^JMc;B{YQz<6A7ZQ0Ll1%A!K_J zUr5*>C-KSD8mpVs$Ph}71*!Kn^DiruM>&iou-9_pL)4|ubgf6JUWaG4FNw{5?qMcI zdWlxrZQ?D2;yrLyC)TQ*#JazNK%RJGo^0fxwY?O;zQVclK5Lyg z064sMM@aaWmG}lZunuyv-=pv_1_!zpYE|M@NIQwY;K9M3U&2F4#7KsRU-|`M>KJH# zbu;mU@T48~>wj3H)74I z60r?h8eKNhg*cQ>Px(2U-d1xZ*3m=z7Ue`~#A{{r`KQsTerhctmnC1<>y*%i{zatO zVj~TSWp%2eetqJV?{Bmubbhbf6-WxQ_y(;!YrUkbv>)E#l~mCdpwGDUV(3-3CzWF< z`8wyCMDr3n(TI9b{b1?XfSSef<%Lk%CDvpuFJ(cOGH7itJgK#Qd5r2zZjT^?)T%kp z*$N=7k`L6v3^0ig4-!y{=bpa#W$z53nmLw-|e2oDPP_FqbHIFqSVF-mMl z?S`@n2(Az=#0^HrVjyPg(eCGi-wL}w>!XQkfsqf!Ti6+|3CspCv+zDhjYp)!Y(+Xs zANVEn2Pr9e1yfL_+%eW7&!53dU=+NQvNA{8B(Xuu)5RW%k&F|e(Ux9Nw*#niSo>h> zz_!94Rl#28>Bm7pF_WM8cVc5=S+xF3&LEV#WSpGXf}9sBjx*ASO55dyJ3%?=C75S^ zU#|VoUn^I;-Kp>bK_NuB?mDf#bTuSrQo+0vsb_A`;v zyo)moGN&L%7cs&XpHJc=XXFx`ah0e4+;QOJUtMGVb0MzLr?B^}QRJsvB-h!WuIE5q zCfC_eTTT2>B0c4m&h(ZZm#w3>W|zK~#azVqtaEydfEL?VC}kbBayx1W>PuaY)E+F^ zO|q6SG|BvAH))@|-ZS0&Z(4b;XQ*z6)`fj6TK53;rAjevq-SZIY7>2~OHGn^^5RG; zXurPpZ%q#*A!tk^Zv&Dcr4WJvX79L~p2d!A$9I{PCi;N6Wd<*PCWOMrJn| zDnATxC38O7zg<{8*yEv~$hxZd9X))v@at8y-&@hyfi4>NZtiL4glYWUUd}FxM*hqOZOL6-Awak1W2EYk8 z(bM2Kgr-WoP6-Jg;)_E{rp6I|?T^*9rndV^W-HEwFSuK1QdGs>UO?+LKco8LVXJQFT@H&(?!M|8fhKYKK z(!2!JDo~hxgp2jwShWzf=5xtKl--Y565dlyiAtp&7OG6j6#Hs8biOU9VVWHoy@R&g7#sPDnZEJ zM@mWg*JF}Kp4=k2v&5|>g%YCkU2-NIzrd~mOS3Ky5_>7zLB7fpc>BD3@<)C_ZboIH zD-PK&SZ4hCN(?JHFqTQvlG8>47#tWsNgjX1^0gB&4Z;$LfMC0VnP&2|8MXFlgd+4o zYl_wcUo80^x=7|no)FKP4j}%#dgDJkRU6V((o7$rZy^=q%!jtGG5##gY17YFeQ@t6 zE%dGS_=}V`bL4q1ZFS8DTUA)2XYu0V-)nZ0Y@<_CMmoax#*OP`_0p5e0ayA()Ki$O zAt;CMKpr;92U^9CQR}|Ii1;1fCa6zEQeV+lc%x{qeEMSHU8Io1Xm2)x`U*R#M-Q}k z@I&7BYn=EI^`;1t(w<6bmi{9S#8ZSc+($<;|10eaTF3ARXEl;jI+RFHF|ET=<_T59 zC)FghV31QQ(`!$b;!_Qp#4a_L5o2#g(aT3a_q5tHW(3LBD$Blqi?2Y&w{oBj!c0W! zfqrXC&>90X$bmB9m0 zf!ezJgI=5K^{6WwaY+q-ABgrF)bdx*i{rfq_FbE#kHV9@?eQ+~3G!EZKFo5-Jekx% z!MnRzYoF))OE!9!!juS|7P_6cZwz)GXJOX zy+4EchXha`%5Qttf8DoB*j}N0A#}x23rc{uWVY;Ix&$UZ^5+WmyStqK3TE+jY7rA4 zRQ{pcyx?g!v;pmg2k|ku(IaeY5}!x;@}@P?0dq|<*W=e;Y6#x-qkIwPL_tDU!Y8PO z>4iIyFH+|ntbWOy5x#o?SB|!jUzKtXuS<^DNFA(R54D@HWCg8Ys7%}@_44vmzrHC+ndtLMWf;0tiSU`@Uzy3=PV6qV za=oB^oXgqsvn5A;lRg&pUrdzc1n!?eOGD(fSAx;{=Si(aJ0kVjMk^q-DH9xd6QdX- z2N?%s=oW$L^$pSLE-}5M4Wwnv_*bvQNqeE||36=;1{8!3if>wmyB~iX74C9;4hFxeFyal;{~insg)tFMLQj~nMH}`kUm?1TAyPG zq)bB(-}jV0t_iuK4{HX|BYD0=Y=FJ?Av9m0cCAY+)Ut_rz8mxcFHj13hCbqGpI_~c z*=vTNnuRdDxOsgc+rMps0{nyZ8d$t;2SO_;LHXpCE^zOSiG$~mNE&6Z%}8o1wIKOk zXfgT^@kF)RlTfiwvHVDjyAyNDc&`=wR$g2smjvf*6j}6Y`fXy#>-TU;{^j}oJ?tRG zF^J4rGzISX3}rZuH`!msTbMFChB;AnUgGzni$|EAW%8W(J8I6CkV0FCnHLXpC;qO5 zLlgLmqZ?qSU5RToAFEC4;g{f@Zx9B2VI|LKFGD{<1)!Lt@(oMU!CD~kPkZQE5VevN z;CrS~CelnFJ_7Kb)?$qMB{1OQe3q1DEIeWFhSp=PpQ4_-pxyQ*fen8|4(~=A!x<>y zV{Ygb)Kv-3dbBsvQ+D-LFIJ=85hYMUEq36vz47-E2$%S&?V=Al0ZdJfySWL<$5XqWVX z_=pBR`za*zuP7s*Gyfy{(Y{EYdmPk(9bR4al~ud5mU*Zn+UiFIt7EX_R1;a>Pv2d2 z3D2esZNGdU)L!1nVL$RC?nphhIKFd1G9IS)LM7t?7j<5w_fcL(QMu623bL3j;RvBK zRu}ZQWkvWZvKhwMJunaQV~wF=b1uSWaMFPdPFwGiefR=8vxhv;=5+Oh4BNO)-T z2k@caoBw5Rr|}S5yF!fBA_r#QmL>iqX@<7W@744-8d6@~M9GkO`a*4|>?_CQ#87VU!HJzNGifluHCG|`*5XWi1M4)NDzO`PNK%MsH-dYl;9 zG@_$D(HgFF`C$)beG1rSC(;jbt5ySnA&4d9ouLL^9K~6H9}sit^`zCv$J)uUVyRPF zC^^tGc!zF*!npeU=v}8#l;hE{8rtO=T3nPVyaA$`c_UT9C6QDL^{0j=spc5oY7p}l zdGMv5hLoZ{mI?h{P##8J$Ap@H8kLd*0iN*lT(tV3AdM25_TW4YZ7^3;%TbhKm--x9 zH^4~Jw{4G5=+<9mmoWc;)^8tbAIo~^Bpszw*lTSez?N3OJugp5@$qHJ_vc=cWRmhL zT7HjH9np$!!%R(MhN6)EaNaB_ROLxe3V#iQyjT15ij&kmnf)%t*$&bYjzN$?jYf)e zg?t?ILuINZQbKq&9%0;csY|XW&B+9p1AxAYJ~3XZY@osJt>1AWH|KM_nKq z{18a!i{x71#WC>FivvoKeaD>Q!3u$lKdpbO!$n8+LXQ}ddNo{d*xrf}B7#VswsO3{7geyGsT3>q_ zBR7%+Pq*QTWl>%_I{bzq zo$bNng?&nTo!(m==`HKUttAb0{g7O!OaCwHO7gWL(Gt1O8<@=}hFgHothL&p`+f2rjK4HBLzHgNr z_m1Y!jeiK?)M1x7NWuH!i+)4>O{}TJ;A0NjVCgL+o^qju!aYu0fj`b6oL``~!OR=O z8u+05A2IirSALKp_oQ|*!VkzATipG+3{=!cgj%FGj`WR;J6MR5H-mtFPQIqE)1gDn zaZ=DPLKogP3J;C~lh6(EPwF~#5W=tppbmseS_0&11DF`w_(wFwAd%-kdt)q#XVL*| zA?>`Z$MCxd%4a}2{2D~?4^~-VO7EQZ;JkJcnhV9Mt)_{l{Tv8+rifs+Q_MADddHEx zOJ2qBo>vcsOJW85mvo@yM-LB;iaYUNE~G7L!g|!44nVnG)G7FPr#_0Y610|UhDQMt z2#RO}i87=K(#ktTlYcY=;k`dqy`@dpR_YqcdJd^=BRqdkeg+4`(nh_FI`#=TOE8kzrr*uf^8vW^iP zY?f&Cz1gULce)^pd-vp%~`ZXib-)4PYn&WhAKSBO$#OLdFAL zZ-MdJUbeCF#eOS@NXumU?~h2IOMml8g*jOaegVgOTiE_Z+B4JwdGA$Wj8+CI9@XZL z+LH|_IWYd)si~;RG2MGe8Tn4G4Q996c(!fO-^&|wafGrRW)o#bF;i?K>$I+xmD;7# zO~*I!Jl3eT!F*o6O^{I}^7DD5-nPVItgCFo$_mm3TtPicP&Ob7-~Nf56Zt)qhAT=H zh3i|$S6wQ4K$rBYN5SP1l!GGGmOUpBZsHBN$M_UFL_)@TWlU}H?>&wsF^Yu5i@3x$ zK{L%w_mvKp5q zv^qC+yf3W-k81T>b6@eS5BP>!6`)(G7xg=mW24Kz)KiAquUT?O`$k*5y%zf5*B96Y zT;ZWco##uT<@gM{7%21~MR*(ka%pAo4ZsrA*0z|X)JH?yq_yfHn&4s^rL>*2@8Vh{ z+I!`qiJI=kI*a~Eyo*#Pe8Yed4@z4R#%y1r6>J4}ar6+gMMw1mWd-r7`A9)`T2|(j zS!UtRYo$S<TDB)_&cmm%GVcCHpf7dk6uNrEkc>@@aIHn$M1j9nxs9>q;9eD zzZ!_@56470g@bUS2I3#D;*5DG^)d{(u%kz67{!O=BPqO|l}M%Ii*>#yBYFQ*jxYqr zp^WUL3ZLiSwO;>8$2^FC8c$PW8HxmT7%YxCkZOCPB?f(y{*FDs43I!gs6Li}hB*3R zXyMT{ZM&WtlA2P{Q^K2sF(|`fErd@27d}U05_Jta?EO+Kk{?5D5kB%@7wTlv!T6&M zNORN@iZs&y9hU!(0fqnQM-RU|MR`Iuz{-q&)Fk8E|37w5>bXoQ*_cbxYu8>(WQ9V= z)OXO?_WH3kU@z?@_Q(NZ_0~(%_f*n%N6N6Tq0aZ9?A5WPNgv|-7qsaS-+Vi;k~Bv6 z5$PzIBGEChI|_YXCSrdazsYZmT8ckq2ev}6zr)@@-r?aHAK?o(lm9>Z7%g~9^dm<> zQwMMl#f7nkC*Of{BXKnV`WmNgZA4Sbp5K-M=P3ww?>L1SuR?5KZ|ilkO?9x^@?&odv6MJI(c)js+hor#L_>p~~j zh`PA=kIP`pQtj|tz*GybZBAuJo>Q@`w+4rC8S^5NmMOXtH;R^|97E5RG+V7*$a{~d z4}Mu<-oN6PAoMV@1}3u*`Yk9?TGNqIXKE6w4?ijVQHKmWY^$YDM5s2qh**S_bqsyL zZMweIx31GYrL{yY4_a`zFRrE|P5MRRpR7%f=J)usd)0LPG)5SInI%fIDp8Z#u#XE% zm>*kd-K2HEeJA@ZnaPa>bFK2r-)bS# z<0+0<(IVm<$Ycq>MaL}Xn{4g70RQ>uFr5Dt{FDd#R=6X7pvj++ON{MWd~=S1soa=W7Q|Kmu?1#S+22=iace?9Ua^ZEW!ee~w1gQVZL zxMXB6BZfWtjVn+)L)_=7iL=D8SJWIqn9~2zCHh6?Uz!>t5^Td#`zp$&#S5O7Y4A7J zG`*1ev8&VFUm*!=Z%}IBKJf%D%zuk!m{P*GRExis*a|zD*O$Mm@W5*IMvr>WyZhx) zKc<*6D}lcGLq9|*)-^oVysJhH4r*m%9#W$kq7t+P^3R_nghC07hY-&o*O=eC{CLv? z57)^GiS`ig5Wmy|J_K4)?V`^?3!uXlaU-D%Qw%*L>bN{7hdN1D#C4QUTHdad1NDHT znvdlnikKJ+k;#nsDq+|b)~g`2Z=mO}5c7?YZE}fw+HzHbH!1%0$*#bZPsS zSFf~q#DvCe{(A?Z$7-Kvn_4)d( zH`*UyE%L*n!x{~7l!pI2gy*~PSl+D(>VMEKCR_0d@5!g(*38nr_%$$DF6PZNNvTO6 z8ehKla_CEi_mpzzS)}KNuKaJ4bc!`jZbvB=PlLpx?rWpdrzqY5t^F57e-yAeTOOGHoSkEdFhdC>(6!iZlibjo?Iq% z{mcnu+DGQU0%nbUK`0SYew~!r*tCZphj>=0IT~@1J|`jqX~=45x0(#!*GT5-H0UHu z_}LGr1aB*cB2gM#Egf2;{S*l;C1D1#mv=&xs`O+r^CPK_cR*wWA#cCwSplq;rRWw{ zk;Ai<84R7OVcy9sjQ;Z^_AKH%aVYQbUHp-mfImn%6WaTW0lqn<)N7IOdv#CJOh)(| z88-L6e~^3$e8ZQDcxOeYZ6sHi644arcwciR44>e_SX>VN`APIO;#UAW!Pyo{QzX?0 zXMd!Y>$(^7W)vEE=tAaH@R!uYM%?AQE9eEa4`F@L3w;)Oag1N^_fOcp65ABNRC zlz90%5Alcg_TKuJ-!AJk=m<>c$3N^rXoox%S;iW=5xQn)sz6PYyd&$A@e9)yU zja4n7j7UdINrYW1>E(5;GXKA&q(HO}(kT+DrAbLtBcC;gx;U$mkmUtNTJ0@iwKY(YqeC^FLUESscqz~sh_6zXw2~Wskwg*b%zsHWa zE#Ul^FeFZp(g$RdVxANhUnsaU1?B~h9uc-n)ogvf>y&*=UEZ99N=^)~@M5>EjCtOovh$glspl%d4D zhZzfe6{k^c*x&20SNpPec+mQ-MGDQ=zR4Hc-aPYK;><$tyXSC*%nugt@TCFbo6_!9 zOJQB&{Td)Rc4Lwn*m&jLlYwmqUjp&hDMZAB|KAC+D@^nLlm3T*;P;O)@4HF2qL8J| zXpQkThxf3wg@3@)wqCf3L)la+NNGJr@-d%&$3^qRNl3|)u5rQ(|1!xkw=dVQ?k09=IEJ(nrxH9oBWj4jieOTu|7rW1Lw+-cnPa^+ws<>l=yufFTwZU zM0OAIq=BqYgW4nS=dteT*%&9%zXQ?p?;u5dlMl19XiwL{+=mlBaUIdya|>-c$5Zet zY|}QYYCJ z_I;Fa=<@EFc=T4YgMM6Oksmu@E~dP=U}~X1gx~(if^y&K-m2#I5Axjg%+r+imR$FU zn4DBWEu+&=?806pxi`^Ii2o~54IX!Wl(&Bxvrm#1--e|IZP}M-Z=}!0I{^6pLa?9w z1CHe(oyO$2rAS~z>>u=rDIaYKpxH04Ni{6VKk4uG;*wBlH0Uc$y_>8LX~P<(r`D9} zH1(O~<9p#A!2Fj#hc0RmKS_h+BN{5thUoywbnTZ{(;yj7n-Y0p+9su7vZwk<^hfeX zGyRrJo|xWD0X(D&jdl{6Y(v6FXxQe>VhDl>@Cg4kr1fbRt>`8d<%Eg?zlkGtO*UYlh#_CN!E&@j+HdL?U!aflzC*d@Yz3f+?{G(;`_(txxFlblAPTWr_0ROP6@R zfF(T=M*RleO&RBdPpHda~gESjpmi z@-(A&hF^m1WbY2$VpcK6+hlze_*Az`!Dt43j*Lm#z)$?KiCUV;T`0%~xkz{8!P}z- zt6`qT>l4@DJ-8NulzvCE1mD~Qf6`48RR3=lag4UcAiPK|{99!=bpMso*$AahdDKkg zUUSt0s0Lpgq}8RFUDEXqULAr2<0bbUxZc6#z0af_V-7m$S-9+v@I^eRZ-AaQ3k6#5 z4JOo9GWKJL6kK~dhl#KslG+?b^S?GZ>Ps0tNA*W(tw;K~@atkfebUuIe*gT1@4J^i zZIM#CT}s|r$rr13XV3kO>?Bpq>VO!jW1XL-ta|!X)5CT?i5uk34W(&;=$ywcVMf9xyqPCV+x9;+W1 zndwo6e6p<9RMKt)Ngui}A^;SRCeVXzg)PIcourD2I zf05*!PBZY5^C+dZL%;mn$(yZWdilBPV;hV>rIca(Bbubo^WQu5`)(0PbEF9?rTmeD zpZ?5w^k?whEC1-9m^TQ$^bfl4m%qWh8>Xp9vyYZPCco|HX7Ya&{XijIF*_hnI#ql= zp1%xfp9*DsDojtf5d7o4uj||UF8w<~{0(DoB0mg%@!MBDjr%i3sd~yNH~Jb!87CO8 z8oiCzjJJ({8f(oeW0iTddAwO__BEe1dzg#N=gh~=CFTnAX>*PFj`@=L9?oUv`&O~} ziglQEtCeBhZhdM^wLZ7|SgY-R_FlV#{i9>pA3B!Pz~1a+I2YMlor|4YoKu|JoClqY zoQIu1IoCQ*JByv!&JyQM=MJ~2dziDpZR56g7P%eVj?Q!Lk!}}fiF=e=>b&TdyXDTy zZa24^^AESm?d~jfk8^uD%iKQh80S^@Vt1;u)t%OfTi8tL86d^HyI&((#hyBdeHMEwG1OpR9)K~GYXVP2}HK=N{&G5lTPYS7oJ z>tJ559)gb#<7}wr;q0j%!Fhpt6sM^kQ%|Z=^(W-a@65Q zu92&Xj69>A$}-v;?bRv95k^;4fUab3N>r6miV}7RN?0$Ly^TKL^fmgb4k&FW z!0d1I2j@iNRM3Nr(_juZPDiQ_F@~!y#@Pl^$vEFQU!7)*H70?cY+SEm#&lyQe816n zKsm;P#zV?B9yT6Ut})M8pjsP$H2$bs7>^r|s|m&v#uMsH<4NNwaGo}vMraor%OU@& z@haqBGhS0&jg`hqHQZQhybEn#BEOF|zA<(}{#)Z)xZP#!2EEt#9_9~b1Lc?v&8EsV z4>60>G3KFW2hbhOj>y$KXUu1n z)W*dypEI9>xx`!ondi-=(Em^KRn-^u6Z`I>j=qD?qL!)_=KJRRFh4NAQ`zQjbC1e3 z_nLd**Y}p8x>=@$uVXBns*TmqI#lIa#a1ymhgpZIv#rCej;fK>$tqKpRc@85Osm2= z4s=heC+J?*@u2%yeL?rLPE`G^ldOTN+!|yJ0_QYqFzC~*vmrCW8UfBZ)=1Dlv#x{w z>#ghI*K}*Ty3)GAxdDDn=%*X-PQxDiS?kh02=;iJ*6tG zr>(`H|7yLUjjkoyiR`wC9E!wdTpgY=~Kp$xz2|bE1 zY}Y)^*mrS@guPqC+h zbA^3{GVN*hG|*StSE`=&RrXb?seQG5wd!bJV_&O^?d$C8AalKaJzPzJt{p@?~dtrN@ zeIIOpXa5edyWjpj=m+cvRCoJ9`$5nT*$*L958DqTP3GD2p!pH|5#`#C+K(b6kJ*nQ zcJu8&sKe|9_5#TN(S98APuNdD@=5zi^>h1A_MZ@vr|hTU`$Bsm_|MqSK=N7pS@`m2 z`_JGkvKPVCU+lktUTiN0{a5=rmG(-6^L6`m(5viK2-_R>YPfpSep6-HZ`p5w|F-=$T&=O!z|~rNEz)+K zy$<>HFZ*AxU2new-`}<0h41g#??G~dy#Z+n=j0_BI>)?b_Sz?a=e3{T2NB+Ws1nJM3>j@3eQSD*Ide zTjatn8|%3CclK^**kkWOtoPb`q4`I9ALJ8`pEoC~WD(YkhU8ZuKm=jZ7od!+= zb&`|mWGmaradK3llk4Py-^jr~9-L+l_Tq3_I4xkdbXuy`PLWdt`9qyzmFKi_T7lo% zX${Ht&JpTJr=!yeCQjAT>Fjidd>5w+WJ;Y<;0=$YtDgw9KxNwA&lOolI0oGIX6?p%%#PIab2@=E7QxVp-@3h}+hxdxKg zI@f}8gL4By`%C9W_%+-4HPYl3=N8D{=G+F`+nw8?ZH_Ys`hVm62K;-R`w*T7od-cb z;yeoZ`OY6;E^rpWuP2-*Vf!cNPq6)q^A~UyJBw8u7;6d4=bh(OcjpD?1@$xMMdw9z zh4Yg0lKPeNcjxbFrt`A%vYO)j!}*7r>@0Pbs zh$?fNxy{uXZVR^s=$39vb+KCnv~`JlsC%fo#w~V>)pc$wx3y~J9_AjVdI1r(Q3Kt! zZd-M_+sqd5u794Bh^)IXScID*X`nVQA2O6OZJ3^iBp5vYadZarN^v~R%sgdrv?z!+~lsgLa zXm>Q|^V~6NH1O`ls;N8Cov2#7m$;XJp5#taC%BW{$?8z|Quk7Ik$ahY8TeD&De5x! za`$r8&zP8=(Tuh6$Vv^M3b!6$jS-J#+$Rt5Lw& ze^;javiq{S(EW#t7S&zqE>+Xrf4a-Sf5m-8&2pE!%hh=IRd)r_^L6)iVN z?XHH*o9>$^32(V?fqvV48}u4?jT$GYU7aeZ9Z1?VRwx5VJ%dQyAX2x1(~k$Y4-k0= z;&FqB+y){Cz9b4Sg zYvAu0#s=ea;N5M;b|By{4eZ=z>;U$4fPKFQ?IT@`YJh7y0Y@GQbQ?E2o1MWCjO!5N zHYdhyPK+BT#w{{WF~-q z4fAc7f@KdQmMt*f1(q!`H<%mLaYVF5K(yGGn^@K&mW=_+9twOZ_%%lSY7xK2fM0!_ zdOUEd#-JEW0Day7dBL4A;!caW^HAc>Ly0>Z19vV~4T&{76Kh^gtl5fK^KfF#Tw+Zt zf;D5rnr9Ge#)vf!A=b2rH4lkk%|n1S4^@5aV&F@Q_%cR(*%?Pg#^gBYsPJw+ueM0_4|$ z$gdHR-%&t*tKdq|UL&HtY@)qRM0+;TUVETDAK`T(!pk7Svx)EwVmq7IE{oXi5@NgT z2)3I39+3GZ1*KbAYTFD6%gSy0K(gWeE9}A&mhh#BhI^=IIk^nUKVj) z260|?1m`s&&O;#u&THV{P?FyNBIo%WDCf+(K$a0!XEyl14v=Wvxz)K9VGumpj(F509(9OECp&jI zcfj^e=T7Lq%lR$b&ILj>o!>bRfEJv3DQ5dHmUA8jTD{D9%y|s-d}ls<`~#4y%Pau7s1adWjdK0Ul6|zCw?79{MwE9 z^+Mv;V~Jl!5Wika{Q5KE*Ncf?#}mJvP5gQ$@oP8Y*Kx$J#}dDuL;QLK@oNv_*CU8u z2NS;zA$}c0{5qES74wZRA#JctV9*XhQ!XpKD(y67_x2PN@k$j zs3VDSPrzLA@tj5eIcAZkfSRhVQkP)n_+dOfido@%iDe%Hiv1U`?K{A)uc`NeUsnOS zey!dHTFq9Qh*0+do0b}lFoS!%QH;6U6O1mHuRR4Q74x-5H{*2UA)`uWUX5R2hV?b$ zHlWP6F$cTW_ylvUpBcNcnt<6;Jnu7Y<6mYBD+?b8YBckJ7sp{9b-X#vJPx?cfdZ zY|mOltwq*i>k8{R>jmp7ptP;9-)39Z@pyp+wF3}F?N+87`r>L%l-Dz zn9X{~J{I#;f53d;A2Bo47g*#OyT8mi*#m$)Ub0UCwpeAKAy~ma4`^T`eBNY#YF~(+ z{tKWs%-LYZ53@Cx@!RE?_GQv5+tbk>H?(g+Pu$p^i5|C^eLH4ZitRh3H?{9}(1Y6d zNUv$%i~h3IzEAo;`$3r_u^*B?&wd!a-EsCj^mfPFk6;$0zdav4*+6>%`miDP6V92= znfBA@rAFHe(HmWC{|&v%6#EtFGwfHv+$| z*&cNJc;|Rogs1Jh$GZzJe-X|d$!C0A{+9SS?>sZ!FMeil4hSfpH#k1HQ71W5Y(~b% z;Pkcl_dDYHoQV6x_>}lne4Z7b?(u#5@IEU(E3gxKR{Rz`-;;7a9)CRk0Io-I1!w;9 z_`;-fX}m7y;`j@YTM=I!oa^G%&g^UavtWRn@lA5YH^?2-r*g%&$9HPm(2Vbm@5K}H zu%KsnVl#lA8R(8OGDDuGGV(L>g-Xt${2dvGXEep-pG5)XaSA#Ybk692vupBM&@ZDR z>Fl3Tm$L_?dgD4ZV@Plg&!~Q$o8h1LW!#i8KVv+vvAoaEn1rWE8B;T^f!&M@k+=<< zyFfjJbAHB?8P8@sm+?}@GHvEB$XHppD`RVB1AS(^mDwQUt&H^Y3FYR8L&}btdZF_ zWsS)imo+hKde)Szt6<)ebw}2tS+nxzkh&-90X)5s^?268ti@R`6n>DkDQj=m3SLWj z-Pph-mWo^jXRJfTEpMtX;)J~jx3%BAWFt_ll!Y8wPcxNW=nc4Z-O$(nad^Wp? zc87!4A-gNiitHYRyOPrxPp4*|n>_@VoWp63&7PV)K6_I3R2|<&h1u8W7&gjA+#kxm zE&HzQ`*5G1{bcsD*ZFw{DZi3b=IhW$vhI32K{Wxdm%)>c5XIjp6IWze@Cud&XlR0xi z&&yem^A!ADMBkU^yqvQpU25703R-hp=Qhr5pW8lXhg>A6>lncplwJHIW@?0l0?4QL9#0r+BDnBOVC823)OvvTjry(RY!n2+Z! z%w3xMK<=ZY7U#ZzrwzF)a#!cB%iWOIG|$aDJoi(%ayQ8x)ONXYcjoSeouWi0`1zoU za2}r5A+KxR_`D%`lf1J6_lmq8dA-5wpLZ(lhJ$x*-dLRDK}}7~sl4ZKK9u(o&WG~u z%DazG^YQd#-m{=z%3GGVGVd*%NV~kPFh9Vx8PpFrzoNNI%sQt^Go3E^CHZ~wEAx+q zc}D(-{0aFdr=T%hi2U6A`}5~PPNbf~xv0Q~^b(ve=Wokj4jpT7 zz6byQ}cN zM%j4!05WLd3ZJFqOORbz_!iFfu-RO=6?VG{e`sVjYS1XVQ6XC8V&Jw;KztJ{%t69@ z8sojj)~`W-Wj#m^R;Y~$m=`yUduiT4oOl`OD}^>2QvTnRA7zRBC<|*V#wd|dchkIA zIO<3X+v|i zwG^D$)?;FN9MMolx)04;8Q)tO-%9$MM}Kdnzqg88(`CFil0TCCk&MNB(V#vi{jt#I zh15BR=8-gKF$}X9hFKm*!Z4WhjWjiVnAH~HJj`ki^EJF1V;J+S402kDX+BKzd2%e$ zSkD4qaQb29k2Ifw`35;}m?GJQbQh0?t@VT{GVf9Uv(#T|iT+ZCq?D4Smgp}vMSrPv zB+TyAQ%bibLaR-lDcVk??TNHK(fp7c$gSwfLH;W$7l43WDFY0FT1@u2@LXWzQ z^lg;CO|(H~7W5R8^BZ#u=wS@uO!_#CVIJnu5|WP@uNN4vV;HYvm~O{dU156+<8>@G z983Bb(#J5S$57AFo++t!3}bYRgdFcyNji_Gc`eOj#Z+4vm#qx#R&sWevs-*ryRAdS zr0s6T{eE*MICoo3#1z^X#Jn5CbZ$b;FPgF<_oBIwrqsR{8Pho95~oa@A&)Z_afUz6 z(7NQi9xb`xQhp#y*Fcu8@0d!fnMwnhN(1R?Ahivowt)=QKswFA?3&Tg5~5x(xuh_a7wM-FyFI^VE)-EA?E}!%~xq|CZ_}GWi%_v zIgIqXFkd!>^Rg+D{Yjr~ehm67(gSI(r~ZZ1-_;WRU8%pT`ESzVwyPz+bT!47uGSFJ zqPeRnzT{F*F6m2zR=0}=yiEp4(9i=Ddcc7m(9i=@OG1NMO?ovo^z}@!okLf1X#0qm z#@#f}VT{gU2+#32(1Vs-(rOdqGKz5-#kh>JhJindu^2_SqZpS_bUTVZj*`%tCOM;N z_M&7j+V&!+fSdwqE5Mt3hJp1|ni94G#&nl()GnsLF1q^AGez=4hUY`-X+gRLW6^># zEi|9uY{3w=p#GNRxAbVqmzIK{h8!>Dr@bq@2~N;w%u zoy*BtPRTc@XFm0`qn>uu(~jY6N11li){ffRu{^Y+hIZ6;D(SPa&TJTGQ~qr871C8m znRTK~tz+3(C$^xaRNN}AU@K)~okvSbzCrp8(QMk(Je)d*Gd#mRj!1qlKEhl<6Mh*( z8S;_-5@Xn{02q#QQ?LpKt$fG5%2T|J~Y92(L zgQ%xD`OQ6A+%{+U@3ka6SfPjcbDCGv{EBTt7t*KF>@21^(0mcG_&qs9AjU_eucU2r z(&y9s9XU4XduaBgIfUj=nmcHYrODpkIEnOeH0g`lM*3JW%|g;6Y0jm2E6tl|eoeE| zEI?e!8UAv%dqYjMkVsGIgNCw|D^O}da~0nHU616LwHpPXB1w(?Bq#2fe$s&bmgnR%2H{!q60LmBepX!fN{U(zv} zxs=KEXo*)L>BAVd2T1p#t2H!_qB)r6Su|rbJJUQ&(hWU`;FA-CW4hGSpSE3@R$ZyH zA36P)hW*GtgPcJ$@1!}0OsIB!_MQLxnhqtFYS(~}e@(I^jnsc3{1=d-n*@t3< z?$p6+OQ0~c@2tj9T)D=$CC%FAc= zc&@x$YJZDwRW8Sy5RN^At1UCJ+EQ%)3U4hOX5a6$aoXAs;H}m6SWoHTbi{hfkxpl< zq~N^-`!RVV!TtkRTNZG&<&Ru#c^s=PeX+K3f^#xfRb;j0FI;VTPTmi&U*MX{i(GSg z32QE6vCc9MD=#atxRmwT}C@`c6M|<0Uz`|f8m`&6 zuERCcqvzm07uWr`=HdDqzOyjY&kf*etX(`#Q`)N|rIs0;47_n;{F!DqBTmj3$iHFW zU3BA4VZ;aK(g)N@;)1+E0Y%>;a^fP;-0j(J&uL!{ z4plPZVCd_PURNI>RWceSXk6*U(kZ1^)r~5-pt@XI-UOt`r6to!t}B`8`&%+QpymYB zf#h3|;*mVRKUs6hQzeT^mXy4V(A0-oP;VEt`%$Al32n)eAj~1PAPq0HB~`B&)gHb& z*fONcgHWxhm0Fd?HXZNXKyKqfp|+&a67mA7qNm7}?8ddNj?|7oE~NGe5A`8ln%iyj zL7?nc^|9jRGq&g= zUoJvDDm;|U>q-|^&O8Xzg}CT56(7*ZH=k8+=Oc z`lwo0>gCdbP!+BJceGRm?NAt_(oajb>-d)L^kc56GJFiGOK~z)x>w|qc@epwWLMo$ z#TEOrQ;ZabKo%Q7pU(lL)xW%+(;RCGrPDlTtZ*0c)!Ld{g!ZPjhl^6oKY<$_Is*wmyeW+9G?V@%+YSbs8 zEgSBKIYJFd!%LB?SB(7dom)1Ee%2>7e79ao(*4vReAfijjDTWqFHcgM^pTE|l9Oti zmWR@$kIG<7cOX>Rebx1aJWZ9oRJN?_S&=JysBC`OlcYjkGWA><+mN?XY>^fZR6Sbt z_<>VpZ`D^z{aqZ4kRs*QGvYAHKBzafHE>Z^ieqwdRaRFQUj=^DPVMsLs$Qt_kWB4V z*$;J-`@hq2a6*t8K0z6NTA)O)s9LR`4umSt{@>A3IoFR}Bu4A1Hb{?Her$Os-TT!* z6{fLG$7>dq)Bcs0$i4iG@)6}Hk*b6reNuR#tx$u~*oM5(Vk<}a1^Qlhs%oe2qOMf= zgnGNU)OS%{J2fqkn_GTe`ONa!<#Wn|JgQh)e!sS_T30@=E>uP9y2;h9??AL1h}+sj zRlXodtB_ja^Qyd2G-~Znq$>8J3{hYCQ=k@!<_Pa)@RkR>RROier{u1W%DLqm51cB0 z_kTxAx)^nP%Zsim4^T@;b(`ed?uQz?f4TB~$cZyWu6!G=9i+f3-|bVLZ73(y0I`*$ zB3_aEe?-{_MRN-sI)pT&8b^8EI&|yWt)glj)(C5)Dq2^!P0MR9auuB`%4AfekBXiF z)i0o!;_{?ZH-!pQH_yB{^`V=SixVGgO0X?US6 zsd~jI4Bwa_ZXp%g)<`j=)BR9YvK^AAbX3Kx>T+p$xA<}ucN`Szo-|rQ9;u4QQ5GH$ zxjIm6+f(F1-a_qL#o}(0Dqf(*1Ea>)TTA_2{N#`#y*#X54{EheA-Je3wW8iG)&(wV zr#1v~pH^(wbVcpdraH>~-)T8GAxIa#ok41l666sc_|4o)`ssiu^*^K~jL}ej!yTw30#$#UI>0~fOo*gw^6 zh`3Fb3whnn)tbAF?>4F1R7xHgHMZVb>hIzpgcK=qjgEOesG9;8b){z1*F_ajpFV1* zy4~i>RhpFzG~MkkpO@}lS?I5;rlUd^RpUbXU{KwD5G~zy`MN5LE8A9fsx0xJ{d?sg zp}H+Y$*xxjDk}pGpem28>;o8a3Pou(ocQsMh0;Tsjzsmkl%W+q+MN{NIV zl_PMC)-Fh85;N3I3=lh}a?)Vxcfj`_XJAzp=2+$2a>=*Fa^`VB>z@1)oV<# zk>uQ&z_)1#t$gA3F>ODlytD#_v6QjCm$6<%zoeDK{}kxr+lF~M&EJxP{fHp*9?ecP&nEwDab?EHDG^hhBj5Ysf7v2GOma#s!#*emK4ljE zkNg_2if#+}o#b)GSnz)%CcaI@my_x@;udl=DJgxtXp@j1L(Z|$h(d5qBzw#zh4 zw};a9I-1wg>?o$XS=_>eHnp4d3q~>Awq|%t%KWHJ+WwWcEg7~S8HPdhU3R(0KLu#s zL(V;^i?*LLRNKVX)TO$C&_)B1G^L+5%)6PwUx;a5#*oN9hlVNW&@eBc{L5mh6-F`K zjv%K$IWHP5!8uQJM5oc8K2}nH2Kl)(FEN(F_7c+Pu%4bH9L2JySn3pgG5S(ZUrLse zE~U;Z@Q-xExQSmx7t{7`ZBphO&y;jKhx}3G4A->L0&&Tq+fn2^DyGRiHF{C97kw$9 zZGmvqE}_*fP0L^0K|??Oi!NVQvm_WFQoaQ>Fg=Y{k~ z9c|l@Ka}-X_Kd<;<-%9%$aiVpiZ%d$88FJ=>N4@&w1scl)Nqz(O1b@>ZofC=ucF^G zMni>T9?mcirK?kE$_|Q#*&~530L>ow8>wL&N7)8RUA;+HceBO*i1d||*-EoHITw?@ko0`Ex6|2P%_rwu zG1Ze2HvHiov~?*tuQ0TCQ6`)8(QLbV2nXLK!-OuOPa{2-^ev*>GTZ~p9 zKb?GKJO^5rP_ut6ns3qRejf19a{tr$|y*EoY18uq|SG8r@mD zZ($v5%sP7ugNL2UGrNioB>$1Mv0nBsE>l z!8hgNZPB_I5Mci)^mQ10O=SwS-c| zC^gzg{#gq9aEXt?X^b5`41@pOGOiVOc5nLL+mrCLDgLX=8bLooKjr@!qR01>f2^|p zL_d{6s>05&o(Xh`OZnr1gk0jsm>G}pIn=^GFOT?|UFwn(V>VE+lFL)XU zFiwW`y8Q3MdW*8s0?;4xHS`V9(DF|;mh`mIwCKwog#Es?l6u}q@st0*!Jd)H+W6-a z>*^FP{}p1%Kf6IyYgk4_H8z?u1tpa0a z%wp?cC~RNFXK5>>50wq zwtsZTlUl4pV$IQFwT!h?tzw794n@1wD%M&Z7Hb=8tJ=hlh;>wLV@Jlis3X{tb^5RV zMHywtIM`R)8g!!Nkk+HdtM+gpXZUwK`HON7{J-FSp4Z{;1q(nez+KFzaL&dx2j^3s z%v|q2kM@hya-K_I%Js7MthsCOpN>_y-o?35ZQW=D)Rw3#hpJ-L3fg2>+D6c8q)2DfB5$BRi93Y1M)!HOxKHO_qZ;J9tA@RX z_bpZz9N%k5v6_4QV3^BK?lrtv?LKATfMTQlsRQ~H8)pt4cuKJ`YY1eF#X|<4Tx@)r zln(Y!PKS66;i@U!vg#?HQ&0Kaddf@7Sf6|^sHc2kJ>?tMQ@%+J@`sHYHU3hy=Dd>0 z=cx_njhcME+H~G|7fn>3(%d;_{DnU^%rO%#zQ|}WcG9TxjBGGQ8BNDexa=aMX#Brl>ruwM$(N0uY%G|3*~H1lQ)gB9*Rtw#|Yr=d2Zz3^(Zxa~r>9GfcL66hLt2>BcjAyq|^BW8`QfSQDpLzCD- zYBxBX5m9-h^J%uE*#V}h4ri-mpnnzFt~8s|B)(Kfk=7WAV{CN<_DUWbbMr>#XX3mc zf1k}Qm{Yi=(QS=hXtcAj*;tOs;2hBSxyGv+?`)FYqzq^8CX<>BYBCb$bxmHtxvptZ z)1FO7H=WjWPSXWVmp9LEIicv>qU(z07tKd#Bt#9AX%9C$VCS*#%^%Dk&3$G9dr}zK zeayl}vySCjF{_o;8oQ1iX?3=`SY557@K>c1EZH6RWNUzRighY>u^1%#<67rhqpZ=^ zdDa-~d~2-rbL#@@LTjA$3v0Y}ku|}(*qUfvVokCpTbEjwSyQabvBUNi)-?Q==_>1L z>l*7?>pJ|Q3A>o#A5Al@8?9N^udJJ_o2}W{$LtpTwdpqewP}uZhjpiQm-Sm~u6Y&q zF?(^p;XWLHerjvAv)WrnSRJg6N%1JP%J8?SZdRpLWp%fXwt85{SjXb;Q9beZsNVQj z6!rr4;(8)>{vConInJ<#S!Y^j;jdC>W3+`GfDb~PCB`%A9OnnEhpdOKdDbJ=qt;{A zeCrR^0_-FFxb=kfr1dB3DeNY^(0T^@hx{4)hx`RQh#-xua_m3yK6ZCoZM|u|WxZ{! zvDRAatbbYSt#_<mzHE^>1sl^|7_Z`UHERe};WWKDV}6Us&6% zFRibvudN-{H`Y$;TWgo~oweKAW9_xRw|=mGwDwsE8{7HGAGEMvoMXFo%x+*ewBvS$ zorzuJvh5t~8kcA1+XZ%^-N{rrKc8HyF6o1tkW}j)FWe>-nwMO8-S|jbB z+2`7$u#3qi```9v>|eG8dzpNKy~{qAJ>BkO>{vAi|Fyc;xeq&4UF-ZFyHm};|F0gDT}GT)*nQ+C z=Vt6Rg8fdhN9qFXOLZIeqrxAsWY<&ddHNLgjaukD<2>v9*;$0$qn2Zzrxn=WX(je` zT7~_bR%0Khubi))9nLq-PUl-^m-C&o+u4JCj)r1?qhZ+Bs6BRNIs&_fmSNA(3hWqK ziQScYyT{93N7(6T1a>$YDLWXsPq|OK3*Be1qt6=mZOlC!5^EM~9%}()Uj$rVET}$q zSnTjv8zB33vGzdr9bz40oq+8-$GXJ2#*T`W#7>Hp#YV)=iH(f?EOu^eRBSYM^)c1Y zfTh}?osm6#8gk#tY|KjxL5uoxH4ZK6cr_WAYdUtRY>hw5J&W<-BJ4^zL~X!19P{a$ z)Y;er@+ma}qv_YwIPA#tF6K@)7~9k|V>@1uZ~dqf&PK zHhQDRk2H>#{S=LX*gbKvF-Z2}F(zU!o>j(W^8YtuhU`{o{1SWJOg3i8{?*28`~&V< zmY1yyNc*Z%zImCEY_9!;~EW6Vg zi?D}ITVpZ)>ek6vBD=~NFUn4G#!K=)H{>F(S1N*m~Yb=%B+Kd&lLz}VE zx!AebctiI1HC8(toR5t+oxQGQyoY_lni?NtZ?Ixxi`&|5ZETg@zl_iEKfw}X8+P!j zGXB5z&OA)3;!5LnPJOrT{aSXKMOkc+r3Ew$5yyTa()sGx9T~wIAUq|XA`WVX6IGwEd zI)loaujv9^LjQc7ZX()!tlM;#?$cu0GG{B<8n&)&Xq(Uiv?k)WBf58_-ReOc?@voL zgjQ)dEzl_1o5yKgCJ&{i!Zcq+SNqO_lS_65)F3~`Hm3f5+YhA(JPBc)rzQe z0TJkOqD@yKOCM(F8%zz$q09@Ilgf*GG5g;b=KGt({C?A^qx4#80adtHrq)qI`@>K( zX%|tYnCRj7$~CBp)R3xzEtrw-d}{r*r+!}-YV!4p`cqeLNOTXi@kS6y9wlBpNo<)y z1er;cm_zlrH;E2!NAE>jqQYn=k>Ox;%%xn0tLAD`CTS?uoP3}YYvD@Z$xqYq}{=vnS;u^$? zhQx&y#DMeTc5(Z-W88(W*NX@+C>|2u6AzC^#Ch@Pcuf3MJSmovOWGyvla5K3q({;x8JG-Ch9#qt zG0Eg)W->2XnY@=2Ci{}&v`M>krF4yS-E_lrlXQ!3+m>#fz94;Z`to$=bk}sxH1+Jw zUi~QLXA#^-UscV1-2xuatzeOU3?5{rSv7~4OIA%WGd3%;+7^OG^mFj2ZUc|$cJR1< zVU!Ny4p4L_sQM*nl?vuGiC=+{?gpKH4aT|$Omr`p)_q`x?gvlP17Ick#VVEcAXtT} zuqst8S(;P}Sly;su3 zZ6?^j)&d*a+TfYYq^*)=>w=A#bzG&fr3Pl0PhO>ot548-mTLOskU3%)KgS z+a_QO+Z1eRn}MxtbMPFS4W4Vy27km1y~>=`)ZmqWux-FL_EM1i3v6r82g$=gaxsv6 z3?wH5$;&`;Gm!iYBu4|u)4)sYKZBRr_8>EUg6%DAD{=b@@N(M~yuw}scCbCbpW17| zj_P6=q z0Oo>K8EB`0H`y1#o9%S)7W)Tqkfpk$+-m;_4z@GF+o)Zja=V=c-T`-_G6cp{-$Q5k0EgZJ76;BdPT{G}}be`Obe z_u0kZ{dNiXYr7O20mq~AfL#XW*yUiZT>*}?E5QftDsYrt4L)SofO&Q;c%E%*EVH== z4`kE<9;gp^57Zp*ftu?*kSPBN_@MVdW_tY$e8_vC=6Mg)hrI{V4r~Yi&3m9e>OD~Z z-Fu*p_8zGJ;XRO+0uR*Rcn{Ray$9-Vy$9+T?}7S+_dwbbJWzkXUv24A*Ug0Cl=#PU3w3tr-FTIYLf z)&<_0^$l;$+DR9BYt{m9&AP}=0vCIm)+OGib*Z-@>N{dXy3E^HI{NZ<7~#JtAX zQ0n~@mO@2hzCRU-`Ocvhv9Az2)>x|j{Vw83n01!Bj&dXJIvN>qzfoTFB>NwZejnk5 z(dY=zqdsgt6PPCo8`zX}B zqs`qEeL)U>%q6U?iQbKNkdGgCY1Y=#8t;q_a8d?qZ$i#~{UMF*X_ zO02DqHb-BPzgvejQ5E(>@Qvs%;KJx5upq)qs0zCUTpaQ2RE5P$s0xdh(DLIY(X!|> z5}xK{{jHegxr%E6c6I&0ZmvJr-3E|@m_LPD z?W<(7Y&NSyHTE@{M%~?{Ew!b2S1;0w%zL5I`ujRaZ#P^09M7gO)B)cg>U|#wwY-a% z5jo!+jFKoZhe93aVyZURG>56tT+bW})tQeopYhoUBMYL3dfJy#3& zTrb>n{cz6>`97t65orMf*aTbYXu2~)`oEgt=r1$On~Wg&!0bc`uErNnu&zvpZ(;$iu22U ztvKwV#bOUFmMbjV6VsZ|ho@yBSEo&hTlwpv*_AEPp0F+4N8E_z`e=A%`%aOzE$q@%cUHggxhiXpI#-V^@h!a8`ZpAqyTMn{&(GDYMFZe{9Vg9m-Ov zLLFc4pD4b;KTF&TJdE|J+gss?9^r^<^CZ-yY-&y zU^+zhazqIl^dW)&@Ye3pn92c5>Lq6yDmRt?qQjav&NV9Ero5|^nQaQFHQb4BQfLFb+-pjPgGIMvUO9aNm9`szi)WEZwHW3SVV? zEG@IItWLP5zw58dbt)W{K}-JE=(lXqR6h0Vzj1zG-ahhNnLi&^Wg^F17?s5ClJ~bR zXxU|7Y?0|ueg%PHYExm&9pBEHxTY1hjX`tuErkEho`VRn+O!YP;MGNce3kO;p5yoT zcNWJBnZu#d^^!Yr$MI3N!=tdh?5a(|+1?9F)B*!T=50sh;qvR2C;@(tKYu~AEL>Mj zF74-u_kkV2G~DF>u+_Ixx$-baoUm7KsooN(*-a*wc6C`6Drc8n14}8r$K}!blkVNO zc0xw<**CX`meJqSNyqcPi0mcz%2}IPFZ&hMO!~YZ>0fn=ZiSoqL_gKfv`{~XrP;1u z=nmbfU&7aXrMvZO-J^S9ZuaW|Ez*N}=vx^6q=Gh)P*FR>o(9KL*;cVtsrFDEwx_1e zw6$z)TL<3fbgFCDv-NEQ7@#w)*D}t62|BeZK;QHfy1>b*Kxdf3lQobMouJRRCfwu}k5_ zmf7WYgpNM}_feSstDbf$&FO!~l1|vsr2k`< zltHg!CkC<$EnpDj+ht}r{fhg{-0x>wCdXVv-znR*IE8M~m0nbLdQatg$q$^1R;TCE z7>{V`$h$nhPTYoHr))O-I$0up8S-+NbyLGL>!zk>*2y$s)=e!};0jD_vcDasPRPK| z3fXM)kj=IU+3b%)HruvB`#?XWq@Eq@kQCS<*-v%URMyno2WiF4@!;Y9TEdad{5ov4143 zsWjAvIzw&ceECQIWx-Dvd3+(GF)xyf z7)9HadPLo+N7O^Em7a2)TrW3BFX=6PjezUQ zf#Vtpw>1h*D-SN~5jd|<^*9{W7`Umia8gggMLoqR-*Gx#C+I|-q|fMN zeO9OFb2?R@M*(?(e`oQcPS-!6h5S)x>PtF{k%m-V)R*-YeN|_pkG!sPbgs_R`6wlC z=t3>fMYb3I{-0Fjr%M&&iP5BJG8*!;(UhPcKOg0nYRA)0tr@!w?nZpt9}`D(jx0vQ{DI zYKd;(Ro2!)W&KG|SuYDJ>*YaZ?HW|pZb4uwL1pz?>)4>RJ{`2yaY0`lAN17;L0_E`^wp_BUwt9ytJ8wM`Xc&jB-81ER6$>@ z9%af)K~4QL{hBP4)ogUs*+D^_7xdEkK`&h#^wOn4FI^t=(iLvKTQ4g^H1zS*^C6x; pGB5>t@2!J}`apTx&;diFCf_1OKge$&xt4wW<@*?@IDU)Y{x?L>(jNc- literal 0 HcmV?d00001 diff --git a/packages/delegation-process/src/resources/assets/font/Montserrat-Regular.ttf b/packages/delegation-process/src/resources/assets/font/Montserrat-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..1cd025961bc821fcbc63005dca001a4a963afb56 GIT binary patch literal 198552 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%0D)?#moV{!aX28%GAYPDSuRezXi~ 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 V=o#4tPSErC$9f&`I=02O|0lUgEYko0 literal 0 HcmV?d00001 diff --git a/packages/delegation-process/src/resources/assets/media/logo_pagopa.png b/packages/delegation-process/src/resources/assets/media/logo_pagopa.png new file mode 100644 index 0000000000000000000000000000000000000000..76b9f55b26bd82fa771e2387d21b2ab1d15a546e GIT binary patch literal 4514 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 A`v3p{ literal 0 HcmV?d00001 diff --git a/packages/delegation-process/src/resources/assets/media/pictogram_pagopa.svg b/packages/delegation-process/src/resources/assets/media/pictogram_pagopa.svg new file mode 100644 index 0000000000..52e708741e --- /dev/null +++ b/packages/delegation-process/src/resources/assets/media/pictogram_pagopa.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html b/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html new file mode 100644 index 0000000000..603eb98290 --- /dev/null +++ b/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html @@ -0,0 +1,87 @@ + + + + + + + Deroga all'erogazione + + {{{paged-pdf-polyfill}}} + + + + + + + +
+ +
+ + +

Deroga all'erogazione

+ + + +
+

Registrazione

+ +
+ In data {{submissionDate}} alle ore + {{submissionTime}}, l’Infrastruttura ha trasmesso al + delegato la richiesta di delega del delegante. +
+ +
+ In data {{activationDate}} alle ore + {{activationTime}}, l’Infrastruttura ha ricevuto la + dichiarazione di accettazione della delega (di seguito “dischiarazione + di accettazione”) inviata dal delegato all'erogazione, tramite + l'operatore amministrativo con identificativo Area Riservata + {{activatorId}}. +
+ +
+ In data {{activationDate}} alle ore + {{activationTime}}, l’Infrastruttura ha comunicato al + delegato all'erogazione la dichiarazione di accettazione. +
+ +
+ In data {{activationDate}} alle ore + {{activationTime}}, l’Infrastruttura ha registrato il + delegato all'erogazione quale soggetto titolato ad erogare l'e-service + per conto del delegante. +
+
+ + diff --git a/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html b/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html new file mode 100644 index 0000000000..e36b2480cc --- /dev/null +++ b/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html @@ -0,0 +1,80 @@ + + + + + + + Revoca della delega all’erogazione + + {{{paged-pdf-polyfill}}} + + + + + + + +
+ +
+ + +

Revoca della delega all’erogazione

+ + + +
+

Registrazione

+ +
+ In data {{revocationDate}} alle ore + {{revocationTime}}, l’infrastruttura ha ricevuto la + dichiarazione di revoca della delega (di seguito “dichiarazione di + revoca”) inviata dal delegante, tramite l’operatore amministrativo con + identificativo Area Riservata {{submitterId}}. +
+ +
+ In data {{revocationDate}} alle ore + {{revocationTime}}, l’Infrastruttura ha comunicato al + delegante e al delegato all’erogazione la dichiarazione di revoca. +
+ +
+ In data {{revocationDate}} alle ore + {{revocationTime}}, l’infrastruttura ha registrato la + revoca al delegato all’erogazione quale soggetto titolato ad erogare + l’e-service per conto del delegante. +
+
+ + diff --git a/packages/delegation-process/src/resources/templates/delegationTemplate.css b/packages/delegation-process/src/resources/templates/delegationTemplate.css new file mode 100644 index 0000000000..4e09620a97 --- /dev/null +++ b/packages/delegation-process/src/resources/templates/delegationTemplate.css @@ -0,0 +1,130 @@ +/* Load fonts */ +@font-face { + font-family: "Montserrat"; + src: url("../assets/font/Montserrat-Regular.ttf") format("truetype"); + font-weight: 400; +} + +@font-face { + font-family: "Montserrat"; + src: url("../assets/font/Montserrat-Italic.ttf") format("truetype"); + font-weight: 400; + font-style: italic; +} + +@font-face { + font-family: "Montserrat"; + src: url("../assets/font/Montserrat-Medium.ttf") format("truetype"); + font-weight: 500; +} + +@font-face { + font-family: "Montserrat"; + src: url("../assets/font/Montserrat-SemiBold.ttf") format("truetype"); + font-weight: 600; +} + +@font-face { + font-family: "Montserrat"; + src: url("../assets/font/Montserrat-Bold.ttf") format("truetype"); + font-weight: 700; +} + +@page { + margin: 1.2cm 2cm 1.4cm 2cm; + size: a4; + + @top-center { + content: element(header); + vertical-align: top; + } + + @bottom-center { + content: element(footer); + vertical-align: bottom; + } +} + +.header { + position: running(header); +} + +.footer { + position: running(footer); +} + +* { + box-sizing: border-box; +} + +body { + font-family: "Montserrat", arial, sans-serif; + font-size: 12px; + line-height: 18px; + margin: 0; +} + +strong { + font-weight: 600; +} + +.header img { + width: 85px; +} + +.header { + text-align: left; +} + +h1 { + font-size: 14px; + line-height: 16px; + margin: 0 0 16px; + font-weight: 700; +} + +h2 { + font-size: 12px; + line-height: 15px; + margin: 16px 0 8px; + font-weight: 700; +} + +.footer hr { + border: 0; + border-top: 1px solid #e3e7eb; + margin: 8px 0; + padding: 0; + height: 1px; +} + +.footer .page-number:after { + content: counter(page) " di " counter(pages); + color: #898989; +} + +.footer .pagopa { + content: ""; + display: block; + clear: both; +} + +.footer .pagopa-data { + float: left; + text-align: left; + color: #475a6d; + display: inline-block; +} + +.footer .pictogram { + float: right; +} + +.footer { + font-size: 6px; + line-height: 7.5px; +} + +.content > div { + margin-bottom: 8px; +} diff --git a/packages/delegation-process/src/routers/DelegationProducerRouter.ts b/packages/delegation-process/src/routers/DelegationProducerRouter.ts new file mode 100644 index 0000000000..8e1dd50659 --- /dev/null +++ b/packages/delegation-process/src/routers/DelegationProducerRouter.ts @@ -0,0 +1,179 @@ +import { ZodiosEndpointDefinitions } from "@zodios/core"; +import { ZodiosRouter } from "@zodios/express"; +import { delegationApi } from "pagopa-interop-api-clients"; +import { + ExpressContext, + fromAppContext, + ReadModelRepository, + userRoles, + ZodiosContext, + zodiosValidationErrorToApiProblem, + authorizationMiddleware, + initDB, + initPDFGenerator, + initFileManager, +} from "pagopa-interop-commons"; +import { unsafeBrandId } from "pagopa-interop-models"; +import { readModelServiceBuilder } from "../services/readModelService.js"; +import { config } from "../config/config.js"; +import { delegationToApiDelegation } from "../model/domain/apiConverter.js"; +import { makeApiProblem } from "../model/domain/errors.js"; +import { delegationProducerServiceBuilder } from "../services/delegationProducerService.js"; +import { + createProducerDelegationErrorMapper, + revokeDelegationErrorMapper, + approveDelegationErrorMapper, + rejectDelegationErrorMapper, +} from "../utilites/errorMappers.js"; + +const readModelService = readModelServiceBuilder( + ReadModelRepository.init(config) +); + +const pdfGenerator = await initPDFGenerator(); +const fileManager = initFileManager(config); + +const delegationProducerService = delegationProducerServiceBuilder( + initDB({ + username: config.eventStoreDbUsername, + password: config.eventStoreDbPassword, + host: config.eventStoreDbHost, + port: config.eventStoreDbPort, + database: config.eventStoreDbName, + schema: config.eventStoreDbSchema, + useSSL: config.eventStoreDbUseSSL, + }), + readModelService, + pdfGenerator, + fileManager +); + +const { ADMIN_ROLE } = userRoles; + +const delegationProducerRouter = ( + ctx: ZodiosContext +): ZodiosRouter => { + const delegationProducerRouter = ctx.router(delegationApi.producerApi.api, { + validationErrorHandler: zodiosValidationErrorToApiProblem, + }); + + delegationProducerRouter + .post( + "/producer/delegations", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + + try { + const delegation = + await delegationProducerService.createProducerDelegation( + req.body, + ctx + ); + return res + .status(200) + .json( + delegationApi.Delegation.parse( + delegationToApiDelegation(delegation) + ) + ); + } catch (error) { + const errorRes = makeApiProblem( + error, + createProducerDelegationErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + } + ) + .delete( + "/producer/delegations/:delegationId", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + + try { + const { delegationId } = req.params; + await delegationProducerService.revokeProducerDelegation( + unsafeBrandId(delegationId), + ctx + ); + + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + revokeDelegationErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + } + ) + .post( + "/producer/delegations/:delegationId/approve", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + const { delegationId } = req.params; + + try { + await delegationProducerService.approveProducerDelegation( + ctx.authData.organizationId, + unsafeBrandId(delegationId), + ctx.correlationId, + ctx.logger + ); + + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + approveDelegationErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + } + ) + .post( + "/producer/delegations/:delegationId/reject", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + const { delegationId } = req.params; + const { rejectionReason } = req.body; + + try { + await delegationProducerService.rejectProducerDelegation( + ctx.authData.organizationId, + unsafeBrandId(delegationId), + ctx.correlationId, + rejectionReason + ); + + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + rejectDelegationErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + } + ); + + return delegationProducerRouter; +}; + +export default delegationProducerRouter; diff --git a/packages/delegation-process/src/routers/DelegationRouter.ts b/packages/delegation-process/src/routers/DelegationRouter.ts new file mode 100644 index 0000000000..99f603305a --- /dev/null +++ b/packages/delegation-process/src/routers/DelegationRouter.ts @@ -0,0 +1,137 @@ +import { ZodiosEndpointDefinitions } from "@zodios/core"; +import { ZodiosRouter } from "@zodios/express"; +import { delegationApi } from "pagopa-interop-api-clients"; +import { + ExpressContext, + ReadModelRepository, + ZodiosContext, + authorizationMiddleware, + fromAppContext, + userRoles, + zodiosValidationErrorToApiProblem, +} from "pagopa-interop-commons"; +import { EServiceId, TenantId, unsafeBrandId } from "pagopa-interop-models"; +import { readModelServiceBuilder } from "../services/readModelService.js"; +import { config } from "../config/config.js"; +import { + apiDelegationKindToDelegationKind, + apiDelegationStateToDelegationState, + delegationToApiDelegation, +} from "../model/domain/apiConverter.js"; +import { makeApiProblem } from "../model/domain/errors.js"; +import { getDelegationErrorMapper } from "../utilites/errorMappers.js"; +import { delegationServiceBuilder } from "../services/delegationService.js"; + +const readModelService = readModelServiceBuilder( + ReadModelRepository.init(config) +); + +const delegationService = delegationServiceBuilder(readModelService); + +const { ADMIN_ROLE, API_ROLE, SECURITY_ROLE, M2M_ROLE, SUPPORT_ROLE } = + userRoles; + +const delegationRouter = ( + ctx: ZodiosContext +): ZodiosRouter => { + const delegationRouter = ctx.router(delegationApi.delegationApi.api, { + validationErrorHandler: zodiosValidationErrorToApiProblem, + }); + + delegationRouter + .get( + "/delegations", + authorizationMiddleware([ + ADMIN_ROLE, + API_ROLE, + SECURITY_ROLE, + M2M_ROLE, + SUPPORT_ROLE, + ]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + const { + offset, + limit, + delegateIds, + delegatorIds, + eserviceIds, + delegationStates, + kind, + } = req.query; + + try { + const delegations = await delegationService.getDelegations({ + delegateIds: delegateIds.map(unsafeBrandId), + delegatorIds: delegatorIds.map(unsafeBrandId), + delegationStates: delegationStates.map( + apiDelegationStateToDelegationState + ), + eserviceIds: eserviceIds.map(unsafeBrandId), + kind: kind && apiDelegationKindToDelegationKind(kind), + offset, + limit, + }); + + return res.status(200).send( + delegationApi.Delegations.parse({ + results: delegations.map((delegation) => + delegationToApiDelegation(delegation) + ), + totalCount: delegations.length, + }) + ); + } catch (error) { + const errorRes = makeApiProblem( + error, + getDelegationErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + } + ) + .get( + "/delegations/:delegationId", + authorizationMiddleware([ + ADMIN_ROLE, + API_ROLE, + SECURITY_ROLE, + M2M_ROLE, + SUPPORT_ROLE, + ]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + const { delegationId } = req.params; + + try { + const delegation = await delegationService.getDelegationById( + unsafeBrandId(delegationId) + ); + + return res + .status(200) + .send( + delegationApi.Delegation.parse( + delegationToApiDelegation(delegation) + ) + ); + } catch (error) { + const errorRes = makeApiProblem( + error, + getDelegationErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + } + ); + + return delegationRouter; +}; + +export default delegationRouter; diff --git a/packages/delegation-process/src/routers/HealthRouter.ts b/packages/delegation-process/src/routers/HealthRouter.ts new file mode 100644 index 0000000000..6e305f570f --- /dev/null +++ b/packages/delegation-process/src/routers/HealthRouter.ts @@ -0,0 +1,8 @@ +import { zodiosRouter } from "@zodios/express"; +import { delegationApi } from "pagopa-interop-api-clients"; + +const healthRouter = zodiosRouter(delegationApi.healthApi.api); + +healthRouter.get("/status", async (_, res) => res.status(200).send()); + +export default healthRouter; diff --git a/packages/delegation-process/src/services/delegationContractBuilder.ts b/packages/delegation-process/src/services/delegationContractBuilder.ts new file mode 100644 index 0000000000..1e20e21f4e --- /dev/null +++ b/packages/delegation-process/src/services/delegationContractBuilder.ts @@ -0,0 +1,185 @@ +import { fileURLToPath } from "url"; +import path from "path"; +import { + dateAtRomeZone, + FileManager, + formatDateyyyyMMddHHmmss, + Logger, + PDFGenerator, + timeAtRomeZone, +} from "pagopa-interop-commons"; +import { + Delegation, + DelegationContractDocument, + DelegationContractId, + EService, + generateId, + Tenant, +} from "pagopa-interop-models"; +import { DelegationProcessConfig } from "../config/config.js"; + +const CONTENT_TYPE_PDF = "application/pdf"; +const DELEGATION_ACTIVATION_CONTRACT_PRETTY_NAME = "Delega"; +const DELEGATION_REVOCATION_CONTRACT_PRETTY_NAME = "Revoca della delega"; + +const createDelegationDocumentName = ( + documentCreatedAt: Date, + documentType: "activation" | "revocation" +): string => + `${formatDateyyyyMMddHHmmss( + documentCreatedAt + )}_delegation_${documentType}_contract.pdf`; + +const filename = fileURLToPath(import.meta.url); +const dirname = path.dirname(filename); + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export const contractBuilder = { + createActivationContract: async ({ + delegation, + delegator, + delegate, + eservice, + pdfGenerator, + fileManager, + config, + logger, + }: { + delegation: Delegation; + delegator: Tenant; + delegate: Tenant; + eservice: EService; + pdfGenerator: PDFGenerator; + fileManager: FileManager; + config: DelegationProcessConfig; + logger: Logger; + }): Promise => { + const templateFilePath = path.resolve( + dirname, + "..", + "resources/templates", + "delegationApprovedTemplate.html" + ); + const documentCreatedAt = new Date(); + const todayDate = dateAtRomeZone(documentCreatedAt); + const todayTime = timeAtRomeZone(documentCreatedAt); + + const documentId = generateId(); + const documentName = createDelegationDocumentName( + documentCreatedAt, + "activation" + ); + + const submissionDate = dateAtRomeZone(delegation.stamps.submission.when); + const submissionTime = timeAtRomeZone(delegation.stamps.submission.when); + + const pdfBuffer = await pdfGenerator.generate(templateFilePath, { + todayDate, + todayTime, + delegationId: delegation.id, + delegatorName: delegator.name, + delegatorCode: delegator.externalId.value, + delegateName: delegate.name, + delegateCode: delegate.externalId.value, + submitterId: delegation.stamps.submission.who, + eServiceName: eservice.name, + eServiceId: eservice.id, + submissionDate, + submissionTime, + activationDate: todayDate, + activationTime: todayTime, + activatorId: delegate.id, + }); + + const documentPath = await fileManager.storeBytes( + { + bucket: config.s3Bucket, + path: `${config.delegationDocumentPath}/${delegation.id}`, + resourceId: documentId, + name: documentName, + content: pdfBuffer, + }, + logger + ); + + return { + id: documentId, + name: documentName, + prettyName: DELEGATION_ACTIVATION_CONTRACT_PRETTY_NAME, + contentType: CONTENT_TYPE_PDF, + path: documentPath, + createdAt: documentCreatedAt, + }; + }, + createRevocationContract: async ({ + delegation, + delegator, + delegate, + eservice, + pdfGenerator, + fileManager, + config, + logger, + }: { + delegation: Delegation; + delegator: Tenant; + delegate: Tenant; + eservice: EService; + pdfGenerator: PDFGenerator; + fileManager: FileManager; + config: DelegationProcessConfig; + logger: Logger; + }): Promise => { + const templateFilePath = path.resolve( + dirname, + "..", + "resources/templates", + "delegationRevokedTemplate.html" + ); + const documentCreatedAt = new Date(); + const todayDate = dateAtRomeZone(documentCreatedAt); + const todayTime = timeAtRomeZone(documentCreatedAt); + + const documentId = generateId(); + const documentName = createDelegationDocumentName( + documentCreatedAt, + "revocation" + ); + + const pdfBuffer = await pdfGenerator.generate(templateFilePath, { + todayDate, + todayTime, + delegationId: delegation.id, + delegatorName: delegator.name, + delegatorCode: delegator.externalId.value, + delegateName: delegate.name, + delegateCode: delegate.externalId.value, + submitterId: delegation.stamps.submission.who, + eServiceName: eservice.name, + eServiceId: eservice.id, + revocationDate: todayDate, + revocationTime: todayTime, + activatorId: delegate.id, + }); + + const documentPath = await fileManager.storeBytes( + { + bucket: config.s3Bucket, + path: `${config.delegationDocumentPath}/${delegation.id}`, + resourceId: documentId, + name: documentName, + content: pdfBuffer, + }, + logger + ); + + return { + id: documentId, + name: documentName, + prettyName: DELEGATION_REVOCATION_CONTRACT_PRETTY_NAME, + contentType: CONTENT_TYPE_PDF, + path: documentPath, + createdAt: documentCreatedAt, + }; + }, +}; diff --git a/packages/delegation-process/src/services/delegationProducerService.ts b/packages/delegation-process/src/services/delegationProducerService.ts new file mode 100644 index 0000000000..dc4ada7d38 --- /dev/null +++ b/packages/delegation-process/src/services/delegationProducerService.ts @@ -0,0 +1,280 @@ +import { delegationApi } from "pagopa-interop-api-clients"; +import { + AppContext, + DB, + eventRepository, + FileManager, + Logger, + PDFGenerator, + WithLogger, +} from "pagopa-interop-commons"; +import { + CorrelationId, + Delegation, + DelegationId, + delegationEventToBinaryDataV2, + delegationKind, + EService, + delegationState, + EServiceId, + generateId, + Tenant, + TenantId, + unsafeBrandId, +} from "pagopa-interop-models"; +import { eserviceNotFound, tenantNotFound } from "../model/domain/errors.js"; +import { + toCreateEventProducerDelegationSubmitted, + toCreateEventProducerDelegationRevoked, + toCreateEventProducerDelegationApproved, + toCreateEventProducerDelegationRejected, +} from "../model/domain/toEvent.js"; +import { config } from "../config/config.js"; +import { ReadModelService } from "./readModelService.js"; +import { + assertDelegationIsRevokable, + assertDelegationNotExists, + assertDelegatorIsIPA, + assertDelegatorIsNotDelegate, + assertEserviceExists, + assertTenantAllowedToReceiveProducerDelegation, + assertIsDelegate, + assertIsState, +} from "./validators.js"; +import { contractBuilder } from "./delegationContractBuilder.js"; +import { retrieveDelegationById } from "./delegationService.js"; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function delegationProducerServiceBuilder( + dbInstance: DB, + readModelService: ReadModelService, + pdfGenerator: PDFGenerator, + fileManager: FileManager +) { + const retrieveTenantById = async (tenantId: TenantId): Promise => { + const tenant = await readModelService.getTenantById(tenantId); + if (!tenant) { + throw tenantNotFound(tenantId); + } + return tenant; + }; + + const retrieveEserviceById = async (id: EServiceId): Promise => { + const eservice = await readModelService.getEServiceById(id); + if (!eservice) { + throw eserviceNotFound(id); + } + return eservice.data; + }; + + const repository = eventRepository(dbInstance, delegationEventToBinaryDataV2); + return { + async createProducerDelegation( + delegationSeed: delegationApi.DelegationSeed, + { authData, logger, correlationId }: WithLogger + ): Promise { + const delegatorId = unsafeBrandId(authData.organizationId); + const delegateId = unsafeBrandId(delegationSeed.delegateId); + const eserviceId = unsafeBrandId(delegationSeed.eserviceId); + + logger.info( + `Creating a delegation for tenant:${delegationSeed.delegateId} by producer:${delegatorId}` + ); + + assertDelegatorIsNotDelegate(delegatorId, delegateId); + + const delegator = await retrieveTenantById(delegatorId); + const delegate = await retrieveTenantById(delegateId); + + assertTenantAllowedToReceiveProducerDelegation(delegate); + await assertDelegatorIsIPA(delegator); + await assertEserviceExists(delegatorId, eserviceId, readModelService); + await assertDelegationNotExists( + delegator, + eserviceId, + delegationKind.delegatedProducer, + readModelService + ); + + const creationDate = new Date(); + const delegation = { + id: generateId(), + delegatorId, + delegateId, + eserviceId, + createdAt: creationDate, + submittedAt: creationDate, + state: delegationState.waitingForApproval, + kind: delegationKind.delegatedProducer, + stamps: { + submission: { + who: delegatorId, + when: creationDate, + }, + }, + }; + + await repository.createEvent( + toCreateEventProducerDelegationSubmitted(delegation, correlationId) + ); + + return delegation; + }, + async revokeProducerDelegation( + delegationId: DelegationId, + { authData, logger, correlationId }: WithLogger + ): Promise { + const delegatorId = unsafeBrandId(authData.organizationId); + logger.info( + `Revoking delegation:${delegationId} by producer:${delegatorId}` + ); + + const currentDelegation = await retrieveDelegationById( + readModelService, + delegationId + ); + assertDelegationIsRevokable(currentDelegation.data, delegatorId); + + const [delegator, delegate, eservice] = await Promise.all([ + retrieveTenantById(currentDelegation.data.delegatorId), + retrieveTenantById(currentDelegation.data.delegateId), + retrieveEserviceById(currentDelegation.data.eserviceId), + ]); + + const revocationContract = await contractBuilder.createRevocationContract( + { + delegation: currentDelegation.data, + delegator, + delegate, + eservice, + pdfGenerator, + fileManager, + config, + logger, + } + ); + + const now = new Date(); + const revokedDelegation = { + ...currentDelegation.data, + state: delegationState.revoked, + revokedAt: now, + revocationContract, + stamps: { + ...currentDelegation.data.stamps, + revocation: { + who: delegatorId, + when: now, + }, + }, + }; + + await repository.createEvent( + toCreateEventProducerDelegationRevoked( + revokedDelegation, + currentDelegation.metadata.version, + correlationId + ) + ); + + return revokedDelegation; + }, + async approveProducerDelegation( + delegateId: TenantId, + delegationId: DelegationId, + correlationId: CorrelationId, + logger: Logger + ): Promise { + const { data: delegation, metadata } = await retrieveDelegationById( + readModelService, + delegationId + ); + + const [delegator, delegate, eservice] = await Promise.all([ + retrieveTenantById(delegation.delegatorId), + retrieveTenantById(delegation.delegateId), + retrieveEserviceById(delegation.eserviceId), + ]); + + assertIsDelegate(delegation, delegateId); + assertIsState(delegationState.waitingForApproval, delegation); + + const activationContract = await contractBuilder.createActivationContract( + { + delegation, + delegator, + delegate, + eservice, + pdfGenerator, + fileManager, + config, + logger, + } + ); + + const now = new Date(); + + await repository.createEvent( + toCreateEventProducerDelegationApproved( + { + data: { + ...delegation, + state: delegationState.active, + approvedAt: now, + activationContract, + stamps: { + ...delegation.stamps, + activation: { + who: delegateId, + when: now, + }, + }, + }, + metadata, + }, + correlationId + ) + ); + }, + async rejectProducerDelegation( + delegateId: TenantId, + delegationId: DelegationId, + correlationId: CorrelationId, + rejectionReason: string + ): Promise { + const { data: delegation, metadata } = await retrieveDelegationById( + readModelService, + delegationId + ); + + assertIsDelegate(delegation, delegateId); + assertIsState(delegationState.waitingForApproval, delegation); + + await repository.createEvent( + toCreateEventProducerDelegationRejected( + { + data: { + ...delegation, + state: delegationState.rejected, + rejectedAt: new Date(), + rejectionReason, + stamps: { + ...delegation.stamps, + rejection: { + who: delegateId, + when: new Date(), + }, + }, + }, + metadata, + }, + correlationId + ) + ); + }, + }; +} + +export type DelegationProducerService = ReturnType< + typeof delegationProducerServiceBuilder +>; diff --git a/packages/delegation-process/src/services/delegationService.ts b/packages/delegation-process/src/services/delegationService.ts new file mode 100644 index 0000000000..c5dfda5677 --- /dev/null +++ b/packages/delegation-process/src/services/delegationService.ts @@ -0,0 +1,63 @@ +import { + Delegation, + DelegationId, + DelegationKind, + DelegationState, + EServiceId, + TenantId, + WithMetadata, +} from "pagopa-interop-models"; +import { delegationNotFound } from "../model/domain/errors.js"; +import { ReadModelService } from "./readModelService.js"; + +export const retrieveDelegationById = async ( + readModelService: ReadModelService, + delegationId: DelegationId +): Promise> => { + const delegation = await readModelService.getDelegationById(delegationId); + if (!delegation?.data) { + throw delegationNotFound(delegationId); + } + return delegation; +}; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function delegationServiceBuilder(readModelService: ReadModelService) { + return { + async getDelegationById(delegationId: DelegationId): Promise { + const delegation = await retrieveDelegationById( + readModelService, + delegationId + ); + return delegation.data; + }, + // eslint-disable-next-line max-params + async getDelegations({ + delegateIds, + delegatorIds, + delegationStates, + eserviceIds, + kind, + offset, + limit, + }: { + delegateIds: TenantId[]; + delegatorIds: TenantId[]; + delegationStates: DelegationState[]; + eserviceIds: EServiceId[]; + kind: DelegationKind | undefined; + offset: number; + limit: number; + }): Promise { + return readModelService.getDelegations({ + delegateIds, + delegatorIds, + eserviceIds, + delegationStates, + kind, + offset, + limit, + }); + }, + }; +} diff --git a/packages/delegation-process/src/services/readModelService.ts b/packages/delegation-process/src/services/readModelService.ts new file mode 100644 index 0000000000..bacc278f27 --- /dev/null +++ b/packages/delegation-process/src/services/readModelService.ts @@ -0,0 +1,251 @@ +import { Filter, WithId } from "mongodb"; +import { + EServiceCollection, + ReadModelFilter, + ReadModelRepository, +} from "pagopa-interop-commons"; +import { + Delegation, + DelegationId, + DelegationKind, + DelegationState, + EService, + EServiceId, + EServiceReadModel, + genericInternalError, + Tenant, + TenantId, + WithMetadata, +} from "pagopa-interop-models"; +import { z } from "zod"; +import { GetDelegationsFilters } from "../model/domain/models.js"; + +const toReadModelFilter = ( + filters: GetDelegationsFilters +): ReadModelFilter => { + const { delegateId, delegatorId, eserviceId, delegationKind, states } = + filters; + + const delegatorIdFilter = delegatorId + ? { + "data.delegatorId": { $eq: delegatorId }, + } + : {}; + const delegateIdFilter = delegateId + ? { + "data.delegateId": { $eq: delegateId }, + } + : {}; + const eserviceIdFilter = eserviceId + ? { + "data.eserviceId": { $eq: eserviceId }, + } + : {}; + const delegationKindFilter = delegationKind + ? { + "data.kind": { $eq: delegationKind }, + } + : {}; + const stateFilter = + states && states.length > 0 + ? { + "data.state": { $in: states }, + } + : {}; + + return { + ...delegatorIdFilter, + ...delegateIdFilter, + ...eserviceIdFilter, + ...delegationKindFilter, + ...stateFilter, + }; +}; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function readModelServiceBuilder( + readModelRepository: ReadModelRepository +) { + const delegations = readModelRepository.delegations; + const eservices = readModelRepository.eservices; + const tenants = readModelRepository.tenants; + + return { + async getEService( + eservices: EServiceCollection, + filter: Filter>> + ): Promise | undefined> { + const data = await eservices.findOne(filter, { + projection: { data: true, metadata: true }, + }); + if (!data) { + return undefined; + } else { + const result = z + .object({ + metadata: z.object({ version: z.number() }), + data: EService, + }) + .safeParse(data); + if (!result.success) { + throw genericInternalError( + `Unable to parse eService item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + return { + data: result.data.data, + metadata: { version: result.data.metadata.version }, + }; + } + }, + async getDelegationById( + id: DelegationId + ): Promise | undefined> { + const data = await delegations.findOne( + { "data.id": id }, + { + projection: { data: true, metadata: true }, + } + ); + if (!data) { + return undefined; + } + + const result = Delegation.safeParse(data.data); + if (!result.success) { + throw genericInternalError( + `Unable to parse delegation item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + return data; + }, + async findDelegations( + filters: GetDelegationsFilters + ): Promise { + const results = await delegations + .aggregate([{ $match: toReadModelFilter(filters) }], { + allowDiskUse: true, + }) + .toArray(); + + if (!results) { + return []; + } + + return results.map((res) => { + const result = Delegation.safeParse(res.data); + if (!result.success) { + throw genericInternalError( + `Unable to parse delegation item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(res)} ` + ); + } + return result.data; + }); + }, + async getEServiceById( + id: EServiceId + ): Promise | undefined> { + return this.getEService(eservices, { "data.id": id }); + }, + async createDelegation(delegation: Delegation): Promise { + await delegations.insertOne({ + data: delegation, + metadata: { version: 0 }, + }); + }, + async getTenantById(tenantId: string): Promise { + const data = await tenants.findOne( + { "data.id": tenantId }, + { projection: { data: true } } + ); + + if (data) { + const result = Tenant.safeParse(data.data); + + if (!result.success) { + throw genericInternalError( + `Unable to parse tenant item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + + return result.data; + } + return undefined; + }, + async getDelegations({ + delegateIds, + delegatorIds, + eserviceIds, + delegationStates, + kind, + offset, + limit, + }: { + delegateIds: TenantId[]; + delegatorIds: TenantId[]; + eserviceIds: EServiceId[]; + delegationStates: DelegationState[]; + kind: DelegationKind | undefined; + offset: number; + limit: number; + }): Promise { + const aggregationPipeline = [ + { + $match: { + ...ReadModelRepository.arrayToFilter(delegateIds, { + "data.delegateId": { $in: delegateIds }, + }), + ...ReadModelRepository.arrayToFilter(delegatorIds, { + "data.delegatorId": { $in: delegatorIds }, + }), + ...ReadModelRepository.arrayToFilter(eserviceIds, { + "data.eserviceId": { $in: eserviceIds }, + }), + ...ReadModelRepository.arrayToFilter(delegationStates, { + "data.state": { $in: delegationStates }, + }), + ...(kind && { + "data.kind": kind, + }), + } satisfies ReadModelFilter, + }, + { + $project: { + data: 1, + }, + }, + ]; + + const aggregationWithOffsetLimit = [ + ...aggregationPipeline, + { $skip: offset }, + { $limit: limit }, + ]; + + const data = await delegations + .aggregate(aggregationWithOffsetLimit, { allowDiskUse: true }) + .toArray(); + const result = z.array(Delegation).safeParse(data.map((a) => a.data)); + + if (!result.success) { + throw genericInternalError( + `Unable to parse delegations: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + + return result.data; + }, + }; +} + +export type ReadModelService = ReturnType; diff --git a/packages/delegation-process/src/services/validators.ts b/packages/delegation-process/src/services/validators.ts new file mode 100644 index 0000000000..c5a6437d4a --- /dev/null +++ b/packages/delegation-process/src/services/validators.ts @@ -0,0 +1,144 @@ +import { + Delegation, + DelegationKind, + DelegationState, + delegationState, + EServiceId, + PUBLIC_ADMINISTRATIONS_IDENTIFIER, + Tenant, + TenantId, +} from "pagopa-interop-models"; +import { + delegationAlreadyExists, + delegationNotRevokable, + delegatorAndDelegateSameIdError, + delegatorNotAllowToRevoke, + differentEServiceProducer, + eserviceNotFound, + incorrectState, + invalidExternalOriginError, + operationRestrictedToDelegate, + tenantNotAllowedToDelegation, + tenantNotFound, +} from "../model/domain/errors.js"; +import { ReadModelService } from "./readModelService.js"; + +/* ========= STATES ========= */ +export const delegationNotActivableStates: DelegationState[] = [ + delegationState.rejected, + delegationState.revoked, +]; + +export const activeDelegationStates: DelegationState[] = [ + delegationState.waitingForApproval, + delegationState.active, +]; + +export const assertEserviceExists = async ( + delegatorId: TenantId, + eserviceId: EServiceId, + readModelService: ReadModelService +): Promise => { + const eservice = await readModelService.getEServiceById(eserviceId); + if (!eservice) { + throw eserviceNotFound(eserviceId); + } + + if (eservice.data.producerId !== delegatorId) { + throw differentEServiceProducer(delegatorId); + } +}; + +export const assertDelegatorIsNotDelegate = ( + delegatorId: TenantId, + delegateId: TenantId +): void => { + if (delegatorId === delegateId) { + throw delegatorAndDelegateSameIdError(); + } +}; + +export const assertDelegatorIsIPA = async ( + delegator?: Tenant +): Promise => { + if (delegator?.externalId?.origin !== PUBLIC_ADMINISTRATIONS_IDENTIFIER) { + throw invalidExternalOriginError(delegator?.externalId?.origin); + } +}; + +export const assertTenantAllowedToReceiveProducerDelegation = ( + tenant: Tenant +): void => { + const delegationFeature = tenant.features.find( + (f) => f.type === "DelegatedProducer" + ); + + if (!delegationFeature) { + throw tenantNotAllowedToDelegation(tenant.id); + } +}; + +export const assertTenantExists = async ( + tenantId: TenantId, + readModelService: ReadModelService +): Promise => { + const tenant = await readModelService.getTenantById(tenantId); + if (!tenant) { + throw tenantNotFound(tenantId); + } +}; + +export const assertDelegationIsRevokable = ( + delegation: Delegation, + expectedDelegatorId: TenantId +): void => { + if (delegation.delegatorId !== expectedDelegatorId) { + throw delegatorNotAllowToRevoke(delegation); + } + + if (!activeDelegationStates.includes(delegation.state)) { + throw delegationNotRevokable(delegation); + } +}; + +export const assertDelegationNotExists = async ( + delegator: Tenant, + eserviceId: EServiceId, + delegationKind: DelegationKind, + readModelService: ReadModelService +): Promise => { + const delegatorId = delegator.id; + + const delegations = await readModelService.findDelegations({ + delegatorId, + eserviceId, + delegationKind, + states: [delegationState.active, delegationState.waitingForApproval], + }); + + if (delegations.length > 0) { + throw delegationAlreadyExists(delegatorId, eserviceId, delegationKind); + } +}; + +export const assertIsDelegate = ( + delegation: Delegation, + delegateId: TenantId +): void => { + if (delegation.delegateId !== delegateId) { + throw operationRestrictedToDelegate(delegateId, delegation.id); + } +}; + +export const assertIsState = ( + state: DelegationState, + delegation: Delegation +): void => { + if (delegation.state !== state) { + throw incorrectState( + delegation.id, + delegation.state, + delegationState.waitingForApproval + ); + } +}; diff --git a/packages/delegation-process/src/utilites/errorMappers.ts b/packages/delegation-process/src/utilites/errorMappers.ts new file mode 100644 index 0000000000..b009d21344 --- /dev/null +++ b/packages/delegation-process/src/utilites/errorMappers.ts @@ -0,0 +1,62 @@ +/* eslint-disable sonarjs/no-identical-functions */ +import { constants } from "http2"; +import { ApiError, CommonErrorCodes } from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { ErrorCodes as LocalErrorCodes } from "../model/domain/errors.js"; + +type ErrorCodes = LocalErrorCodes | CommonErrorCodes; + +const { + HTTP_STATUS_INTERNAL_SERVER_ERROR, + HTTP_STATUS_NOT_FOUND, + HTTP_STATUS_BAD_REQUEST, + HTTP_STATUS_FORBIDDEN, + HTTP_STATUS_UNAUTHORIZED, +} = constants; + +export const getDelegationByIdsrrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("delegationNotFound", () => HTTP_STATUS_NOT_FOUND) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const createProducerDelegationErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with( + "eserviceNotFound", + "delegationAlreadyExists", + "tenantNotFound", + "invalidDelegatorAndDelegateIds", + "invalidExternalOriginId", + "tenantNotAllowedToDelegation", + () => HTTP_STATUS_BAD_REQUEST + ) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const getDelegationErrorMapper = (error: ApiError): number => + match(error.code) + .with("delegationNotFound", () => HTTP_STATUS_NOT_FOUND) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const revokeDelegationErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("delegationNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("delegationNotRevokable", () => HTTP_STATUS_FORBIDDEN) + .with("operationNotAllowOnDelegation", () => HTTP_STATUS_UNAUTHORIZED) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const approveDelegationErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("operationRestrictedToDelegate", () => HTTP_STATUS_FORBIDDEN) + .with("incorrectState", () => HTTP_STATUS_BAD_REQUEST) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const rejectDelegationErrorMapper = approveDelegationErrorMapper; diff --git a/packages/delegation-process/test/.eslintrc.json b/packages/delegation-process/test/.eslintrc.json new file mode 100644 index 0000000000..6135a5ce08 --- /dev/null +++ b/packages/delegation-process/test/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "extends": ["../../../.eslintrc.cjs"], + "rules": { + "functional/immutable-data": "off", + "sonarjs/no-identical-functions": "off" + } +} diff --git a/packages/delegation-process/test/approveProducerDelegation.test.ts b/packages/delegation-process/test/approveProducerDelegation.test.ts new file mode 100644 index 0000000000..c88b48a689 --- /dev/null +++ b/packages/delegation-process/test/approveProducerDelegation.test.ts @@ -0,0 +1,247 @@ +/* eslint-disable functional/no-let */ +import { + decodeProtobufPayload, + getMockDelegationProducer, + getMockTenant, + getMockEService, +} from "pagopa-interop-commons-test/index.js"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { + ProducerDelegationApprovedV2, + DelegationContractId, + DelegationId, + EService, + generateId, + Tenant, + toDelegationV2, + unsafeBrandId, +} from "pagopa-interop-models"; +import { delegationState } from "pagopa-interop-models"; +import { + formatDateyyyyMMddHHmmss, + genericLogger, +} from "pagopa-interop-commons"; +import { + delegationNotFound, + operationRestrictedToDelegate, + incorrectState, +} from "../src/model/domain/errors.js"; +import { config } from "../src/config/config.js"; +import { contractBuilder } from "../src/services/delegationContractBuilder.js"; +import { + addOneDelegation, + addOneTenant, + addOneEservice, + delegationProducerService, + fileManager, + readLastDelegationEvent, + pdfGenerator, + flushPDFMetadata, +} from "./utils.js"; + +describe("approve delegation", () => { + const currentExecutionTime = new Date(); + beforeAll(async () => { + vi.useFakeTimers(); + vi.setSystemTime(currentExecutionTime); + }); + + let delegate: Tenant; + let delegator: Tenant; + let eservice: EService; + + beforeEach(async () => { + delegate = getMockTenant(); + delegator = getMockTenant(); + eservice = getMockEService(); + await addOneTenant(delegate); + await addOneTenant(delegator); + await addOneEservice(eservice); + }); + + it("should approve delegation if validations succeed", async () => { + const delegationId = generateId(); + + const delegation = getMockDelegationProducer({ + id: delegationId, + state: "WaitingForApproval", + delegateId: delegate.id, + delegatorId: delegator.id, + eserviceId: eservice.id, + }); + await addOneDelegation(delegation); + const { version } = await readLastDelegationEvent(delegation.id); + expect(version).toBe("0"); + + await delegationProducerService.approveProducerDelegation( + delegate.id, + delegation.id, + generateId(), + genericLogger + ); + + const event = await readLastDelegationEvent(delegation.id); + expect(event.version).toBe("1"); + + const { delegation: actualDelegation } = decodeProtobufPayload({ + messageType: ProducerDelegationApprovedV2, + payload: event.data, + }); + + const expectedContractFilePath = ( + await fileManager.listFiles(config.s3Bucket, genericLogger) + )[0]; + + const documentId = unsafeBrandId( + expectedContractFilePath.split("/")[2] + ); + + const expectedDelegation = { + ...toDelegationV2({ + ...delegation, + state: delegationState.active, + approvedAt: currentExecutionTime, + stamps: { + ...delegation.stamps, + activation: { + who: delegate.id, + when: currentExecutionTime, + }, + }, + activationContract: { + id: documentId, + contentType: "application/pdf", + createdAt: currentExecutionTime, + name: `${formatDateyyyyMMddHHmmss( + currentExecutionTime + )}_delegation_activation_contract.pdf`, + path: expectedContractFilePath, + prettyName: "Delega", + }, + }), + }; + expect(actualDelegation).toEqual(expectedDelegation); + + const actualContract = await fileManager.get( + config.s3Bucket, + expectedContractFilePath, + genericLogger + ); + + const { path: expectedContractPath } = + await contractBuilder.createActivationContract({ + delegation, + delegator, + delegate, + eservice, + pdfGenerator, + fileManager, + config, + logger: genericLogger, + }); + + const expectedContract = await fileManager.get( + config.s3Bucket, + expectedContractPath, + genericLogger + ); + + expect(flushPDFMetadata(actualContract, currentExecutionTime)).toEqual( + flushPDFMetadata(expectedContract, currentExecutionTime) + ); + }); + + it("should throw delegationNotFound when delegation doesn't exist", async () => { + const delegateId = getMockTenant().id; + const nonExistentDelegationId = + unsafeBrandId("non-existent-id"); + + await expect( + delegationProducerService.approveProducerDelegation( + delegateId, + nonExistentDelegationId, + generateId(), + genericLogger + ) + ).rejects.toThrow(delegationNotFound(nonExistentDelegationId)); + }); + + it("should throw operationRestrictedToDelegate when approver is not the delegate", async () => { + const wrongDelegate = getMockTenant(); + await addOneTenant(wrongDelegate); + const delegation = getMockDelegationProducer({ + state: "WaitingForApproval", + delegateId: delegate.id, + delegatorId: delegator.id, + eserviceId: eservice.id, + }); + await addOneDelegation(delegation); + + await expect( + delegationProducerService.approveProducerDelegation( + wrongDelegate.id, + delegation.id, + generateId(), + genericLogger + ) + ).rejects.toThrow( + operationRestrictedToDelegate(wrongDelegate.id, delegation.id) + ); + }); + + it("should throw incorrectState when delegation is not in WaitingForApproval state", async () => { + const delegation = getMockDelegationProducer({ + state: "Active", + delegateId: delegate.id, + delegatorId: delegator.id, + eserviceId: eservice.id, + }); + await addOneDelegation(delegation); + + await expect( + delegationProducerService.approveProducerDelegation( + delegate.id, + delegation.id, + generateId(), + genericLogger + ) + ).rejects.toThrow( + incorrectState( + delegation.id, + delegationState.active, + delegationState.waitingForApproval + ) + ); + }); + + it("should generete a pdf document for a delegation", async () => { + const delegation = getMockDelegationProducer({ + state: "WaitingForApproval", + delegateId: delegate.id, + delegatorId: delegator.id, + eserviceId: eservice.id, + }); + await addOneDelegation(delegation); + const { version } = await readLastDelegationEvent(delegation.id); + expect(version).toBe("0"); + + await delegationProducerService.approveProducerDelegation( + delegate.id, + delegation.id, + unsafeBrandId("9999"), + genericLogger + ); + + const contracts = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + + const hasActivationContract = contracts.some( + (contract) => + contract.includes("activation") && contract.includes(delegation.id) + ); + + expect(hasActivationContract).toBeTruthy(); + }); +}); diff --git a/packages/delegation-process/test/createProducerDelegation.test.ts b/packages/delegation-process/test/createProducerDelegation.test.ts new file mode 100644 index 0000000000..97b0cec3c2 --- /dev/null +++ b/packages/delegation-process/test/createProducerDelegation.test.ts @@ -0,0 +1,564 @@ +import { fail } from "assert"; +import { genericLogger } from "pagopa-interop-commons"; +import { + decodeProtobufPayload, + getMockDelegationProducer, + getMockEService, + getMockTenant, + getRandomAuthData, + randomArrayItem, +} from "pagopa-interop-commons-test"; +import { + Delegation, + DelegationId, + delegationKind, + delegationState, + ProducerDelegationSubmittedV2, + EServiceId, + generateId, + TenantId, + toDelegationV2, +} from "pagopa-interop-models"; +import { describe, expect, it, vi } from "vitest"; +import { + delegationAlreadyExists, + delegatorAndDelegateSameIdError, + differentEServiceProducer, + eserviceNotFound, + invalidExternalOriginError, + tenantNotAllowedToDelegation, + tenantNotFound, +} from "../src/model/domain/errors.js"; + +import { + activeDelegationStates, + delegationNotActivableStates, +} from "../src/services/validators.js"; +import { + addOneDelegation, + addOneEservice, + addOneTenant, + delegationProducerService, + readLastDelegationEvent, +} from "./utils.js"; + +/** + * Validates the creation of a delegation by comparing the actual delegation + * with the expected delegation. It ensures that the delegation IDs are defined + * and equal, and verifies that the last delegation event matches the expected + * delegation data. + * + * @param actualDelegation - The actual delegation object to be validated, + * typically a response from an API. + * @param expectedDelegation - The expected delegation object to compare against. + * @returns A promise that resolves to void. + * @throws Will fail if the delegation is not found in the event store. + */ +const expectedDelegationCreation = async ( + actualDelegation: Delegation, + expectedDelegation: Delegation +): Promise => { + expect(actualDelegation.id).toBeDefined(); + expect(expectedDelegation.id).toBeDefined(); + expect(actualDelegation.id).toEqual(expectedDelegation.id); + + const lastDelegationEvent = await readLastDelegationEvent( + actualDelegation.id + ); + + if (!lastDelegationEvent) { + fail("Creation fails: delegation not found in event-store"); + } + + const actualDelegationData = decodeProtobufPayload({ + messageType: ProducerDelegationSubmittedV2, + payload: lastDelegationEvent.data, + }); + + expect(actualDelegation).toMatchObject(expectedDelegation); + expect(actualDelegationData.delegation).toEqual( + toDelegationV2(expectedDelegation) + ); +}; + +describe("create delegation", () => { + it("should create a delegation if not exists", async () => { + const currentExecutionTime = new Date(); + vi.useFakeTimers(); + vi.setSystemTime(currentExecutionTime); + + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "anythings", + }, + }; + + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: currentExecutionTime, + }, + ], + }; + const eservice = getMockEService(generateId(), delegatorId); + + await addOneTenant(delegator); + await addOneTenant(delegate); + await addOneEservice(eservice); + + const actualDelegation = + await delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: eservice.id, + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ); + + const expectedDelegation: Delegation = { + id: actualDelegation.id, + delegatorId, + delegateId: delegate.id, + eserviceId: eservice.id, + kind: delegationKind.delegatedProducer, + state: delegationState.waitingForApproval, + createdAt: currentExecutionTime, + submittedAt: currentExecutionTime, + stamps: { + submission: { + who: delegatorId, + when: currentExecutionTime, + }, + }, + }; + + await expectedDelegationCreation(actualDelegation, expectedDelegation); + vi.useRealTimers(); + }); + + it("should create a delegation if already exists the same delegation in status Rejected or Revoked", async () => { + const currentExecutionTime = new Date(); + vi.useFakeTimers(); + vi.setSystemTime(currentExecutionTime); + + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "anythings", + }, + }; + + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: currentExecutionTime, + }, + ], + }; + const eservice = getMockEService(generateId(), delegatorId); + + const existentDelegation = { + ...getMockDelegationProducer({ + id: generateId(), + delegatorId, + delegateId: delegate.id, + eserviceId: eservice.id, + }), + state: randomArrayItem(delegationNotActivableStates), + }; + + await addOneTenant(delegator); + await addOneTenant(delegate); + await addOneEservice(eservice); + await addOneDelegation(existentDelegation); + + const actualDelegation = + await delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: eservice.id, + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ); + + const expectedDelegation: Delegation = { + id: actualDelegation.id, + delegatorId, + delegateId: delegate.id, + eserviceId: eservice.id, + kind: delegationKind.delegatedProducer, + state: delegationState.waitingForApproval, + createdAt: currentExecutionTime, + submittedAt: currentExecutionTime, + stamps: { + submission: { + who: delegatorId, + when: currentExecutionTime, + }, + }, + }; + + await expectedDelegationCreation(actualDelegation, expectedDelegation); + vi.useRealTimers(); + }); + + it("should throw an differentEServiceProducer error if requester is not Eservice producer", async () => { + const currentExecutionTime = new Date(); + vi.useFakeTimers(); + vi.setSystemTime(currentExecutionTime); + + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "anythings", + }, + }; + + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: currentExecutionTime, + }, + ], + }; + const eservice = getMockEService(); + + await addOneTenant(delegator); + await addOneTenant(delegate); + await addOneEservice(eservice); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: eservice.id, + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(differentEServiceProducer(delegatorId)); + + vi.useRealTimers(); + }); + + it.each(activeDelegationStates)( + "should throw an delegationAlreadyExists error when Delegation for eservice producer already exists with for same delegator, delegate and eserivce ", + async (validDelegationState) => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "anythings", + }, + }; + + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: new Date(), + }, + ], + }; + const eservice = getMockEService(generateId(), delegatorId); + const existentValidDelegation = { + ...getMockDelegationProducer({ + id: generateId(), + delegatorId, + delegateId: delegate.id, + eserviceId: eservice.id, + }), + state: validDelegationState, + }; + + await addOneTenant(delegate); + await addOneTenant(delegator); + await addOneEservice(eservice); + // Add existent valid delegation for the same delegator, delegate and eservice + await addOneDelegation(existentValidDelegation); + // Add existent invalid delegation for the same delegator, delegate and eservice + await addOneDelegation({ + ...existentValidDelegation, + id: generateId(), + state: validDelegationState, + }); + + // Add another generic delegation + await addOneDelegation(getMockDelegationProducer()); + + // Add another delegation with same delegator + await addOneDelegation( + getMockDelegationProducer({ + delegatorId, + }) + ); + + // Add another delegation with same delegate + await addOneDelegation( + getMockDelegationProducer({ + delegateId: delegate.id, + }) + ); + + // Add another delegation for the same eservice + await addOneDelegation( + getMockDelegationProducer({ + eserviceId: eservice.id, + }) + ); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: eservice.id, + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError( + delegationAlreadyExists( + delegatorId, + existentValidDelegation.eserviceId, + delegationKind.delegatedProducer + ) + ); + } + ); + + it("should throw an tenantNotFound error if delegated tenant not exists", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = getMockTenant(delegatorId); + + const delegateId = generateId(); + + await addOneTenant(delegator); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId, + eserviceId: generateId(), + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(tenantNotFound(delegateId)); + }); + + it("should throw an tenantNotFound error if delegator tenant not exists", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + + const delegateId = generateId(); + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: new Date(), + }, + ], + }; + + await addOneTenant(delegate); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId, + eserviceId: generateId(), + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(tenantNotFound(delegatorId)); + }); + + it("should throw an invalidDelegatorAndDelegateAreSame error if delegatorId and delegateId is the same", async () => { + const sameTenantId = generateId(); + const authData = getRandomAuthData(sameTenantId); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: sameTenantId, + eserviceId: generateId(), + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(delegatorAndDelegateSameIdError()); + }); + + it("should throw an invalidExternalOriginError error if delegator has externalId origin different from IPA", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "NOT_IPA", + value: "anythings", + }, + }; + + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: new Date(), + }, + ], + }; + + await addOneTenant(delegate); + await addOneTenant(delegator); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: generateId(), + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError( + invalidExternalOriginError(delegator.externalId.origin) + ); + }); + + it("should throw an eserviceNotFound error if Eservice not exists", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "anythings", + }, + }; + + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: new Date(), + }, + ], + }; + const eserviceId = generateId(); + const delegation = getMockDelegationProducer({ + id: generateId(), + delegatorId, + delegateId: delegate.id, + }); + + await addOneTenant(delegate); + await addOneTenant(delegator); + await addOneDelegation(delegation); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId, + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(eserviceNotFound(eserviceId)); + }); + + it("should throw an invalidExternalOriginError error if delegator has externalId origin different from IPA", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "anythings", + }, + }; + + const delegate = getMockTenant(); + + await addOneTenant(delegate); + await addOneTenant(delegator); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: generateId(), + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(tenantNotAllowedToDelegation(delegate.id)); + }); +}); diff --git a/packages/delegation-process/test/getDelegationById.test.ts b/packages/delegation-process/test/getDelegationById.test.ts new file mode 100644 index 0000000000..8cd65334d5 --- /dev/null +++ b/packages/delegation-process/test/getDelegationById.test.ts @@ -0,0 +1,33 @@ +/* eslint-disable functional/no-let */ +import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; +import { DelegationId, generateId } from "pagopa-interop-models"; +import { describe, expect, it } from "vitest"; +import { delegationNotFound } from "../src/model/domain/errors.js"; +import { addOneDelegation, delegationService } from "./utils.js"; + +describe("get delegation by id", () => { + it("should get the delegation if it exists", async () => { + const delegation = getMockDelegationProducer(); + + await addOneDelegation(delegation); + + const expectedDelegation = await delegationService.getDelegationById( + delegation.id + ); + + expect(delegation).toEqual(expectedDelegation); + }); + + it("should fail with delegationNotFound", async () => { + const delegation = getMockDelegationProducer(); + + await addOneDelegation(delegation); + + const notFoundId = generateId(); + const expectedDelegation = delegationService.getDelegationById(notFoundId); + + await expect(expectedDelegation).rejects.toThrow( + delegationNotFound(notFoundId) + ); + }); +}); diff --git a/packages/delegation-process/test/getDelegations.test.ts b/packages/delegation-process/test/getDelegations.test.ts new file mode 100644 index 0000000000..37a9b7a836 --- /dev/null +++ b/packages/delegation-process/test/getDelegations.test.ts @@ -0,0 +1,68 @@ +/* eslint-disable functional/no-let */ +import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; +import { describe, expect, it } from "vitest"; +import { addOneDelegation, delegationService } from "./utils.js"; + +describe("get delegations", () => { + it("should get delegations", async () => { + const delegation1 = getMockDelegationProducer({ state: "Active" }); + const delegation2 = getMockDelegationProducer(); + await addOneDelegation(delegation1); + await addOneDelegation(delegation2); + + const res1 = await delegationService.getDelegations({ + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }); + expect(res1).toEqual([delegation1]); + + const res2 = await delegationService.getDelegations({ + delegateIds: [delegation2.delegateId], + delegatorIds: [], + delegationStates: [], + eserviceIds: [], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }); + expect(res2).toEqual([delegation2]); + + const res3 = await delegationService.getDelegations({ + delegateIds: [], + delegatorIds: [], + delegationStates: ["Revoked"], + eserviceIds: [], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }); + expect(res3).toEqual([]); + + const res4 = await delegationService.getDelegations({ + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [], + kind: undefined, + offset: 0, + limit: 50, + }); + expect(res4).toEqual([delegation1]); + + const res5 = await delegationService.getDelegations({ + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [delegation1.eserviceId], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }); + expect(res5).toEqual([delegation1]); + }); +}); diff --git a/packages/delegation-process/test/rejectProducerDelegation.test.ts b/packages/delegation-process/test/rejectProducerDelegation.test.ts new file mode 100644 index 0000000000..bcc1bbe7ea --- /dev/null +++ b/packages/delegation-process/test/rejectProducerDelegation.test.ts @@ -0,0 +1,127 @@ +/* eslint-disable functional/no-let */ +import { + decodeProtobufPayload, + getMockDelegationProducer, + getMockTenant, +} from "pagopa-interop-commons-test/index.js"; +import { describe, expect, it, vi } from "vitest"; +import { + DelegationId, + ProducerDelegationRejectedV2, + generateId, + toDelegationV2, + unsafeBrandId, +} from "pagopa-interop-models"; +import { delegationState } from "pagopa-interop-models"; +import { + delegationNotFound, + operationRestrictedToDelegate, + incorrectState, +} from "../src/model/domain/errors.js"; +import { + addOneDelegation, + delegationProducerService, + readLastDelegationEvent, +} from "./utils.js"; + +describe("reject delegation", () => { + it("should reject delegation if all validations succed", async () => { + const currentExecutionTime = new Date(); + vi.useFakeTimers(); + vi.setSystemTime(currentExecutionTime); + + const delegate = getMockTenant(); + const delegation = getMockDelegationProducer({ + state: "WaitingForApproval", + delegateId: delegate.id, + }); + await addOneDelegation(delegation); + + const rejectionReason = "I don't like computers, please send me a pigeon"; + + await delegationProducerService.rejectProducerDelegation( + delegate.id, + delegation.id, + generateId(), + rejectionReason + ); + + const event = await readLastDelegationEvent(delegation.id); + + const { delegation: actualDelegation } = decodeProtobufPayload({ + messageType: ProducerDelegationRejectedV2, + payload: event.data, + }); + const expectedDelegation = toDelegationV2({ + ...delegation, + state: delegationState.rejected, + rejectedAt: currentExecutionTime, + rejectionReason, + stamps: { + ...delegation.stamps, + rejection: { who: delegate.id, when: currentExecutionTime }, + }, + }); + expect(actualDelegation).toEqual(expectedDelegation); + }); + + it("should throw delegationNotFound when delegation doesn't exist", async () => { + const delegateId = getMockTenant().id; + const nonExistentDelegationId = + unsafeBrandId("non-existent-id"); + + await expect( + delegationProducerService.rejectProducerDelegation( + delegateId, + nonExistentDelegationId, + generateId(), + "" + ) + ).rejects.toThrow(delegationNotFound(nonExistentDelegationId)); + }); + + it("should throw operationRestrictedToDelegate when rejecter is not the delegate", async () => { + const delegate = getMockTenant(); + const wrongDelegate = getMockTenant(); + const delegation = getMockDelegationProducer({ + state: "WaitingForApproval", + delegateId: delegate.id, + }); + await addOneDelegation(delegation); + + await expect( + delegationProducerService.rejectProducerDelegation( + wrongDelegate.id, + delegation.id, + generateId(), + "" + ) + ).rejects.toThrow( + operationRestrictedToDelegate(wrongDelegate.id, delegation.id) + ); + }); + + it("should throw incorrectState when delegation is not in WaitingForApproval state", async () => { + const delegate = getMockTenant(); + const delegation = getMockDelegationProducer({ + state: "Active", + delegateId: delegate.id, + }); + await addOneDelegation(delegation); + + await expect( + delegationProducerService.rejectProducerDelegation( + delegate.id, + delegation.id, + generateId(), + "" + ) + ).rejects.toThrow( + incorrectState( + delegation.id, + delegationState.active, + delegationState.waitingForApproval + ) + ); + }); +}); diff --git a/packages/delegation-process/test/revokeProducerDelegation.test.ts b/packages/delegation-process/test/revokeProducerDelegation.test.ts new file mode 100644 index 0000000000..ebff03f0cb --- /dev/null +++ b/packages/delegation-process/test/revokeProducerDelegation.test.ts @@ -0,0 +1,371 @@ +import { fail } from "assert"; +import { + decodeProtobufPayload, + getMockDelegationProducer, + getMockEService, + getMockTenant, + getRandomAuthData, +} from "pagopa-interop-commons-test"; +import { + Delegation, + DelegationId, + ProducerDelegationRevokedV2, + delegationState, + generateId, + TenantId, + fromDelegationV2, + EServiceId, + unsafeBrandId, + DelegationContractId, +} from "pagopa-interop-models"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { + formatDateyyyyMMddHHmmss, + genericLogger, +} from "pagopa-interop-commons"; +import { + delegationNotFound, + delegationNotRevokable, + delegatorNotAllowToRevoke, +} from "../src/model/domain/errors.js"; +import { config } from "../src/config/config.js"; +import { contractBuilder } from "../src/services/delegationContractBuilder.js"; +import { + addOneDelegation, + addOneEservice, + addOneTenant, + delegationProducerService, + fileManager, + flushPDFMetadata, + pdfGenerator, + readDelegationEventByVersion, +} from "./utils.js"; + +type DelegationStateSeed = + | { + delegationData: { + state: "Rejected"; + rejectedAt: Date; + rejectionReason: string; + }; + stamps: { + rejection: { + who: TenantId; + when: Date; + }; + }; + } + | { + delegationData: { + state: "Revoked"; + revokedAt: Date; + }; + stamps: { + revocation: { + who: TenantId; + when: Date; + }; + }; + }; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +const getNotRevocableStateSeeds = (): DelegationStateSeed[] => { + const rejectionOrRevokeDate = new Date(); + rejectionOrRevokeDate.setMonth(new Date().getMonth() - 1); + + return [ + { + delegationData: { + state: delegationState.rejected, + rejectedAt: rejectionOrRevokeDate, + rejectionReason: "Test is a test stop", + }, + stamps: { + rejection: { + who: generateId(), + when: rejectionOrRevokeDate, + }, + }, + }, + { + delegationData: { + state: delegationState.revoked, + revokedAt: rejectionOrRevokeDate, + }, + stamps: { + revocation: { + who: generateId(), + when: rejectionOrRevokeDate, + }, + }, + }, + ]; +}; + +describe("revoke delegation", () => { + const TEST_EXECUTION_DATE = new Date(); + + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(TEST_EXECUTION_DATE); + }); + + afterAll(() => { + vi.useRealTimers(); + }); + + const notRevocableDelegationState = getNotRevocableStateSeeds(); + + it("should revoke a delegation if it exists", async () => { + const currentExecutionTime = new Date(); + const eserviceId = generateId(); + const delegatorId = generateId(); + const delegateId = generateId(); + const authData = getRandomAuthData(delegatorId); + + const delegationCreationDate = new Date(); + delegationCreationDate.setMonth(currentExecutionTime.getMonth() - 2); + + const delegationActivationDate = new Date(); + delegationActivationDate.setMonth(currentExecutionTime.getMonth() - 1); + + const delegate = getMockTenant(delegateId); + const delegator = getMockTenant(delegatorId); + const eservice = getMockEService(eserviceId); + + await addOneTenant(delegate); + await addOneTenant(delegator); + await addOneEservice(eservice); + + const existentDelegation: Delegation = { + ...getMockDelegationProducer({ + delegatorId, + delegateId, + }), + eserviceId, + approvedAt: delegationActivationDate, + submittedAt: delegationCreationDate, + stamps: { + submission: { + who: delegatorId, + when: delegationCreationDate, + }, + activation: { + who: delegateId, + when: delegationActivationDate, + }, + }, + }; + + await addOneDelegation(existentDelegation); + + const actualDelegation = + await delegationProducerService.revokeProducerDelegation( + existentDelegation.id, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ); + + const expectedContractFilePath = ( + await fileManager.listFiles(config.s3Bucket, genericLogger) + )[0]; + + const documentId = unsafeBrandId( + expectedContractFilePath.split("/")[2] + ); + + const expectedDelegation: Delegation = { + ...existentDelegation, + state: delegationState.revoked, + revokedAt: currentExecutionTime, + stamps: { + submission: { + who: delegatorId, + when: delegationCreationDate, + }, + activation: { + who: delegateId, + when: delegationActivationDate, + }, + revocation: { + who: delegatorId, + when: currentExecutionTime, + }, + }, + revocationContract: { + id: documentId, + contentType: "application/pdf", + createdAt: currentExecutionTime, + name: `${formatDateyyyyMMddHHmmss( + currentExecutionTime + )}_delegation_revocation_contract.pdf`, + path: expectedContractFilePath, + prettyName: "Revoca della delega", + }, + }; + + expect(actualDelegation).toEqual(expectedDelegation); + + const actualContract = await fileManager.get( + config.s3Bucket, + expectedContractFilePath, + genericLogger + ); + + const { path: expectedContractPath } = + await contractBuilder.createActivationContract({ + delegation: actualDelegation, + delegator, + delegate, + eservice, + pdfGenerator, + fileManager, + config, + logger: genericLogger, + }); + + const expectedContract = await fileManager.get( + config.s3Bucket, + expectedContractPath, + genericLogger + ); + + expect(flushPDFMetadata(actualContract, currentExecutionTime)).toEqual( + flushPDFMetadata(expectedContract, currentExecutionTime) + ); + + const lastDelegationEvent = await readDelegationEventByVersion( + actualDelegation.id, + 1 + ); + + const delegationEventPayload = decodeProtobufPayload({ + messageType: ProducerDelegationRevokedV2, + payload: lastDelegationEvent.data, + }).delegation; + if (!delegationEventPayload) { + return fail("DelegationRevokedV2 payload not found"); + } + + const delegationFromLastEvent = fromDelegationV2(delegationEventPayload); + + expect(lastDelegationEvent.version).toBe("1"); + expect(delegationFromLastEvent).toMatchObject(expectedDelegation); + }); + + it("should throw an delegationNotFound if Delegation not exists", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegationId = generateId(); + await expect( + delegationProducerService.revokeProducerDelegation(delegationId, { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + }) + ).rejects.toThrow(delegationNotFound(delegationId)); + }); + + it("should throw an delegatorNotAllowToRevoke if Requester Id and DelegatorId are differents", async () => { + const currentExecutionTime = new Date(); + + const delegatorId = generateId(); + const delegateId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegationId = generateId(); + + const delegationCreationDate = new Date(); + delegationCreationDate.setMonth(currentExecutionTime.getMonth() - 2); + + const delegationApprovalDate = new Date(); + delegationApprovalDate.setMonth(currentExecutionTime.getMonth() - 1); + + const existentDelegation = { + ...getMockDelegationProducer({ + id: delegationId, + delegateId, + }), + approvedAt: delegationApprovalDate, + submittedAt: delegationCreationDate, + stamps: { + submission: { + who: delegatorId, + when: delegationCreationDate, + }, + approval: { + who: delegateId, + when: delegationApprovalDate, + }, + }, + }; + + await addOneDelegation(existentDelegation); + + await expect( + delegationProducerService.revokeProducerDelegation(delegationId, { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + }) + ).rejects.toThrow(delegatorNotAllowToRevoke(existentDelegation)); + vi.useRealTimers(); + }); + + it.each(notRevocableDelegationState)( + "should throw an delegatorNotAllowToRevoke if delegation doesn't have revocable one of revocable states [Rejected,Revoked]", + async (notRevocableDelegationState: DelegationStateSeed) => { + const currentExecutionTime = new Date(); + + const delegatorId = generateId(); + const delegateId = generateId(); + const authData = getRandomAuthData(delegatorId); + + const delegationCreationDate = new Date(); + delegationCreationDate.setMonth(currentExecutionTime.getMonth() - 2); + + const delegationActivationDate = new Date(); + delegationActivationDate.setMonth(currentExecutionTime.getMonth() - 1); + + const existentDelegation: Delegation = { + ...getMockDelegationProducer({ + delegatorId, + delegateId, + }), + approvedAt: delegationActivationDate, + submittedAt: delegationCreationDate, + stamps: { + submission: { + who: delegatorId, + when: delegationCreationDate, + }, + activation: { + who: delegateId, + when: delegationActivationDate, + }, + ...notRevocableDelegationState.stamps, + }, + ...notRevocableDelegationState.delegationData, + }; + + await addOneDelegation(existentDelegation); + + await expect( + delegationProducerService.revokeProducerDelegation( + existentDelegation.id, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrow(delegationNotRevokable(existentDelegation)); + } + ); +}); diff --git a/packages/delegation-process/test/tsconfig.json b/packages/delegation-process/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/delegation-process/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/delegation-process/test/utils.ts b/packages/delegation-process/test/utils.ts new file mode 100644 index 0000000000..94fbca3251 --- /dev/null +++ b/packages/delegation-process/test/utils.ts @@ -0,0 +1,137 @@ +/* eslint-disable functional/no-let */ +import { + ReadEvent, + readEventByStreamIdAndVersion, + readLastEventByStreamId, + setupTestContainersVitest, + StoredEvent, + writeInEventstore, + writeInReadmodel, +} from "pagopa-interop-commons-test"; +import { + Delegation, + DelegationEvent, + DelegationId, + EService, + Tenant, + toDelegationV2, + toReadModelEService, + toReadModelTenant, +} from "pagopa-interop-models"; +import { afterAll, afterEach, inject, vi } from "vitest"; +import { + initPDFGenerator, + launchPuppeteerBrowser, +} from "pagopa-interop-commons"; +import puppeteer, { Browser } from "puppeteer"; +import { PDFDocument } from "pdf-lib"; +import { delegationProducerServiceBuilder } from "../src/services/delegationProducerService.js"; +import { delegationServiceBuilder } from "../src/services/delegationService.js"; +import { readModelServiceBuilder } from "../src/services/readModelService.js"; + +export const { cleanup, readModelRepository, postgresDB, fileManager } = + await setupTestContainersVitest( + inject("readModelConfig"), + inject("eventStoreConfig"), + inject("fileManagerConfig") + ); +afterEach(cleanup); + +export const delegations = readModelRepository.delegations; +export const eservices = readModelRepository.eservices; +export const tenants = readModelRepository.tenants; + +export const readModelService = readModelServiceBuilder(readModelRepository); + +const testBrowserInstance: Browser = await launchPuppeteerBrowser({ + pipe: true, +}); +const closeTestBrowserInstance = async (): Promise => + await testBrowserInstance.close(); + +afterAll(closeTestBrowserInstance); +afterAll(() => { + vi.useRealTimers(); +}); + +vi.spyOn(puppeteer, "launch").mockImplementation( + async () => testBrowserInstance +); + +export const pdfGenerator = await initPDFGenerator(); + +export const delegationProducerService = delegationProducerServiceBuilder( + postgresDB, + readModelService, + pdfGenerator, + fileManager +); + +export const delegationService = delegationServiceBuilder(readModelService); + +export const writeSubmitDelegationInEventstore = async ( + delegation: Delegation +): Promise => { + const createProducerDelegationEvent: DelegationEvent = { + type: "ProducerDelegationSubmitted", + event_version: 2, + data: { + delegation: toDelegationV2(delegation), + }, + }; + + const eventToWrite: StoredEvent = { + stream_id: delegation.id, + version: 0, + event: createProducerDelegationEvent, + }; + + await writeInEventstore(eventToWrite, "delegation", postgresDB); +}; + +export const readLastDelegationEvent = async ( + delegationId: DelegationId +): Promise> => + await readLastEventByStreamId(delegationId, "delegation", postgresDB); + +export const readDelegationEventByVersion = async ( + delegationId: DelegationId, + version: number +): Promise> => + await readEventByStreamIdAndVersion( + delegationId, + version, + "delegation", + postgresDB + ); + +export const addOneDelegation = async ( + delegation: Delegation +): Promise => { + await writeSubmitDelegationInEventstore(delegation); + await writeInReadmodel(delegation, delegations); +}; + +export const addOneTenant = async (tenant: Tenant): Promise => { + await writeInReadmodel(toReadModelTenant(tenant), tenants); +}; +export const addOneEservice = async (eservice: EService): Promise => { + await writeInReadmodel(toReadModelEService(eservice), eservices); +}; + +export const flushPDFMetadata = async ( + byteArray: Uint8Array, + currentExecutionTime: Date +): Promise => { + const pdfModified = await PDFDocument.load(byteArray); + // Remove metadata properties + pdfModified.setTitle(""); + pdfModified.setAuthor(""); + pdfModified.setSubject(""); + pdfModified.setKeywords([]); + pdfModified.setProducer(""); + pdfModified.setCreator(""); + pdfModified.setCreationDate(currentExecutionTime); + pdfModified.setModificationDate(currentExecutionTime); + return await pdfModified.save(); +}; diff --git a/packages/delegation-process/test/vitestGlobalSetup.ts b/packages/delegation-process/test/vitestGlobalSetup.ts new file mode 100644 index 0000000000..85a4c8ea41 --- /dev/null +++ b/packages/delegation-process/test/vitestGlobalSetup.ts @@ -0,0 +1,3 @@ +import { setupTestContainersVitestGlobal } from "pagopa-interop-commons-test/index.js"; + +export default setupTestContainersVitestGlobal(); diff --git a/packages/delegation-process/tsconfig.check.json b/packages/delegation-process/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/delegation-process/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/delegation-process/tsconfig.json b/packages/delegation-process/tsconfig.json new file mode 100644 index 0000000000..039e0b4d16 --- /dev/null +++ b/packages/delegation-process/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/packages/delegation-process/vitest.config.ts b/packages/delegation-process/vitest.config.ts new file mode 100644 index 0000000000..9ece1be991 --- /dev/null +++ b/packages/delegation-process/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globalSetup: ["./test/vitestGlobalSetup.ts"], + testTimeout: 60000, + hookTimeout: 60000, + fileParallelism: false, + pool: "forks", + }, +}); diff --git a/packages/delegation-readmodel-writer/.env b/packages/delegation-readmodel-writer/.env new file mode 100644 index 0000000000..43cdfd569a --- /dev/null +++ b/packages/delegation-readmodel-writer/.env @@ -0,0 +1,13 @@ +LOG_LEVEL=info + +KAFKA_CLIENT_ID="delegation" +KAFKA_GROUP_ID="delegation-group" +KAFKA_BROKERS="localhost:9092" +KAFKA_DISABLE_AWS_IAM_AUTH="true" +DELEGATION_TOPIC="event-store.delegation.events" +READMODEL_DB_HOST="localhost" +READMODEL_DB_NAME="readmodel" +READMODEL_DB_USERNAME="root" +READMODEL_DB_PASSWORD="example" +READMODEL_DB_PORT=27017 +AWS_REGION="eu-south-1" diff --git a/packages/delegation-readmodel-writer/Dockerfile b/packages/delegation-readmodel-writer/Dockerfile new file mode 100644 index 0000000000..06f93b3fba --- /dev/null +++ b/packages/delegation-readmodel-writer/Dockerfile @@ -0,0 +1,45 @@ +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ + +COPY ./packages/delegation-readmodel-writer/package.json /app/packages/delegation-readmodel-writer/package.json +COPY ./packages/commons/package.json /app/packages/commons/package.json +COPY ./packages/models/package.json /app/packages/models/package.json +COPY ./packages/kafka-iam-auth/package.json /app/packages/kafka-iam-auth/package.json + +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile + +COPY tsconfig.json /app/ +COPY turbo.json /app/ +COPY ./packages/delegation-readmodel-writer /app/packages/delegation-readmodel-writer +COPY ./packages/commons /app/packages/commons +COPY ./packages/models /app/packages/models +COPY ./packages/kafka-iam-auth /app/packages/kafka-iam-auth + +RUN pnpm build && \ + rm -rf /app/node_modules/.modules.yaml && \ + rm -rf /app/node_modules/.cache && \ + mkdir /out && \ + cp -a --parents -t /out \ + node_modules packages/delegation-readmodel-writer/node_modules \ + package*.json packages/delegation-readmodel-writer/package*.json \ + packages/commons \ + packages/models \ + packages/kafka-iam-auth \ + packages/delegation-readmodel-writer/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final + +COPY --from=build /out /app + +WORKDIR /app/packages/delegation-readmodel-writer +EXPOSE 3000 + +CMD ["node", "."] diff --git a/packages/delegation-readmodel-writer/package.json b/packages/delegation-readmodel-writer/package.json new file mode 100644 index 0000000000..22bba68c81 --- /dev/null +++ b/packages/delegation-readmodel-writer/package.json @@ -0,0 +1,42 @@ +{ + "name": "pagopa-interop-delegation-readmodel-writer", + "private": true, + "version": "1.0.0", + "description": "PagoPA Interoperability delegation consumer service that updates the read model when events are stored", + "main": "dist", + "type": "module", + "scripts": { + "test": "vitest", + "test:it": "vitest integration", + "lint": "eslint . --ext .ts,.tsx", + "lint:autofix": "eslint . --ext .ts,.tsx --fix", + "format:check": "prettier --check src", + "format:write": "prettier --write src", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@pagopa/eslint-config": "3.0.0", + "@types/node": "20.14.6", + "pagopa-interop-commons-test": "workspace:*", + "prettier": "2.8.8", + "testcontainers": "10.9.0", + "tsx": "4.19.1", + "typescript": "5.4.5", + "vitest": "1.6.0" + }, + "dependencies": { + "@protobuf-ts/runtime": "2.9.4", + "dotenv-flow": "4.1.0", + "kafka-iam-auth": "workspace:*", + "kafkajs": "2.2.4", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "ts-pattern": "5.2.0", + "zod": "3.23.8" + } +} diff --git a/packages/delegation-readmodel-writer/src/config/config.ts b/packages/delegation-readmodel-writer/src/config/config.ts new file mode 100644 index 0000000000..31bf913b82 --- /dev/null +++ b/packages/delegation-readmodel-writer/src/config/config.ts @@ -0,0 +1,16 @@ +import { + DelegationTopicConfig, + ReadModelWriterConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +export const DelegationReadModelWriterConfig = ReadModelWriterConfig.and( + DelegationTopicConfig +); + +export type DelegationReadModelWriterConfig = z.infer< + typeof DelegationReadModelWriterConfig +>; + +export const config: DelegationReadModelWriterConfig = + DelegationReadModelWriterConfig.parse(process.env); diff --git a/packages/delegation-readmodel-writer/src/delegationConsumerServiceV2.ts b/packages/delegation-readmodel-writer/src/delegationConsumerServiceV2.ts new file mode 100644 index 0000000000..65cdc3c701 --- /dev/null +++ b/packages/delegation-readmodel-writer/src/delegationConsumerServiceV2.ts @@ -0,0 +1,38 @@ +import { DelegationCollection } from "pagopa-interop-commons"; +import { + DelegationEventEnvelopeV2, + fromDelegationV2, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; + +export async function handleMessageV2( + message: DelegationEventEnvelopeV2, + delegations: DelegationCollection +): Promise { + await match(message) + .with( + { type: "ProducerDelegationApproved" }, + { type: "ProducerDelegationRejected" }, + { type: "ProducerDelegationRevoked" }, + { type: "ProducerDelegationSubmitted" }, + async (message) => { + const delegation = message.data.delegation; + await delegations.updateOne( + { + "data.id": message.stream_id, + "metadata.version": { $lte: message.version }, + }, + { + $set: { + data: delegation ? fromDelegationV2(delegation) : undefined, + metadata: { + version: message.version, + }, + }, + }, + { upsert: true } + ); + } + ) + .exhaustive(); +} diff --git a/packages/delegation-readmodel-writer/src/index.ts b/packages/delegation-readmodel-writer/src/index.ts new file mode 100644 index 0000000000..7044e3e07c --- /dev/null +++ b/packages/delegation-readmodel-writer/src/index.ts @@ -0,0 +1,45 @@ +import { EachMessagePayload } from "kafkajs"; +import { + logger, + ReadModelRepository, + decodeKafkaMessage, +} from "pagopa-interop-commons"; +import { runConsumer } from "kafka-iam-auth"; +import { + CorrelationId, + DelegationEvent, + generateId, + unsafeBrandId, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { handleMessageV2 } from "./delegationConsumerServiceV2.js"; +import { config } from "./config/config.js"; + +const { delegations } = ReadModelRepository.init(config); + +async function processMessage({ + message, + partition, +}: EachMessagePayload): Promise { + const decodedMessage = decodeKafkaMessage(message, DelegationEvent); + + const loggerInstance = logger({ + serviceName: "delegation-readmodel-writer", + eventType: decodedMessage.type, + eventVersion: decodedMessage.event_version, + streamId: decodedMessage.stream_id, + correlationId: decodedMessage.correlation_id + ? unsafeBrandId(decodedMessage.correlation_id) + : generateId(), + }); + + await match(decodedMessage) + .with({ event_version: 2 }, (msg) => handleMessageV2(msg, delegations)) + .exhaustive(); + + loggerInstance.info( + `Read model was updated. Partition number: ${partition}. Offset: ${message.offset}` + ); +} + +await runConsumer(config, [config.delegationTopic], processMessage); diff --git a/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts b/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts new file mode 100644 index 0000000000..48348a453f --- /dev/null +++ b/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts @@ -0,0 +1,121 @@ +import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; +import { + DelegationEventEnvelopeV2, + toDelegationV2, + ProducerDelegationApprovedV2, + ProducerDelegationRejectedV2, + ProducerDelegationRevokedV2, + ProducerDelegationSubmittedV2, +} from "pagopa-interop-models"; +import { describe, expect, it } from "vitest"; +import { handleMessageV2 } from "../src/delegationConsumerServiceV2.js"; +import { delegations } from "./utils.js"; + +describe("Events V2", async () => { + const mockDelegation = getMockDelegationProducer(); + const mockMessage: DelegationEventEnvelopeV2 = { + event_version: 2, + stream_id: mockDelegation.id, + version: 1, + sequence_num: 1, + log_date: new Date(), + type: "ProducerDelegationApproved", + data: {}, + }; + + it("ProducerDelegationApproved", async () => { + const payload: ProducerDelegationApprovedV2 = { + delegation: toDelegationV2(mockDelegation), + }; + + const message: DelegationEventEnvelopeV2 = { + ...mockMessage, + type: "ProducerDelegationApproved", + data: payload, + }; + + await handleMessageV2(message, delegations); + + const retrievedDelegation = await delegations.findOne({ + "data.id": mockDelegation.id, + }); + + expect(retrievedDelegation?.data).toEqual(mockDelegation); + + expect(retrievedDelegation?.metadata).toEqual({ + version: 1, + }); + }); + + it("ProducerDelegationRejected", async () => { + const payload: ProducerDelegationRejectedV2 = { + delegation: toDelegationV2(mockDelegation), + }; + + const message: DelegationEventEnvelopeV2 = { + ...mockMessage, + type: "ProducerDelegationRejected", + data: payload, + }; + + await handleMessageV2(message, delegations); + + const retrievedDelegation = await delegations.findOne({ + "data.id": mockDelegation.id, + }); + + expect(retrievedDelegation?.data).toEqual(mockDelegation); + + expect(retrievedDelegation?.metadata).toEqual({ + version: 1, + }); + }); + + it("ProducerDelegationRevoked", async () => { + const payload: ProducerDelegationRevokedV2 = { + delegation: toDelegationV2(mockDelegation), + }; + + const message: DelegationEventEnvelopeV2 = { + ...mockMessage, + type: "ProducerDelegationRevoked", + data: payload, + }; + + await handleMessageV2(message, delegations); + + const retrievedDelegation = await delegations.findOne({ + "data.id": mockDelegation.id, + }); + + expect(retrievedDelegation?.data).toEqual(mockDelegation); + + expect(retrievedDelegation?.metadata).toEqual({ + version: 1, + }); + }); + + it("ProducerDelegationSubmitted", async () => { + const payload: ProducerDelegationSubmittedV2 = { + delegation: toDelegationV2(mockDelegation), + }; + + const message: DelegationEventEnvelopeV2 = { + ...mockMessage, + type: "ProducerDelegationSubmitted", + data: payload, + }; + + await handleMessageV2(message, delegations); + + const retrievedDelegation = await delegations.findOne({ + "data.id": mockDelegation.id, + }); + + expect(retrievedDelegation?.data).toEqual(mockDelegation); + + expect(retrievedDelegation?.metadata).toEqual({ + version: 1, + }); + }); +}); diff --git a/packages/delegation-readmodel-writer/test/tsconfig.json b/packages/delegation-readmodel-writer/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/delegation-readmodel-writer/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/delegation-readmodel-writer/test/utils.ts b/packages/delegation-readmodel-writer/test/utils.ts new file mode 100644 index 0000000000..adf10b846f --- /dev/null +++ b/packages/delegation-readmodel-writer/test/utils.ts @@ -0,0 +1,10 @@ +import { setupTestContainersVitest } from "pagopa-interop-commons-test"; +import { inject, afterEach } from "vitest"; + +export const { cleanup, readModelRepository } = await setupTestContainersVitest( + inject("readModelConfig") +); + +afterEach(cleanup); + +export const delegations = readModelRepository.delegations; diff --git a/packages/delegation-readmodel-writer/test/vitestGlobalSetup.ts b/packages/delegation-readmodel-writer/test/vitestGlobalSetup.ts new file mode 100644 index 0000000000..32972b5c32 --- /dev/null +++ b/packages/delegation-readmodel-writer/test/vitestGlobalSetup.ts @@ -0,0 +1,3 @@ +import { setupTestContainersVitestGlobal } from "pagopa-interop-commons-test"; + +export default setupTestContainersVitestGlobal(); diff --git a/packages/delegation-readmodel-writer/tsconfig.check.json b/packages/delegation-readmodel-writer/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/delegation-readmodel-writer/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/delegation-readmodel-writer/tsconfig.json b/packages/delegation-readmodel-writer/tsconfig.json new file mode 100644 index 0000000000..a1ec44f6e6 --- /dev/null +++ b/packages/delegation-readmodel-writer/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src" + ] +} diff --git a/packages/delegation-readmodel-writer/vitest.config.ts b/packages/delegation-readmodel-writer/vitest.config.ts new file mode 100644 index 0000000000..9ece1be991 --- /dev/null +++ b/packages/delegation-readmodel-writer/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globalSetup: ["./test/vitestGlobalSetup.ts"], + testTimeout: 60000, + hookTimeout: 60000, + fileParallelism: false, + pool: "forks", + }, +}); diff --git a/packages/models/proto/v2/delegation/delegation.proto b/packages/models/proto/v2/delegation/delegation.proto new file mode 100644 index 0000000000..abacf41bd4 --- /dev/null +++ b/packages/models/proto/v2/delegation/delegation.proto @@ -0,0 +1,54 @@ +syntax = "proto3"; + +package delegation.v2; + +message DelegationV2 { + string id = 1; + string delegatorId = 2; + string delegateId = 3; + string eserviceId = 4; + int64 createdAt = 5; + int64 submittedAt = 6; + optional int64 approvedAt = 7; + optional int64 rejectedAt = 8; + optional string rejectionReason = 9; + optional int64 revokedAt = 10; + DelegationStateV2 state = 11; + DelegationKindV2 kind = 12; + DelegationStampsV2 stamps = 13; + optional DelegationContractDocumentV2 activationContract = 14; + optional DelegationContractDocumentV2 revocationContract = 15; +} + +message DelegationContractDocumentV2 { + string id = 1; + string name = 2; + string prettyName = 3; + string contentType = 4; + string path = 5; + int64 createdAt = 6; +} + +message DelegationStampV2 { + string who = 1; + int64 when = 2; +} + +message DelegationStampsV2 { + DelegationStampV2 submission = 1; + optional DelegationStampV2 activation = 2; + optional DelegationStampV2 rejection = 3; + optional DelegationStampV2 revocation = 4; +} + +enum DelegationStateV2 { + WAITING_FOR_APPROVAL= 0; + ACTIVE= 1; + REJECTED= 2; + REVOKED= 3; +} + +enum DelegationKindV2 { + DELEGATED_PRODUCER = 0; + DELEGATED_CONSUMER = 1; +} \ No newline at end of file diff --git a/packages/models/proto/v2/delegation/events.proto b/packages/models/proto/v2/delegation/events.proto new file mode 100644 index 0000000000..ef551774ff --- /dev/null +++ b/packages/models/proto/v2/delegation/events.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package delegation.v2; + +import "v2/delegation/delegation.proto"; + +message ProducerDelegationSubmittedV2 { + DelegationV2 delegation = 1; +} + +message ProducerDelegationApprovedV2 { + DelegationV2 delegation = 1; +} + +message ProducerDelegationRejectedV2 { + DelegationV2 delegation = 1; +} + +message ProducerDelegationRevokedV2 { + DelegationV2 delegation = 1; +} diff --git a/packages/models/proto/v2/tenant/events.proto b/packages/models/proto/v2/tenant/events.proto index 370e390292..a45cfe7e38 100644 --- a/packages/models/proto/v2/tenant/events.proto +++ b/packages/models/proto/v2/tenant/events.proto @@ -75,3 +75,7 @@ message TenantMailDeletedV2 { message MaintenanceTenantPromotedToCertifierV2{ TenantV2 tenant = 1; } + +message TenantDelegatedProducerFeatureAddedV2 { + TenantV2 tenant = 1; +} diff --git a/packages/models/proto/v2/tenant/tenant.proto b/packages/models/proto/v2/tenant/tenant.proto index 5cb16a63ae..75aaf6d2bb 100644 --- a/packages/models/proto/v2/tenant/tenant.proto +++ b/packages/models/proto/v2/tenant/tenant.proto @@ -46,6 +46,7 @@ enum TenantUnitTypeV2 { message TenantFeatureV2 { oneof sealed_value { CertifierV2 certifier = 1; + DelegatedProducerV2 delegatedProducer = 2; } } @@ -53,6 +54,10 @@ message CertifierV2 { string certifierId = 1; } +message DelegatedProducerV2 { + int64 availabilityTimestamp = 1; +} + message TenantVerifierV2 { string id = 1; int64 verificationDate = 2; diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index 84d0dd1708..3da409a6e9 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -74,6 +74,14 @@ export type SelfcareId = z.infer; export const ProducerKeychainId = z.string().uuid().brand("ProducerKeychainId"); export type ProducerKeychainId = z.infer; +export const DelegationId = z.string().uuid().brand("DelegationId"); +export type DelegationId = z.infer; + +export const DelegationContractId = z + .string() + .uuid() + .brand("DelegationContractId"); +export type DelegationContractId = z.infer; export const PlatformStatesEServiceDescriptorPK = z .string() .brand(`ESERVICEDESCRIPTOR#eServiceId#descriptorId`); @@ -152,6 +160,8 @@ type IDS = | UserId | SelfcareId | ProducerKeychainId + | DelegationId + | DelegationContractId | PlatformStatesEServiceDescriptorPK | PlatformStatesAgreementPK | PlatformStatesPurposePK diff --git a/packages/models/src/constants.ts b/packages/models/src/constants.ts new file mode 100644 index 0000000000..7f774481c2 --- /dev/null +++ b/packages/models/src/constants.ts @@ -0,0 +1,4 @@ +export const PUBLIC_ADMINISTRATIONS_IDENTIFIER = "IPA"; +export const CONTRACT_AUTHORITY_PUBLIC_SERVICES_MANAGERS = "SAG"; +export const PUBLIC_SERVICES_MANAGERS = "L37"; +export const SCP = "PDND_INFOCAMERE-SCP"; diff --git a/packages/models/src/delegation/delegation.ts b/packages/models/src/delegation/delegation.ts new file mode 100644 index 0000000000..f9cc0dd3d6 --- /dev/null +++ b/packages/models/src/delegation/delegation.ts @@ -0,0 +1,75 @@ +import z from "zod"; +import { + DelegationContractId, + DelegationId, + EServiceId, + TenantId, +} from "../brandedIds.js"; + +export const delegationKind = { + delegatedConsumer: "DelegatedConsumer", + delegatedProducer: "DelegatedProducer", +} as const; +export const DelegationKind = z.enum([ + Object.values(delegationKind)[0], + ...Object.values(delegationKind).slice(1), +]); +export type DelegationKind = z.infer; + +export const delegationState = { + waitingForApproval: "WaitingForApproval", + active: "Active", + rejected: "Rejected", + revoked: "Revoked", +} as const; + +export const DelegationState = z.enum([ + Object.values(delegationState)[0], + ...Object.values(delegationState).slice(1), +]); +export type DelegationState = z.infer; + +export const DelegationContractDocument = z.object({ + id: DelegationContractId, + name: z.string(), + prettyName: z.string(), + contentType: z.string(), + path: z.string(), + createdAt: z.coerce.date(), +}); +export type DelegationContractDocument = z.infer< + typeof DelegationContractDocument +>; + +export const DelegationStamp = z.object({ + who: TenantId, + when: z.coerce.date(), +}); +export type DelegationStamp = z.infer; + +export const DelegationStamps = z.object({ + submission: DelegationStamp, + activation: DelegationStamp.optional(), + rejection: DelegationStamp.optional(), + revocation: DelegationStamp.optional(), +}); +export type DelegationStamps = z.infer; + +export const Delegation = z.object({ + id: DelegationId, + delegatorId: TenantId, + delegateId: TenantId, + eserviceId: EServiceId, + createdAt: z.coerce.date(), + submittedAt: z.coerce.date(), + approvedAt: z.coerce.date().optional(), + rejectedAt: z.coerce.date().optional(), + rejectionReason: z.string().optional(), + revokedAt: z.coerce.date().optional(), + state: DelegationState, + kind: DelegationKind, + activationContract: DelegationContractDocument.optional(), + revocationContract: DelegationContractDocument.optional(), + stamps: DelegationStamps, +}); +export type Delegation = z.infer; diff --git a/packages/models/src/delegation/delegationEvents.ts b/packages/models/src/delegation/delegationEvents.ts new file mode 100644 index 0000000000..89206416b6 --- /dev/null +++ b/packages/models/src/delegation/delegationEvents.ts @@ -0,0 +1,85 @@ +import { z } from "zod"; +import { match } from "ts-pattern"; + +import { + ProducerDelegationSubmittedV2, + ProducerDelegationApprovedV2, + ProducerDelegationRejectedV2, + ProducerDelegationRevokedV2, +} from "../gen/v2/delegation/events.js"; +import { protobufDecoder } from "../protobuf/protobuf.js"; +import { EventEnvelope } from "../events/events.js"; + +export const DelegationEventV2 = z.discriminatedUnion("type", [ + z.object({ + event_version: z.literal(2), + type: z.literal("ProducerDelegationSubmitted"), + data: protobufDecoder(ProducerDelegationSubmittedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("ProducerDelegationApproved"), + data: protobufDecoder(ProducerDelegationApprovedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("ProducerDelegationRejected"), + data: protobufDecoder(ProducerDelegationRejectedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("ProducerDelegationRevoked"), + data: protobufDecoder(ProducerDelegationRevokedV2), + }), +]); + +export type DelegationEventV2 = z.infer; + +export function delegationEventToBinaryDataV2( + event: DelegationEventV2 +): Uint8Array { + return match(event) + .with({ type: "ProducerDelegationSubmitted" }, ({ data }) => + ProducerDelegationSubmittedV2.toBinary(data) + ) + .with({ type: "ProducerDelegationApproved" }, ({ data }) => + ProducerDelegationApprovedV2.toBinary(data) + ) + .with({ type: "ProducerDelegationRejected" }, ({ data }) => + ProducerDelegationRejectedV2.toBinary(data) + ) + .with({ type: "ProducerDelegationRevoked" }, ({ data }) => + ProducerDelegationRevokedV2.toBinary(data) + ) + .exhaustive(); +} + +const eventV2 = z + .object({ + event_version: z.literal(2), + }) + .passthrough(); + +export const DelegationEvent = z + .discriminatedUnion("event_version", [eventV2]) + .transform((obj, ctx) => { + const res = match(obj) + .with({ event_version: 2 }, () => DelegationEventV2.safeParse(obj)) + .exhaustive(); + + if (!res.success) { + res.error.issues.forEach(ctx.addIssue); + return z.NEVER; + } + return res.data; + }); + +export type DelegationEvent = z.infer; + +export const DelegationEventEnvelopeV2 = EventEnvelope(DelegationEventV2); +export type DelegationEventEnvelopeV2 = z.infer< + typeof DelegationEventEnvelopeV2 +>; + +export const DelegationEventEnvelope = EventEnvelope(DelegationEvent); +export type DelegationEventEnvelope = z.infer; diff --git a/packages/models/src/delegation/protobufConverterFromV2.ts b/packages/models/src/delegation/protobufConverterFromV2.ts new file mode 100644 index 0000000000..1100eebe42 --- /dev/null +++ b/packages/models/src/delegation/protobufConverterFromV2.ts @@ -0,0 +1,125 @@ +import { unsafeBrandId } from "../brandedIds.js"; +import { genericError } from "../errors.js"; +import { + DelegationContractDocumentV2, + DelegationKindV2, + DelegationStampsV2, + DelegationStampV2, + DelegationStateV2, + DelegationV2, +} from "../gen/v2/delegation/delegation.js"; +import { bigIntToDate } from "../utils.js"; +import { + Delegation, + DelegationContractDocument, + delegationKind, + DelegationKind, + DelegationStamp, + DelegationStamps, + DelegationState, + delegationState, +} from "./delegation.js"; + +export const fromDelegationStateV2 = ( + input: DelegationStateV2 +): DelegationState => { + switch (input) { + case DelegationStateV2.ACTIVE: + return delegationState.active; + case DelegationStateV2.REJECTED: + return delegationState.rejected; + case DelegationStateV2.REVOKED: + return delegationState.revoked; + case DelegationStateV2.WAITING_FOR_APPROVAL: + return delegationState.waitingForApproval; + } +}; + +export const fromDelegationKindV2 = ( + input: DelegationKindV2 +): DelegationKind => { + switch (input) { + case DelegationKindV2.DELEGATED_CONSUMER: + return delegationKind.delegatedConsumer; + case DelegationKindV2.DELEGATED_PRODUCER: + return delegationKind.delegatedProducer; + } +}; + +export const fromDelegationContractDocumentV2 = ( + input: DelegationContractDocumentV2 +): DelegationContractDocument => ({ + id: unsafeBrandId(input.id), + name: input.name, + prettyName: input.prettyName, + contentType: input.contentType, + path: input.path, + createdAt: bigIntToDate(input.createdAt), +}); + +export function fromDelegationStampV2( + input: DelegationStampV2 +): DelegationStamp; +export function fromDelegationStampV2( + input: DelegationStampV2 | undefined +): DelegationStamp | undefined { + return input + ? { + when: bigIntToDate(input.when), + who: unsafeBrandId(input.who), + } + : undefined; +} + +export const fromDelegationStampsV2 = ( + input: DelegationStampsV2 +): DelegationStamps => { + if (!input.submission) { + throw genericError( + `Error while deserializing DelegationStampsV2: missing submission stamp ` + ); + } + + return { + submission: fromDelegationStampV2(input.submission), + activation: input.activation + ? fromDelegationStampV2(input.activation) + : undefined, + rejection: input.rejection + ? fromDelegationStampV2(input.rejection) + : undefined, + revocation: input.revocation + ? fromDelegationStampV2(input.revocation) + : undefined, + }; +}; + +export const fromDelegationV2 = (input: DelegationV2): Delegation => { + if (!input.stamps) { + throw genericError( + `Error while deserializing DelegationV2 (${input.id}): missing stamps ` + ); + } + + return { + id: unsafeBrandId(input.id), + delegatorId: unsafeBrandId(input.delegatorId), + delegateId: unsafeBrandId(input.delegateId), + eserviceId: unsafeBrandId(input.eserviceId), + createdAt: bigIntToDate(input.createdAt), + submittedAt: bigIntToDate(input.submittedAt), + approvedAt: bigIntToDate(input.approvedAt), + rejectedAt: bigIntToDate(input.rejectedAt), + rejectionReason: input.rejectionReason, + revokedAt: bigIntToDate(input.revokedAt), + state: fromDelegationStateV2(input.state), + kind: fromDelegationKindV2(input.kind), + activationContract: + input.activationContract && + fromDelegationContractDocumentV2(input.activationContract), + revocationContract: + input.revocationContract && + fromDelegationContractDocumentV2(input.revocationContract), + stamps: fromDelegationStampsV2(input.stamps), + }; +}; diff --git a/packages/models/src/delegation/protobufConverterToV2.ts b/packages/models/src/delegation/protobufConverterToV2.ts new file mode 100644 index 0000000000..71872e1ba0 --- /dev/null +++ b/packages/models/src/delegation/protobufConverterToV2.ts @@ -0,0 +1,87 @@ +import { match } from "ts-pattern"; +import { + DelegationV2, + DelegationStampV2, + DelegationStampsV2, + DelegationStateV2, + DelegationKindV2, + DelegationContractDocumentV2, +} from "../gen/v2/delegation/delegation.js"; +import { dateToBigInt } from "../utils.js"; +import { + Delegation, + DelegationKind, + delegationKind, + DelegationContractDocument, + DelegationStamp, + DelegationStamps, + DelegationState, + delegationState, +} from "./delegation.js"; + +export const toDelegationStateV2 = ( + state: DelegationState +): DelegationStateV2 => + match(state) + .with(delegationState.active, () => DelegationStateV2.ACTIVE) + .with(delegationState.rejected, () => DelegationStateV2.REJECTED) + .with(delegationState.revoked, () => DelegationStateV2.REVOKED) + .with( + delegationState.waitingForApproval, + () => DelegationStateV2.WAITING_FOR_APPROVAL + ) + .exhaustive(); + +export const toDelegationKindV2 = (kind: DelegationKind): DelegationKindV2 => + match(kind) + .with( + delegationKind.delegatedConsumer, + () => DelegationKindV2.DELEGATED_CONSUMER + ) + .with( + delegationKind.delegatedProducer, + () => DelegationKindV2.DELEGATED_PRODUCER + ) + .exhaustive(); + +export const toDelegationContractDocumentV2 = ( + contract: DelegationContractDocument +): DelegationContractDocumentV2 => ({ + ...contract, + createdAt: dateToBigInt(contract.createdAt), +}); + +export const toDelegationStampV2 = ( + stamp: DelegationStamp +): DelegationStampV2 => ({ + when: dateToBigInt(stamp.when), + who: stamp.who, +}); + +export const toDelegationStampsV2 = ( + input: DelegationStamps +): DelegationStampsV2 => ({ + submission: toDelegationStampV2(input.submission), + activation: input.activation && toDelegationStampV2(input.activation), + rejection: input.rejection && toDelegationStampV2(input.rejection), + revocation: input.revocation && toDelegationStampV2(input.revocation), +}); + +export const toDelegationV2 = (delegation: Delegation): DelegationV2 => ({ + ...delegation, + state: toDelegationStateV2(delegation.state), + kind: toDelegationKindV2(delegation.kind), + createdAt: dateToBigInt(delegation.createdAt), + submittedAt: dateToBigInt(delegation.submittedAt), + approvedAt: dateToBigInt(delegation.approvedAt), + rejectedAt: dateToBigInt(delegation.rejectedAt), + revokedAt: dateToBigInt(delegation.revokedAt), + stamps: toDelegationStampsV2(delegation.stamps), + rejectionReason: delegation.rejectionReason, + activationContract: + delegation.activationContract && + toDelegationContractDocumentV2(delegation.activationContract), + revocationContract: + delegation.revocationContract && + toDelegationContractDocumentV2(delegation.revocationContract), +}); diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index 853c28d655..8e7aeee6e8 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -15,6 +15,11 @@ export * from "./attribute/attributeEvents.js"; export * from "./attribute/protobufConverterFromV1.js"; export * from "./attribute/protobufConverterToV1.js"; +export * from "./delegation/delegation.js"; +export * from "./delegation/delegationEvents.js"; +export * from "./delegation/protobufConverterFromV2.js"; +export * from "./delegation/protobufConverterToV2.js"; + export * from "./email/email.js"; export * from "./eservice/eservice.js"; @@ -69,9 +74,11 @@ export * from "./read-models/tenantReadModel.js"; export * from "./read-models/purposeReadModel.js"; export * from "./read-models/readModels.js"; export * from "./read-models/authorizationReadModel.js"; +export * from "./read-models/delegationReadModel.js"; // Utilities export * from "./brandedIds.js"; +export * from "./constants.js"; export * from "./errors.js"; export * from "./utils.js"; @@ -103,3 +110,5 @@ export * from "./gen/v2/authorization/key.js"; export * from "./gen/v2/authorization/events.js"; export * from "./gen/v2/tenant/tenant.js"; export * from "./gen/v2/tenant/events.js"; +export * from "./gen/v2/delegation/delegation.js"; +export * from "./gen/v2/delegation/events.js"; diff --git a/packages/models/src/read-models/delegationReadModel.ts b/packages/models/src/read-models/delegationReadModel.ts new file mode 100644 index 0000000000..4402fcd444 --- /dev/null +++ b/packages/models/src/read-models/delegationReadModel.ts @@ -0,0 +1,5 @@ +import { z } from "zod"; +import { Delegation } from "../delegation/delegation.js"; + +export const DelegationReadModel = Delegation; +export type DelegationReadModel = z.infer; diff --git a/packages/models/src/tenant/protobufConverterFromV2.ts b/packages/models/src/tenant/protobufConverterFromV2.ts index 5901a9c88c..e61d630bc1 100644 --- a/packages/models/src/tenant/protobufConverterFromV2.ts +++ b/packages/models/src/tenant/protobufConverterFromV2.ts @@ -20,7 +20,6 @@ import { TenantMailKind, tenantMailKind, TenantMail, - TenantFeatureCertifier, TenantVerifier, TenantRevoker, TenantAttribute, @@ -29,6 +28,7 @@ import { tenantAttributeType, TenantUnitType, tenantUnitType, + TenantFeature, } from "./tenant.js"; export const fromTenantKindV2 = (input: TenantKindV2): TenantKind => { @@ -62,16 +62,18 @@ export const fromTenantMailV2 = (input: TenantMailV2): TenantMail => ({ kind: fromTenantMailKindV2(input.kind), }); -export const fromTenantFeatureV2 = ( - input: TenantFeatureV2 -): TenantFeatureCertifier => - match( - input.sealedValue - ) +export const fromTenantFeatureV2 = (input: TenantFeatureV2): TenantFeature => + match(input.sealedValue) .with({ oneofKind: "certifier" }, ({ certifier }) => ({ type: "PersistentCertifier", certifierId: certifier.certifierId, })) + .with({ oneofKind: "delegatedProducer" }, ({ delegatedProducer }) => ({ + type: "DelegatedProducer", + availabilityTimestamp: bigIntToDate( + delegatedProducer.availabilityTimestamp + ), + })) .with({ oneofKind: undefined }, () => { throw new Error("Unable to deserialize TenantFeature"); }) diff --git a/packages/models/src/tenant/protobufConverterToV2.ts b/packages/models/src/tenant/protobufConverterToV2.ts index 66f935a786..36350a983f 100644 --- a/packages/models/src/tenant/protobufConverterToV2.ts +++ b/packages/models/src/tenant/protobufConverterToV2.ts @@ -37,6 +37,14 @@ export function toFeatureV2(feature: TenantFeature): TenantFeatureV2 { }, }, })) + .with({ type: "DelegatedProducer" }, (feature) => ({ + sealedValue: { + oneofKind: "delegatedProducer", + delegatedProducer: { + availabilityTimestamp: dateToBigInt(feature.availabilityTimestamp), + }, + }, + })) .exhaustive(); } diff --git a/packages/models/src/tenant/tenant.ts b/packages/models/src/tenant/tenant.ts index 47205c6205..02f8e93bd7 100644 --- a/packages/models/src/tenant/tenant.ts +++ b/packages/models/src/tenant/tenant.ts @@ -26,10 +26,20 @@ export const TenantFeatureCertifier = z.object({ type: z.literal("PersistentCertifier"), certifierId: z.string(), }); - export type TenantFeatureCertifier = z.infer; -export const TenantFeature = TenantFeatureCertifier; // It will be extended with other features, we will use this union to discriminate them +export const TenantFeatureDelegatedProducer = z.object({ + type: z.literal("DelegatedProducer"), + availabilityTimestamp: z.coerce.date(), +}); +export type TenantFeatureDelegatedProducer = z.infer< + typeof TenantFeatureDelegatedProducer +>; + +export const TenantFeature = z.discriminatedUnion("type", [ + TenantFeatureCertifier, + TenantFeatureDelegatedProducer, +]); export type TenantFeature = z.infer; diff --git a/packages/models/src/tenant/tenantEvents.ts b/packages/models/src/tenant/tenantEvents.ts index 960d9c7ff2..8a31948355 100644 --- a/packages/models/src/tenant/tenantEvents.ts +++ b/packages/models/src/tenant/tenantEvents.ts @@ -25,6 +25,7 @@ import { MaintenanceTenantPromotedToCertifierV2, TenantMailDeletedV2, TenantKindUpdatedV2, + TenantDelegatedProducerFeatureAddedV2, } from "../gen/v2/tenant/events.js"; import { protobufDecoder } from "../protobuf/protobuf.js"; import { EventEnvelope } from "../events/events.js"; @@ -109,6 +110,9 @@ export function tenantEventToBinaryDataV2(event: TenantEventV2): Uint8Array { .with({ type: "MaintenanceTenantPromotedToCertifier" }, ({ data }) => MaintenanceTenantPromotedToCertifierV2.toBinary(data) ) + .with({ type: "TenantDelegatedProducerFeatureAdded" }, ({ data }) => + TenantDelegatedProducerFeatureAddedV2.toBinary(data) + ) .exhaustive(); } @@ -228,6 +232,11 @@ export const TenantEventV2 = z.discriminatedUnion("type", [ type: z.literal("MaintenanceTenantPromotedToCertifier"), data: protobufDecoder(MaintenanceTenantPromotedToCertifierV2), }), + z.object({ + event_version: z.literal(2), + type: z.literal("TenantDelegatedProducerFeatureAdded"), + data: protobufDecoder(TenantDelegatedProducerFeatureAddedV2), + }), ]); export type TenantEventV2 = z.infer; diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index c3e80434a4..857f575ae2 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.4-b", + "@pagopa/interop-outbound-models": "1.0.6-b", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 6e79cc66a9..7a950f251c 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.4-b", + "@pagopa/interop-outbound-models": "1.0.6-b", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts b/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts index 515f66a488..f95023456a 100644 --- a/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts +++ b/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts @@ -45,6 +45,7 @@ export function toOutboundEventV2( { type: "TenantOnboarded" }, { type: "TenantOnboardDetailsUpdated" }, { type: "MaintenanceTenantPromotedToCertifier" }, + { type: "TenantDelegatedProducerFeatureAdded" }, (msg) => ({ event_version: msg.event_version, type: msg.type, diff --git a/packages/tenant-process/src/model/domain/apiConverter.ts b/packages/tenant-process/src/model/domain/apiConverter.ts index 16ec19c944..77db01d68b 100644 --- a/packages/tenant-process/src/model/domain/apiConverter.ts +++ b/packages/tenant-process/src/model/domain/apiConverter.ts @@ -40,6 +40,11 @@ export function toApiTenantFeature( certifierId: feature.certifierId, }, })) + .with({ type: "DelegatedProducer" }, (feature) => ({ + delegatedProducer: { + availabilityTimestamp: feature.availabilityTimestamp.toJSON(), + }, + })) .exhaustive(); } diff --git a/packages/tenant-process/src/model/domain/errors.ts b/packages/tenant-process/src/model/domain/errors.ts index 4fc4a3c12e..f150139a78 100644 --- a/packages/tenant-process/src/model/domain/errors.ts +++ b/packages/tenant-process/src/model/domain/errors.ts @@ -33,6 +33,7 @@ export const errorCodes = { certifierWithExistingAttributes: "0024", attributeNotFoundInTenant: "0025", tenantNotFoundByExternalId: "0026", + tenantAlreadyHasDelegatedProducerFeature: "0027", }; export type ErrorCodes = keyof typeof errorCodes; @@ -292,3 +293,13 @@ export function attributeNotFoundInTenant( title: "Attribute not found in tenant", }); } + +export function tenantAlreadyHasDelegatedProducerFeature( + tenantId: TenantId +): ApiError { + return new ApiError({ + detail: `Tenant ${tenantId} already has delegated producer feature assigned`, + code: "tenantAlreadyHasDelegatedProducerFeature", + title: "Feature already assigned", + }); +} diff --git a/packages/tenant-process/src/model/domain/toEvent.ts b/packages/tenant-process/src/model/domain/toEvent.ts index 3704cc836a..dd4fa94063 100644 --- a/packages/tenant-process/src/model/domain/toEvent.ts +++ b/packages/tenant-process/src/model/domain/toEvent.ts @@ -285,3 +285,20 @@ export const toCreateEventMaintenanceTenantPromotedToCertifier = ( }, correlationId, }); + +export const toCreateEventTenantDelegatedProducerFeatureAdded = ( + version: number, + updatedTenant: Tenant, + correlationId: CorrelationId +): CreateEvent => ({ + streamId: updatedTenant.id, + version, + event: { + type: "TenantDelegatedProducerFeatureAdded", + event_version: 2, + data: { + tenant: toTenantV2(updatedTenant), + }, + }, + correlationId, +}); diff --git a/packages/tenant-process/src/routers/TenantRouter.ts b/packages/tenant-process/src/routers/TenantRouter.ts index ab621d3f2c..7b503babf4 100644 --- a/packages/tenant-process/src/routers/TenantRouter.ts +++ b/packages/tenant-process/src/routers/TenantRouter.ts @@ -37,6 +37,7 @@ import { internalUpsertTenantErrorMapper, m2mRevokeCertifiedAttributeErrorMapper, m2mUpsertTenantErrorMapper, + assignTenantDelegatedProducerFeatureErrorMapper, } from "../utilities/errorMappers.js"; import { readModelServiceBuilder } from "../services/readModelService.js"; import { config } from "../config/config.js"; @@ -468,6 +469,30 @@ const tenantsRouter = ( return res.status(errorRes.status).send(errorRes); } } + ) + .post( + "/tenants/delegatedProducer", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + try { + await tenantService.assignTenantDelegatedProducerFeature({ + organizationId: req.ctx.authData.organizationId, + correlationId: req.ctx.correlationId, + authData: ctx.authData, + logger: ctx.logger, + }); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + assignTenantDelegatedProducerFeatureErrorMapper, + ctx.logger, + ctx.correlationId + ); + return res.status(errorRes.status).send(errorRes); + } + } ); const m2mRouter = ctx.router(tenantApi.m2mApi.api, { diff --git a/packages/tenant-process/src/services/tenantService.ts b/packages/tenant-process/src/services/tenantService.ts index ecea35ca68..07858091a4 100644 --- a/packages/tenant-process/src/services/tenantService.ts +++ b/packages/tenant-process/src/services/tenantService.ts @@ -6,6 +6,7 @@ import { WithLogger, AppContext, CreateEvent, + AuthData, } from "pagopa-interop-commons"; import { Attribute, @@ -28,6 +29,7 @@ import { TenantMail, TenantEvent, tenantMailKind, + TenantFeatureCertifier, CorrelationId, tenantKind, } from "pagopa-interop-models"; @@ -49,6 +51,7 @@ import { toCreateEventTenantVerifiedAttributeAssigned, toCreateEventMaintenanceTenantPromotedToCertifier, toCreateEventTenantVerifiedAttributeRevoked, + toCreateEventTenantDelegatedProducerFeatureAdded, } from "../model/domain/toEvent.js"; import { attributeAlreadyRevoked, @@ -80,6 +83,8 @@ import { assertRequesterAllowed, assertVerifiedAttributeOperationAllowed, retrieveCertifierId, + assertRequesterIPAOrigin, + assertDelegatedProducerFeatureNotAssigned, getTenantKind, } from "./validators.js"; import { ReadModelService } from "./readModelService.js"; @@ -1441,7 +1446,9 @@ export function tenantServiceBuilder( const tenant = await retrieveTenant(tenantId, readModelService); - const certifierFeature = tenant.data.features.find((a) => a.certifierId); + const certifierFeature = tenant.data.features.find( + (a): a is TenantFeatureCertifier => a.type === "PersistentCertifier" + ); if (certifierFeature) { if (certifierId === certifierFeature.certifierId) { @@ -1507,7 +1514,7 @@ export function tenantServiceBuilder( ); const certifierId = requesterTenant.data.features.find( - (f) => f.type === "PersistentCertifier" + (f): f is TenantFeatureCertifier => f.type === "PersistentCertifier" )?.certifierId; if (!certifierId) { @@ -1577,6 +1584,47 @@ export function tenantServiceBuilder( await repository.createEvent(attributeAssignmentEvent); } }, + async assignTenantDelegatedProducerFeature({ + organizationId, + correlationId, + authData, + logger, + }: { + organizationId: TenantId; + correlationId: CorrelationId; + authData: AuthData; + logger: Logger; + }): Promise { + logger.info( + `Assigning delegated producer feature to tenant ${organizationId}` + ); + + assertRequesterIPAOrigin(authData); + + const requesterTenant = await retrieveTenant( + organizationId, + readModelService + ); + + assertDelegatedProducerFeatureNotAssigned(requesterTenant.data); + + const updatedTenant: Tenant = { + ...requesterTenant.data, + features: [ + ...requesterTenant.data.features, + { type: "DelegatedProducer", availabilityTimestamp: new Date() }, + ], + updatedAt: new Date(), + }; + + await repository.createEvent( + toCreateEventTenantDelegatedProducerFeatureAdded( + requesterTenant.metadata.version, + updatedTenant, + correlationId + ) + ); + }, }; } diff --git a/packages/tenant-process/src/services/validators.ts b/packages/tenant-process/src/services/validators.ts index 175b95a2a5..d99263f4da 100644 --- a/packages/tenant-process/src/services/validators.ts +++ b/packages/tenant-process/src/services/validators.ts @@ -3,10 +3,14 @@ import { AgreementState, Attribute, AttributeId, + CONTRACT_AUTHORITY_PUBLIC_SERVICES_MANAGERS, EService, ExternalId, + PUBLIC_ADMINISTRATIONS_IDENTIFIER, + PUBLIC_SERVICES_MANAGERS, Tenant, TenantAttribute, + TenantFeatureCertifier, TenantId, TenantKind, TenantVerifier, @@ -15,6 +19,7 @@ import { operationForbidden, tenantAttributeType, tenantKind, + SCP, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { @@ -26,6 +31,7 @@ import { tenantIsNotACertifier, verifiedAttributeSelfVerificationNotAllowed, attributeNotFound, + tenantAlreadyHasDelegatedProducerFeature, } from "../model/domain/errors.js"; import { ReadModelService } from "./readModelService.js"; @@ -118,11 +124,6 @@ export function assertExpirationDateExist( } } -export const PUBLIC_ADMINISTRATIONS_IDENTIFIER = "IPA"; -const CONTRACT_AUTHORITY_PUBLIC_SERVICES_MANAGERS = "SAG"; -const PUBLIC_SERVICES_MANAGERS = "L37"; -export const SCP = "PDND_INFOCAMERE-SCP"; - export function getTenantKind( attributes: ExternalId[], externalId: ExternalId @@ -154,6 +155,12 @@ export async function assertRequesterAllowed( } } +export function assertRequesterIPAOrigin(authData: AuthData): void { + if (authData.externalId.origin !== PUBLIC_ADMINISTRATIONS_IDENTIFIER) { + throw operationForbidden; + } +} + export async function assertResourceAllowed( resourceId: string, authData: AuthData @@ -240,7 +247,7 @@ export function evaluateNewSelfcareId({ export function retrieveCertifierId(tenant: Tenant): string { const certifierFeature = tenant.features.find( - (f) => f.type === "PersistentCertifier" + (f): f is TenantFeatureCertifier => f.type === "PersistentCertifier" )?.certifierId; if (!certifierFeature) { @@ -248,3 +255,11 @@ export function retrieveCertifierId(tenant: Tenant): string { } return certifierFeature; } + +export function assertDelegatedProducerFeatureNotAssigned( + tenant: Tenant +): void { + if (tenant.features.some((f) => f.type === "DelegatedProducer")) { + throw tenantAlreadyHasDelegatedProducerFeature(tenant.id); + } +} diff --git a/packages/tenant-process/src/utilities/errorMappers.ts b/packages/tenant-process/src/utilities/errorMappers.ts index 65d1892143..54aa74b988 100644 --- a/packages/tenant-process/src/utilities/errorMappers.ts +++ b/packages/tenant-process/src/utilities/errorMappers.ts @@ -229,3 +229,13 @@ export const m2mRevokeCertifiedAttributeErrorMapper = ( ) .with("tenantIsNotACertifier", () => HTTP_STATUS_FORBIDDEN) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const assignTenantDelegatedProducerFeatureErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with( + "tenantAlreadyHasDelegatedProducerFeature", + () => HTTP_STATUS_CONFLICT + ) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/tenant-process/test/addCertifiedAttribute.test.ts b/packages/tenant-process/test/addCertifiedAttribute.test.ts index 447420f715..d0066a2a9f 100644 --- a/packages/tenant-process/test/addCertifiedAttribute.test.ts +++ b/packages/tenant-process/test/addCertifiedAttribute.test.ts @@ -1,7 +1,10 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-floating-promises */ import { genericLogger } from "pagopa-interop-commons"; -import { getMockTenant } from "pagopa-interop-commons-test"; +import { + getMockTenant, + getTenantOneCertifierFeature, +} from "pagopa-interop-commons-test"; import { Tenant, Attribute, @@ -59,7 +62,7 @@ describe("addCertifiedAttribute", async () => { ...getMockAttribute(), id: unsafeBrandId(tenantAttributeSeed.id), kind: attributeKind.certified, - origin: requesterTenant.features[0].certifierId, + origin: getTenantOneCertifierFeature(requesterTenant).certifierId, }; beforeAll(async () => { diff --git a/packages/tenant-process/test/assignTenantDelegatedProducerFeature.test.ts b/packages/tenant-process/test/assignTenantDelegatedProducerFeature.test.ts new file mode 100644 index 0000000000..6728565f59 --- /dev/null +++ b/packages/tenant-process/test/assignTenantDelegatedProducerFeature.test.ts @@ -0,0 +1,107 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { + generateId, + Tenant, + protobufDecoder, + toTenantV2, + TenantDelegatedProducerFeatureAddedV2, + operationForbidden, +} from "pagopa-interop-models"; +import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; +import { readLastEventByStreamId } from "pagopa-interop-commons-test/dist/eventStoreTestUtils.js"; +import { getMockAuthData, getMockTenant } from "pagopa-interop-commons-test"; +import { tenantAlreadyHasDelegatedProducerFeature } from "../src/model/domain/errors.js"; +import { addOneTenant, postgresDB, tenantService } from "./utils.js"; + +describe("assignTenantDelegatedProducerFeature", async () => { + beforeAll(async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + }); + + afterAll(() => { + vi.useRealTimers(); + }); + + it("Should correctly assign the feature", async () => { + const mockTenant = getMockTenant(); + await addOneTenant(mockTenant); + await tenantService.assignTenantDelegatedProducerFeature({ + organizationId: mockTenant.id, + correlationId: generateId(), + authData: getMockAuthData(), + logger: genericLogger, + }); + const writtenEvent = await readLastEventByStreamId( + mockTenant.id, + "tenant", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockTenant.id, + version: "1", + type: "TenantDelegatedProducerFeatureAdded", + event_version: 2, + }); + + const writtenPayload: TenantDelegatedProducerFeatureAddedV2 | undefined = + protobufDecoder(TenantDelegatedProducerFeatureAddedV2).parse( + writtenEvent.data + ); + + const updatedTenant: Tenant = { + ...mockTenant, + features: [ + ...mockTenant.features, + { + type: "DelegatedProducer", + availabilityTimestamp: new Date(), + }, + ], + updatedAt: new Date(), + }; + expect(writtenPayload.tenant).toEqual(toTenantV2(updatedTenant)); + }); + + it("Should throw tenantAlreadyHasDelegatedProducerFeature if the requester tenant already has the delegated producer feature", async () => { + const tenant: Tenant = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer", + availabilityTimestamp: new Date(), + }, + ], + }; + + await addOneTenant(tenant); + + expect( + tenantService.assignTenantDelegatedProducerFeature({ + organizationId: tenant.id, + correlationId: generateId(), + authData: getMockAuthData(), + logger: genericLogger, + }) + ).rejects.toThrowError(tenantAlreadyHasDelegatedProducerFeature(tenant.id)); + }); + it("Should throw operationForbidden if the requester tenant is not a public administration", async () => { + const tenant = getMockTenant(); + + await addOneTenant(tenant); + + expect( + tenantService.assignTenantDelegatedProducerFeature({ + organizationId: tenant.id, + correlationId: generateId(), + authData: { + ...getMockAuthData(), + externalId: { origin: "UNKNOWN", value: "test" }, + }, + logger: genericLogger, + }) + ).rejects.toThrowError(operationForbidden); + }); +}); diff --git a/packages/tenant-process/test/revokeCertifiedAttributeById.test.ts b/packages/tenant-process/test/revokeCertifiedAttributeById.test.ts index 7147a31e23..ccd8acdb2a 100644 --- a/packages/tenant-process/test/revokeCertifiedAttributeById.test.ts +++ b/packages/tenant-process/test/revokeCertifiedAttributeById.test.ts @@ -22,6 +22,7 @@ import { getMockTenant, readEventByStreamIdAndVersion, getRandomAuthData, + getTenantOneCertifierFeature, } from "pagopa-interop-commons-test"; import { tenantNotFound, @@ -53,7 +54,7 @@ describe("revokeCertifiedAttributeById", async () => { const attribute: Attribute = { ...getMockAttribute(), kind: attributeKind.certified, - origin: requesterTenant.features[0].certifierId, + origin: getTenantOneCertifierFeature(requesterTenant).certifierId, }; beforeAll(async () => { diff --git a/packages/tenant-process/test/selfcareUpsertTenant.test.ts b/packages/tenant-process/test/selfcareUpsertTenant.test.ts index d8b6baa577..6f19a3da66 100644 --- a/packages/tenant-process/test/selfcareUpsertTenant.test.ts +++ b/packages/tenant-process/test/selfcareUpsertTenant.test.ts @@ -12,16 +12,14 @@ import { TenantOnboardedV2, toTenantV2, CorrelationId, + PUBLIC_ADMINISTRATIONS_IDENTIFIER, + SCP, } from "pagopa-interop-models"; import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; import { tenantApi } from "pagopa-interop-api-clients"; import { getMockAuthData, getMockTenant } from "pagopa-interop-commons-test"; import { selfcareIdConflict } from "../src/model/domain/errors.js"; -import { - PUBLIC_ADMINISTRATIONS_IDENTIFIER, - SCP, -} from "../src/services/validators.js"; import { addOneTenant, readLastTenantEvent, tenantService } from "./utils.js"; describe("selfcareUpsertTenant", async () => { diff --git a/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts b/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts index 03fcaed360..26780629af 100644 --- a/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts +++ b/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts @@ -34,6 +34,7 @@ export async function handleMessageV2( { type: "MaintenanceTenantPromotedToCertifier" }, { type: "TenantMailDeleted" }, { type: "TenantKindUpdated" }, + { type: "TenantDelegatedProducerFeatureAdded" }, async (message) => await tenants.updateOne( { diff --git a/packages/tenant-readmodel-writer/test/converterV1.ts b/packages/tenant-readmodel-writer/test/converterV1.ts index d2b76f2a78..29799a15f1 100644 --- a/packages/tenant-readmodel-writer/test/converterV1.ts +++ b/packages/tenant-readmodel-writer/test/converterV1.ts @@ -35,7 +35,9 @@ export function toFeatureV1(feature: TenantFeature): TenantFeatureV1 { }, }, })) - .exhaustive(); + .otherwise(() => { + throw new Error("Unsupported tenant feature"); + }); } export function toTenantVerifierV1(verifier: TenantVerifier): TenantVerifierV1 { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3fb2443e3b..a4d8f45bdd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,8 +98,8 @@ importers: packages/agreement-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.4-b - version: 1.0.4-b + specifier: 1.0.6-b + version: 1.0.6-b '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -975,8 +975,8 @@ importers: packages/catalog-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.4-b - version: 1.0.4-b + specifier: 1.0.6-b + version: 1.0.6-b '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1645,6 +1645,143 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) + packages/delegation-process: + dependencies: + '@zodios/core': + specifier: 10.9.6 + version: 10.9.6(axios@1.7.4)(zod@3.23.8) + '@zodios/express': + specifier: 10.6.1 + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.19.2)(zod@3.23.8) + dotenv-flow: + specifier: 4.1.0 + version: 4.1.0 + express: + specifier: 4.19.2 + version: 4.19.2 + mongodb: + specifier: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) + openapi-zod-client: + specifier: 1.18.1 + version: 1.18.1 + pagopa-interop-api-clients: + specifier: workspace:* + version: link:../api-clients + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-models: + specifier: workspace:* + version: link:../models + ts-pattern: + specifier: 5.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@anatine/zod-mock': + specifier: 3.13.4 + version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + '@types/express': + specifier: 4.17.21 + version: 4.17.21 + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + date-fns: + specifier: 3.6.0 + version: 3.6.0 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + pdf-lib: + specifier: 1.17.1 + version: 1.17.1 + pg-promise: + specifier: 11.8.0 + version: 11.8.0 + prettier: + specifier: 2.8.8 + version: 2.8.8 + puppeteer: + specifier: 22.11.2 + version: 22.11.2(typescript@5.4.5) + testcontainers: + specifier: 10.9.0 + version: 10.9.0 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + + packages/delegation-readmodel-writer: + dependencies: + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + dotenv-flow: + specifier: 4.1.0 + version: 4.1.0 + kafka-iam-auth: + specifier: workspace:* + version: link:../kafka-iam-auth + kafkajs: + specifier: 2.2.4 + version: 2.2.4 + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-models: + specifier: workspace:* + version: link:../models + ts-pattern: + specifier: 5.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + testcontainers: + specifier: 10.9.0 + version: 10.9.0 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + packages/dtd-catalog-exporter: dependencies: dotenv-flow: @@ -2312,8 +2449,8 @@ importers: packages/purpose-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.4-b - version: 1.0.4-b + specifier: 1.0.6-b + version: 1.0.6-b '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2617,8 +2754,8 @@ importers: packages/tenant-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.4-b - version: 1.0.4-b + specifier: 1.0.6-b + version: 1.0.6-b '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -3974,8 +4111,14 @@ packages: '@pagopa/eslint-config@3.0.0': resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} - '@pagopa/interop-outbound-models@1.0.4-b': - resolution: {integrity: sha512-c1kxjIBCU0gqV2s3bLIdor+fIfvGLZgML2Y26dTdY3ppoXOhJBVTWKVBu5mq8nmZvZj0KnmK7tjeD6NUJjlywg==} + '@pagopa/interop-outbound-models@1.0.6-b': + resolution: {integrity: sha512-yxGueush6nX1XqU4/WHueJwNcZ56XCNDkz44RPPXLWCJSucLrw9gKO9Mc3kUA/ocGd0mIsLYOCnuRpr42Pw7Iw==} + + '@pdf-lib/standard-fonts@1.0.0': + resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==} + + '@pdf-lib/upng@1.0.1': + resolution: {integrity: sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -4641,9 +4784,6 @@ packages: '@types/lodash.isequal@4.5.8': resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} - '@types/lodash@4.17.13': - resolution: {integrity: sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==} - '@types/lodash@4.17.6': resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} @@ -5011,6 +5151,10 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + body-parser@1.20.3: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -5669,6 +5813,10 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + express@4.19.2: + resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} + engines: {node: '>= 0.10.0'} + express@4.20.0: resolution: {integrity: sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==} engines: {node: '>= 0.10.0'} @@ -6340,6 +6488,9 @@ packages: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} + merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} @@ -6617,6 +6768,9 @@ packages: package-json-from-dist@1.0.0: resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -6667,6 +6821,9 @@ packages: path-to-regexp@0.1.10: resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} + path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -6680,6 +6837,9 @@ packages: pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + pdf-lib@1.17.1: + resolution: {integrity: sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==} + pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -6982,6 +7142,10 @@ packages: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} + serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + serve-static@1.16.0: resolution: {integrity: sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==} engines: {node: '>= 0.8.0'} @@ -9494,7 +9658,7 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 + '@smithy/credential-provider-imds': 3.1.3 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 @@ -10476,12 +10640,20 @@ snapshots: - tsutils - typescript - '@pagopa/interop-outbound-models@1.0.4-b': + '@pagopa/interop-outbound-models@1.0.6-b': dependencies: '@protobuf-ts/runtime': 2.9.4 ts-pattern: 5.2.0 zod: 3.23.8 + '@pdf-lib/standard-fonts@1.0.0': + dependencies: + pako: 1.0.11 + + '@pdf-lib/upng@1.0.1': + dependencies: + pako: 1.0.11 + '@pkgjs/parseargs@0.11.0': optional: true @@ -11453,14 +11625,12 @@ snapshots: '@types/lodash.isempty@4.4.9': dependencies: - '@types/lodash': 4.17.13 + '@types/lodash': 4.17.6 '@types/lodash.isequal@4.5.8': dependencies: '@types/lodash': 4.17.6 - '@types/lodash@4.17.13': {} - '@types/lodash@4.17.6': {} '@types/mime@1.3.5': {} @@ -11660,6 +11830,12 @@ snapshots: axios: 1.7.4 zod: 3.23.8 + '@zodios/express@10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.19.2)(zod@3.23.8)': + dependencies: + '@zodios/core': 10.9.6(axios@1.7.4)(zod@3.23.8) + express: 4.19.2 + zod: 3.23.8 + '@zodios/express@10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8)': dependencies: '@zodios/core': 10.9.6(axios@1.7.4)(zod@3.23.8) @@ -11943,6 +12119,23 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + body-parser@1.20.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + body-parser@1.20.3: dependencies: bytes: 3.1.2 @@ -12765,6 +12958,42 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + express@4.19.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.2 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.6.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + express@4.20.0: dependencies: accepts: 1.3.8 @@ -13474,6 +13703,8 @@ snapshots: meow@12.1.1: {} + merge-descriptors@1.0.1: {} + merge-descriptors@1.0.3: {} merge-stream@2.0.0: {} @@ -13740,6 +13971,8 @@ snapshots: package-json-from-dist@1.0.0: {} + pako@1.0.11: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -13778,6 +14011,8 @@ snapshots: path-to-regexp@0.1.10: {} + path-to-regexp@0.1.7: {} + path-to-regexp@6.3.0: {} path-type@4.0.0: {} @@ -13786,6 +14021,13 @@ snapshots: pathval@1.1.1: {} + pdf-lib@1.17.1: + dependencies: + '@pdf-lib/standard-fonts': 1.0.0 + '@pdf-lib/upng': 1.0.1 + pako: 1.0.11 + tslib: 1.14.1 + pend@1.2.0: {} pg-cloudflare@1.1.1: @@ -14159,6 +14401,15 @@ snapshots: transitivePeerDependencies: - supports-color + serve-static@1.15.0: + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + serve-static@1.16.0: dependencies: encodeurl: 1.0.2 From 59dbe0f4c7cdcfa3217a3a779dc6ea6c84c49b03 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Tue, 12 Nov 2024 17:07:36 +0100 Subject: [PATCH 25/34] Fixing TS errors in delegation routers (#1194) --- packages/delegation-process/src/app.ts | 2 +- .../src/routers/DelegationProducerRouter.ts | 5 ++--- packages/delegation-process/src/routers/DelegationRouter.ts | 5 ++--- .../src/{utilites => utilities}/errorMappers.ts | 0 4 files changed, 5 insertions(+), 7 deletions(-) rename packages/delegation-process/src/{utilites => utilities}/errorMappers.ts (100%) diff --git a/packages/delegation-process/src/app.ts b/packages/delegation-process/src/app.ts index 007a7b0706..f111eb52fa 100644 --- a/packages/delegation-process/src/app.ts +++ b/packages/delegation-process/src/app.ts @@ -11,7 +11,7 @@ import delegationProducerRouter from "./routers/DelegationProducerRouter.js"; import delegationRouter from "./routers/DelegationRouter.js"; import { config } from "./config/config.js"; -const serviceName = "delgation-process"; +const serviceName = "delegation-process"; const app = zodiosCtx.app(); diff --git a/packages/delegation-process/src/routers/DelegationProducerRouter.ts b/packages/delegation-process/src/routers/DelegationProducerRouter.ts index 8e1dd50659..be00ca4d70 100644 --- a/packages/delegation-process/src/routers/DelegationProducerRouter.ts +++ b/packages/delegation-process/src/routers/DelegationProducerRouter.ts @@ -1,4 +1,3 @@ -import { ZodiosEndpointDefinitions } from "@zodios/core"; import { ZodiosRouter } from "@zodios/express"; import { delegationApi } from "pagopa-interop-api-clients"; import { @@ -24,7 +23,7 @@ import { revokeDelegationErrorMapper, approveDelegationErrorMapper, rejectDelegationErrorMapper, -} from "../utilites/errorMappers.js"; +} from "../utilities/errorMappers.js"; const readModelService = readModelServiceBuilder( ReadModelRepository.init(config) @@ -52,7 +51,7 @@ const { ADMIN_ROLE } = userRoles; const delegationProducerRouter = ( ctx: ZodiosContext -): ZodiosRouter => { +): ZodiosRouter => { const delegationProducerRouter = ctx.router(delegationApi.producerApi.api, { validationErrorHandler: zodiosValidationErrorToApiProblem, }); diff --git a/packages/delegation-process/src/routers/DelegationRouter.ts b/packages/delegation-process/src/routers/DelegationRouter.ts index 99f603305a..372da10e0a 100644 --- a/packages/delegation-process/src/routers/DelegationRouter.ts +++ b/packages/delegation-process/src/routers/DelegationRouter.ts @@ -1,4 +1,3 @@ -import { ZodiosEndpointDefinitions } from "@zodios/core"; import { ZodiosRouter } from "@zodios/express"; import { delegationApi } from "pagopa-interop-api-clients"; import { @@ -19,7 +18,7 @@ import { delegationToApiDelegation, } from "../model/domain/apiConverter.js"; import { makeApiProblem } from "../model/domain/errors.js"; -import { getDelegationErrorMapper } from "../utilites/errorMappers.js"; +import { getDelegationErrorMapper } from "../utilities/errorMappers.js"; import { delegationServiceBuilder } from "../services/delegationService.js"; const readModelService = readModelServiceBuilder( @@ -33,7 +32,7 @@ const { ADMIN_ROLE, API_ROLE, SECURITY_ROLE, M2M_ROLE, SUPPORT_ROLE } = const delegationRouter = ( ctx: ZodiosContext -): ZodiosRouter => { +): ZodiosRouter => { const delegationRouter = ctx.router(delegationApi.delegationApi.api, { validationErrorHandler: zodiosValidationErrorToApiProblem, }); diff --git a/packages/delegation-process/src/utilites/errorMappers.ts b/packages/delegation-process/src/utilities/errorMappers.ts similarity index 100% rename from packages/delegation-process/src/utilites/errorMappers.ts rename to packages/delegation-process/src/utilities/errorMappers.ts From 717cdc56aa0e94e3eb43fd0b687765bc6ea71c37 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Tue, 12 Nov 2024 17:45:58 +0100 Subject: [PATCH 26/34] Small fixes and refactors in `delegation-process` (#1195) --- .../src/routers/DelegationProducerRouter.ts | 9 +- .../src/routers/DelegationRouter.ts | 37 +++--- .../src/services/delegationProducerService.ts | 33 +++--- .../src/services/delegationService.ts | 50 +++++---- .../src/utilities/errorMappers.ts | 12 +- .../test/approveProducerDelegation.test.ts | 58 +++++----- .../test/getDelegationById.test.ts | 9 +- .../test/getDelegations.test.ts | 106 ++++++++++-------- .../test/rejectProducerDelegation.test.ts | 46 +++++--- 9 files changed, 209 insertions(+), 151 deletions(-) diff --git a/packages/delegation-process/src/routers/DelegationProducerRouter.ts b/packages/delegation-process/src/routers/DelegationProducerRouter.ts index be00ca4d70..f9fbe342b3 100644 --- a/packages/delegation-process/src/routers/DelegationProducerRouter.ts +++ b/packages/delegation-process/src/routers/DelegationProducerRouter.ts @@ -123,10 +123,8 @@ const delegationProducerRouter = ( try { await delegationProducerService.approveProducerDelegation( - ctx.authData.organizationId, unsafeBrandId(delegationId), - ctx.correlationId, - ctx.logger + ctx ); return res.status(204).send(); @@ -152,10 +150,9 @@ const delegationProducerRouter = ( try { await delegationProducerService.rejectProducerDelegation( - ctx.authData.organizationId, unsafeBrandId(delegationId), - ctx.correlationId, - rejectionReason + rejectionReason, + ctx ); return res.status(204).send(); diff --git a/packages/delegation-process/src/routers/DelegationRouter.ts b/packages/delegation-process/src/routers/DelegationRouter.ts index 372da10e0a..82aa33063b 100644 --- a/packages/delegation-process/src/routers/DelegationRouter.ts +++ b/packages/delegation-process/src/routers/DelegationRouter.ts @@ -18,7 +18,10 @@ import { delegationToApiDelegation, } from "../model/domain/apiConverter.js"; import { makeApiProblem } from "../model/domain/errors.js"; -import { getDelegationErrorMapper } from "../utilities/errorMappers.js"; +import { + getDelegationsErrorMapper, + getDelegationByIdErrorMapper, +} from "../utilities/errorMappers.js"; import { delegationServiceBuilder } from "../services/delegationService.js"; const readModelService = readModelServiceBuilder( @@ -60,17 +63,20 @@ const delegationRouter = ( } = req.query; try { - const delegations = await delegationService.getDelegations({ - delegateIds: delegateIds.map(unsafeBrandId), - delegatorIds: delegatorIds.map(unsafeBrandId), - delegationStates: delegationStates.map( - apiDelegationStateToDelegationState - ), - eserviceIds: eserviceIds.map(unsafeBrandId), - kind: kind && apiDelegationKindToDelegationKind(kind), - offset, - limit, - }); + const delegations = await delegationService.getDelegations( + { + delegateIds: delegateIds.map(unsafeBrandId), + delegatorIds: delegatorIds.map(unsafeBrandId), + delegationStates: delegationStates.map( + apiDelegationStateToDelegationState + ), + eserviceIds: eserviceIds.map(unsafeBrandId), + kind: kind && apiDelegationKindToDelegationKind(kind), + offset, + limit, + }, + ctx.logger + ); return res.status(200).send( delegationApi.Delegations.parse({ @@ -83,7 +89,7 @@ const delegationRouter = ( } catch (error) { const errorRes = makeApiProblem( error, - getDelegationErrorMapper, + getDelegationsErrorMapper, ctx.logger, ctx.correlationId ); @@ -107,7 +113,8 @@ const delegationRouter = ( try { const delegation = await delegationService.getDelegationById( - unsafeBrandId(delegationId) + unsafeBrandId(delegationId), + ctx.logger ); return res @@ -120,7 +127,7 @@ const delegationRouter = ( } catch (error) { const errorRes = makeApiProblem( error, - getDelegationErrorMapper, + getDelegationByIdErrorMapper, ctx.logger, ctx.correlationId ); diff --git a/packages/delegation-process/src/services/delegationProducerService.ts b/packages/delegation-process/src/services/delegationProducerService.ts index dc4ada7d38..f323a8de45 100644 --- a/packages/delegation-process/src/services/delegationProducerService.ts +++ b/packages/delegation-process/src/services/delegationProducerService.ts @@ -4,12 +4,10 @@ import { DB, eventRepository, FileManager, - Logger, PDFGenerator, WithLogger, } from "pagopa-interop-commons"; import { - CorrelationId, Delegation, DelegationId, delegationEventToBinaryDataV2, @@ -78,7 +76,7 @@ export function delegationProducerServiceBuilder( const eserviceId = unsafeBrandId(delegationSeed.eserviceId); logger.info( - `Creating a delegation for tenant:${delegationSeed.delegateId} by producer:${delegatorId}` + `Creating a delegation for tenant ${delegationSeed.delegateId} by producer ${delegatorId}` ); assertDelegatorIsNotDelegate(delegatorId, delegateId); @@ -126,7 +124,7 @@ export function delegationProducerServiceBuilder( ): Promise { const delegatorId = unsafeBrandId(authData.organizationId); logger.info( - `Revoking delegation:${delegationId} by producer:${delegatorId}` + `Revoking delegation ${delegationId} by producer ${delegatorId}` ); const currentDelegation = await retrieveDelegationById( @@ -180,25 +178,29 @@ export function delegationProducerServiceBuilder( return revokedDelegation; }, async approveProducerDelegation( - delegateId: TenantId, delegationId: DelegationId, - correlationId: CorrelationId, - logger: Logger + { logger, correlationId, authData }: WithLogger ): Promise { + const delegateId = unsafeBrandId(authData.organizationId); + + logger.info( + `Approving delegation ${delegationId} by delegate ${delegateId}` + ); + const { data: delegation, metadata } = await retrieveDelegationById( readModelService, delegationId ); + assertIsDelegate(delegation, delegateId); + assertIsState(delegationState.waitingForApproval, delegation); + const [delegator, delegate, eservice] = await Promise.all([ retrieveTenantById(delegation.delegatorId), retrieveTenantById(delegation.delegateId), retrieveEserviceById(delegation.eserviceId), ]); - assertIsDelegate(delegation, delegateId); - assertIsState(delegationState.waitingForApproval, delegation); - const activationContract = await contractBuilder.createActivationContract( { delegation, @@ -237,11 +239,16 @@ export function delegationProducerServiceBuilder( ); }, async rejectProducerDelegation( - delegateId: TenantId, delegationId: DelegationId, - correlationId: CorrelationId, - rejectionReason: string + rejectionReason: string, + { logger, correlationId, authData }: WithLogger ): Promise { + const delegateId = unsafeBrandId(authData.organizationId); + + logger.info( + `Rejecting delegation ${delegationId} by delegate ${delegateId}` + ); + const { data: delegation, metadata } = await retrieveDelegationById( readModelService, delegationId diff --git a/packages/delegation-process/src/services/delegationService.ts b/packages/delegation-process/src/services/delegationService.ts index c5dfda5677..0b55683fb4 100644 --- a/packages/delegation-process/src/services/delegationService.ts +++ b/packages/delegation-process/src/services/delegationService.ts @@ -7,6 +7,7 @@ import { TenantId, WithMetadata, } from "pagopa-interop-models"; +import { Logger } from "pagopa-interop-commons"; import { delegationNotFound } from "../model/domain/errors.js"; import { ReadModelService } from "./readModelService.js"; @@ -24,31 +25,42 @@ export const retrieveDelegationById = async ( // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function delegationServiceBuilder(readModelService: ReadModelService) { return { - async getDelegationById(delegationId: DelegationId): Promise { + async getDelegationById( + delegationId: DelegationId, + logger: Logger + ): Promise { + logger.info(`Retrieving delegation by id ${delegationId}`); + const delegation = await retrieveDelegationById( readModelService, delegationId ); return delegation.data; }, - // eslint-disable-next-line max-params - async getDelegations({ - delegateIds, - delegatorIds, - delegationStates, - eserviceIds, - kind, - offset, - limit, - }: { - delegateIds: TenantId[]; - delegatorIds: TenantId[]; - delegationStates: DelegationState[]; - eserviceIds: EServiceId[]; - kind: DelegationKind | undefined; - offset: number; - limit: number; - }): Promise { + async getDelegations( + { + delegateIds, + delegatorIds, + delegationStates, + eserviceIds, + kind, + offset, + limit, + }: { + delegateIds: TenantId[]; + delegatorIds: TenantId[]; + delegationStates: DelegationState[]; + eserviceIds: EServiceId[]; + kind: DelegationKind | undefined; + offset: number; + limit: number; + }, + logger: Logger + ): Promise { + logger.info( + `Retrieving delegations with filters: delegateIds=${delegateIds}, delegatorIds=${delegatorIds}, delegationStates=${delegationStates}, eserviceIds=${eserviceIds}, kind=${kind}, offset=${offset}, limit=${limit}` + ); + return readModelService.getDelegations({ delegateIds, delegatorIds, diff --git a/packages/delegation-process/src/utilities/errorMappers.ts b/packages/delegation-process/src/utilities/errorMappers.ts index b009d21344..98e2a40d3a 100644 --- a/packages/delegation-process/src/utilities/errorMappers.ts +++ b/packages/delegation-process/src/utilities/errorMappers.ts @@ -14,7 +14,12 @@ const { HTTP_STATUS_UNAUTHORIZED, } = constants; -export const getDelegationByIdsrrorMapper = ( +export const getDelegationsErrorMapper = ( + error: ApiError +): number => + match(error.code).otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const getDelegationByIdErrorMapper = ( error: ApiError ): number => match(error.code) @@ -36,11 +41,6 @@ export const createProducerDelegationErrorMapper = ( ) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); -export const getDelegationErrorMapper = (error: ApiError): number => - match(error.code) - .with("delegationNotFound", () => HTTP_STATUS_NOT_FOUND) - .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); - export const revokeDelegationErrorMapper = ( error: ApiError ): number => diff --git a/packages/delegation-process/test/approveProducerDelegation.test.ts b/packages/delegation-process/test/approveProducerDelegation.test.ts index c88b48a689..dc1841a5ca 100644 --- a/packages/delegation-process/test/approveProducerDelegation.test.ts +++ b/packages/delegation-process/test/approveProducerDelegation.test.ts @@ -4,6 +4,7 @@ import { getMockDelegationProducer, getMockTenant, getMockEService, + getMockAuthData, } from "pagopa-interop-commons-test/index.js"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { @@ -73,12 +74,12 @@ describe("approve delegation", () => { const { version } = await readLastDelegationEvent(delegation.id); expect(version).toBe("0"); - await delegationProducerService.approveProducerDelegation( - delegate.id, - delegation.id, - generateId(), - genericLogger - ); + await delegationProducerService.approveProducerDelegation(delegation.id, { + authData: getMockAuthData(delegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }); const event = await readLastDelegationEvent(delegation.id); expect(event.version).toBe("1"); @@ -158,10 +159,13 @@ describe("approve delegation", () => { await expect( delegationProducerService.approveProducerDelegation( - delegateId, nonExistentDelegationId, - generateId(), - genericLogger + { + authData: getMockAuthData(delegateId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } ) ).rejects.toThrow(delegationNotFound(nonExistentDelegationId)); }); @@ -178,12 +182,12 @@ describe("approve delegation", () => { await addOneDelegation(delegation); await expect( - delegationProducerService.approveProducerDelegation( - wrongDelegate.id, - delegation.id, - generateId(), - genericLogger - ) + delegationProducerService.approveProducerDelegation(delegation.id, { + authData: getMockAuthData(wrongDelegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }) ).rejects.toThrow( operationRestrictedToDelegate(wrongDelegate.id, delegation.id) ); @@ -199,12 +203,12 @@ describe("approve delegation", () => { await addOneDelegation(delegation); await expect( - delegationProducerService.approveProducerDelegation( - delegate.id, - delegation.id, - generateId(), - genericLogger - ) + delegationProducerService.approveProducerDelegation(delegation.id, { + authData: getMockAuthData(delegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }) ).rejects.toThrow( incorrectState( delegation.id, @@ -225,12 +229,12 @@ describe("approve delegation", () => { const { version } = await readLastDelegationEvent(delegation.id); expect(version).toBe("0"); - await delegationProducerService.approveProducerDelegation( - delegate.id, - delegation.id, - unsafeBrandId("9999"), - genericLogger - ); + await delegationProducerService.approveProducerDelegation(delegation.id, { + authData: getMockAuthData(delegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }); const contracts = await fileManager.listFiles( config.s3Bucket, diff --git a/packages/delegation-process/test/getDelegationById.test.ts b/packages/delegation-process/test/getDelegationById.test.ts index 8cd65334d5..ddaf026c79 100644 --- a/packages/delegation-process/test/getDelegationById.test.ts +++ b/packages/delegation-process/test/getDelegationById.test.ts @@ -2,6 +2,7 @@ import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; import { DelegationId, generateId } from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; import { delegationNotFound } from "../src/model/domain/errors.js"; import { addOneDelegation, delegationService } from "./utils.js"; @@ -12,7 +13,8 @@ describe("get delegation by id", () => { await addOneDelegation(delegation); const expectedDelegation = await delegationService.getDelegationById( - delegation.id + delegation.id, + genericLogger ); expect(delegation).toEqual(expectedDelegation); @@ -24,7 +26,10 @@ describe("get delegation by id", () => { await addOneDelegation(delegation); const notFoundId = generateId(); - const expectedDelegation = delegationService.getDelegationById(notFoundId); + const expectedDelegation = delegationService.getDelegationById( + notFoundId, + genericLogger + ); await expect(expectedDelegation).rejects.toThrow( delegationNotFound(notFoundId) diff --git a/packages/delegation-process/test/getDelegations.test.ts b/packages/delegation-process/test/getDelegations.test.ts index 37a9b7a836..e7ba0c8381 100644 --- a/packages/delegation-process/test/getDelegations.test.ts +++ b/packages/delegation-process/test/getDelegations.test.ts @@ -1,6 +1,7 @@ /* eslint-disable functional/no-let */ import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; import { describe, expect, it } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; import { addOneDelegation, delegationService } from "./utils.js"; describe("get delegations", () => { @@ -10,59 +11,74 @@ describe("get delegations", () => { await addOneDelegation(delegation1); await addOneDelegation(delegation2); - const res1 = await delegationService.getDelegations({ - delegateIds: [], - delegatorIds: [], - delegationStates: ["Active"], - eserviceIds: [], - kind: "DelegatedProducer", - offset: 0, - limit: 50, - }); + const res1 = await delegationService.getDelegations( + { + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }, + genericLogger + ); expect(res1).toEqual([delegation1]); - const res2 = await delegationService.getDelegations({ - delegateIds: [delegation2.delegateId], - delegatorIds: [], - delegationStates: [], - eserviceIds: [], - kind: "DelegatedProducer", - offset: 0, - limit: 50, - }); + const res2 = await delegationService.getDelegations( + { + delegateIds: [delegation2.delegateId], + delegatorIds: [], + delegationStates: [], + eserviceIds: [], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }, + genericLogger + ); expect(res2).toEqual([delegation2]); - const res3 = await delegationService.getDelegations({ - delegateIds: [], - delegatorIds: [], - delegationStates: ["Revoked"], - eserviceIds: [], - kind: "DelegatedProducer", - offset: 0, - limit: 50, - }); + const res3 = await delegationService.getDelegations( + { + delegateIds: [], + delegatorIds: [], + delegationStates: ["Revoked"], + eserviceIds: [], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }, + genericLogger + ); expect(res3).toEqual([]); - const res4 = await delegationService.getDelegations({ - delegateIds: [], - delegatorIds: [], - delegationStates: ["Active"], - eserviceIds: [], - kind: undefined, - offset: 0, - limit: 50, - }); + const res4 = await delegationService.getDelegations( + { + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [], + kind: undefined, + offset: 0, + limit: 50, + }, + genericLogger + ); expect(res4).toEqual([delegation1]); - const res5 = await delegationService.getDelegations({ - delegateIds: [], - delegatorIds: [], - delegationStates: ["Active"], - eserviceIds: [delegation1.eserviceId], - kind: "DelegatedProducer", - offset: 0, - limit: 50, - }); + const res5 = await delegationService.getDelegations( + { + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [delegation1.eserviceId], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }, + genericLogger + ); expect(res5).toEqual([delegation1]); }); }); diff --git a/packages/delegation-process/test/rejectProducerDelegation.test.ts b/packages/delegation-process/test/rejectProducerDelegation.test.ts index bcc1bbe7ea..f24401386d 100644 --- a/packages/delegation-process/test/rejectProducerDelegation.test.ts +++ b/packages/delegation-process/test/rejectProducerDelegation.test.ts @@ -1,6 +1,7 @@ /* eslint-disable functional/no-let */ import { decodeProtobufPayload, + getMockAuthData, getMockDelegationProducer, getMockTenant, } from "pagopa-interop-commons-test/index.js"; @@ -13,6 +14,7 @@ import { unsafeBrandId, } from "pagopa-interop-models"; import { delegationState } from "pagopa-interop-models"; +import { genericLogger } from "pagopa-interop-commons"; import { delegationNotFound, operationRestrictedToDelegate, @@ -40,10 +42,14 @@ describe("reject delegation", () => { const rejectionReason = "I don't like computers, please send me a pigeon"; await delegationProducerService.rejectProducerDelegation( - delegate.id, delegation.id, - generateId(), - rejectionReason + rejectionReason, + { + authData: getMockAuthData(delegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } ); const event = await readLastDelegationEvent(delegation.id); @@ -72,10 +78,14 @@ describe("reject delegation", () => { await expect( delegationProducerService.rejectProducerDelegation( - delegateId, nonExistentDelegationId, - generateId(), - "" + "", + { + authData: getMockAuthData(delegateId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } ) ).rejects.toThrow(delegationNotFound(nonExistentDelegationId)); }); @@ -90,12 +100,12 @@ describe("reject delegation", () => { await addOneDelegation(delegation); await expect( - delegationProducerService.rejectProducerDelegation( - wrongDelegate.id, - delegation.id, - generateId(), - "" - ) + delegationProducerService.rejectProducerDelegation(delegation.id, "", { + authData: getMockAuthData(wrongDelegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }) ).rejects.toThrow( operationRestrictedToDelegate(wrongDelegate.id, delegation.id) ); @@ -110,12 +120,12 @@ describe("reject delegation", () => { await addOneDelegation(delegation); await expect( - delegationProducerService.rejectProducerDelegation( - delegate.id, - delegation.id, - generateId(), - "" - ) + delegationProducerService.rejectProducerDelegation(delegation.id, "", { + authData: getMockAuthData(delegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }) ).rejects.toThrow( incorrectState( delegation.id, From fe5344aa326205fa61d716683d98fdf1d8726d66 Mon Sep 17 00:00:00 2001 From: Alessio Gallitano <25105748+galales@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:57:06 +0100 Subject: [PATCH 27/34] PIN-5632: Add maintenance endpoint to tenant process (#1196) --- .../agreement-outbound-writer/package.json | 4 +- packages/api-clients/open-api/tenantApi.yml | 61 + packages/catalog-outbound-writer/package.json | 4 +- .../compute-agreements-consumer/src/index.ts | 1 + packages/models/proto/v2/tenant/events.proto | 4 + packages/models/src/tenant/tenantEvents.ts | 9 + packages/purpose-outbound-writer/package.json | 4 +- packages/tenant-outbound-writer/package.json | 4 +- .../src/converters/toOutboundEventV2.ts | 1 + .../src/model/domain/toEvent.ts | 17 + .../src/routers/TenantRouter.ts | 28 + .../src/services/tenantService.ts | 44 + .../src/utilities/errorMappers.ts | 7 + .../src/tenantConsumerServiceV2.ts | 1 + .../test/consumerServiceV2.test.ts | 57 + pnpm-lock.yaml | 1862 ++++++++++------- 16 files changed, 1316 insertions(+), 792 deletions(-) diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index c414df4e13..d0bf7bbb3c 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.6-b", + "@pagopa/interop-outbound-models": "1.0.10", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", @@ -40,4 +40,4 @@ "ts-pattern": "5.2.0", "zod": "3.23.8" } -} +} \ No newline at end of file diff --git a/packages/api-clients/open-api/tenantApi.yml b/packages/api-clients/open-api/tenantApi.yml index 4bed5af187..5cf569ea6d 100644 --- a/packages/api-clients/open-api/tenantApi.yml +++ b/packages/api-clients/open-api/tenantApi.yml @@ -863,6 +863,26 @@ paths: required: - currentVersion required: true + post: + tags: + - tenant + summary: Updates a tenant + operationId: maintenanceTenantUpdate + responses: + "204": + description: Tenant Updated + "404": + description: Tenant not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/MaintenanceTenantUpdatePayload" + required: true /maintenance/tenants/{tenantId}/certifier: parameters: - $ref: "#/components/parameters/CorrelationIdHeader" @@ -1468,6 +1488,47 @@ components: type: string required: - certifierId + MaintenanceTenantUpdatePayload: + description: Maintenance payload to update tenant fields + type: object + additionalProperties: false + properties: + currentVersion: + type: number + tenant: + $ref: "#/components/schemas/MaintenanceTenantUpdate" + required: + - currentVersion + - tenant + MaintenanceTenantUpdate: + description: Tenant model + type: object + additionalProperties: false + properties: + selfcareId: + type: string + externalId: + $ref: "#/components/schemas/ExternalId" + mails: + type: array + items: + $ref: "#/components/schemas/Mail" + name: + type: string + kind: + $ref: "#/components/schemas/TenantKind" + onboardedAt: + type: string + format: date-time + subUnitType: + $ref: "#/components/schemas/TenantUnitType" + required: + - selfcareId + - externalId + - mails + - name + - kind + - onboardedAt Problem: properties: type: diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index 1cd5b3c363..5e81c6a5db 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.6-b", + "@pagopa/interop-outbound-models": "1.0.10", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", @@ -40,4 +40,4 @@ "ts-pattern": "5.2.0", "zod": "3.23.8" } -} +} \ No newline at end of file diff --git a/packages/compute-agreements-consumer/src/index.ts b/packages/compute-agreements-consumer/src/index.ts index 2fa68f8742..116ddc08b9 100644 --- a/packages/compute-agreements-consumer/src/index.ts +++ b/packages/compute-agreements-consumer/src/index.ts @@ -92,6 +92,7 @@ async function processMessage({ "TenantVerifiedAttributeExpirationUpdated", "TenantKindUpdated", "MaintenanceTenantDeleted", + "MaintenanceTenantUpdated", "TenantMailDeleted", "TenantMailAdded", "MaintenanceTenantPromotedToCertifier", diff --git a/packages/models/proto/v2/tenant/events.proto b/packages/models/proto/v2/tenant/events.proto index a45cfe7e38..70fb333ef8 100644 --- a/packages/models/proto/v2/tenant/events.proto +++ b/packages/models/proto/v2/tenant/events.proto @@ -57,6 +57,10 @@ message MaintenanceTenantDeletedV2 { TenantV2 tenant = 2; } +message MaintenanceTenantUpdatedV2 { + TenantV2 tenant = 1; +} + message TenantMailAddedV2 { string mailId = 1; TenantV2 tenant = 2; diff --git a/packages/models/src/tenant/tenantEvents.ts b/packages/models/src/tenant/tenantEvents.ts index 8a31948355..14321eb4d4 100644 --- a/packages/models/src/tenant/tenantEvents.ts +++ b/packages/models/src/tenant/tenantEvents.ts @@ -26,6 +26,7 @@ import { TenantMailDeletedV2, TenantKindUpdatedV2, TenantDelegatedProducerFeatureAddedV2, + MaintenanceTenantUpdatedV2, } from "../gen/v2/tenant/events.js"; import { protobufDecoder } from "../protobuf/protobuf.js"; import { EventEnvelope } from "../events/events.js"; @@ -98,6 +99,9 @@ export function tenantEventToBinaryDataV2(event: TenantEventV2): Uint8Array { .with({ type: "MaintenanceTenantDeleted" }, ({ data }) => MaintenanceTenantDeletedV2.toBinary(data) ) + .with({ type: "MaintenanceTenantUpdated" }, ({ data }) => + MaintenanceTenantUpdatedV2.toBinary(data) + ) .with({ type: "TenantMailAdded" }, ({ data }) => TenantMailAddedV2.toBinary(data) ) @@ -212,6 +216,11 @@ export const TenantEventV2 = z.discriminatedUnion("type", [ type: z.literal("MaintenanceTenantDeleted"), data: protobufDecoder(MaintenanceTenantDeletedV2), }), + z.object({ + event_version: z.literal(2), + type: z.literal("MaintenanceTenantUpdated"), + data: protobufDecoder(MaintenanceTenantUpdatedV2), + }), z.object({ event_version: z.literal(2), type: z.literal("TenantMailAdded"), diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index 857f575ae2..88741fed89 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.6-b", + "@pagopa/interop-outbound-models": "1.0.10", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", @@ -40,4 +40,4 @@ "ts-pattern": "5.2.0", "zod": "3.23.8" } -} +} \ No newline at end of file diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 7a950f251c..e7577c6274 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.6-b", + "@pagopa/interop-outbound-models": "1.0.10", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", @@ -40,4 +40,4 @@ "ts-pattern": "5.2.0", "zod": "3.23.8" } -} +} \ No newline at end of file diff --git a/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts b/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts index f95023456a..4ba155f4d4 100644 --- a/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts +++ b/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts @@ -45,6 +45,7 @@ export function toOutboundEventV2( { type: "TenantOnboarded" }, { type: "TenantOnboardDetailsUpdated" }, { type: "MaintenanceTenantPromotedToCertifier" }, + { type: "MaintenanceTenantUpdated" }, { type: "TenantDelegatedProducerFeatureAdded" }, (msg) => ({ event_version: msg.event_version, diff --git a/packages/tenant-process/src/model/domain/toEvent.ts b/packages/tenant-process/src/model/domain/toEvent.ts index dd4fa94063..5b65157807 100644 --- a/packages/tenant-process/src/model/domain/toEvent.ts +++ b/packages/tenant-process/src/model/domain/toEvent.ts @@ -174,6 +174,23 @@ export const toCreateEventMaintenanceTenantDeleted = ( correlationId, }); +export const toCreateEventMaintenanceTenantUpdated = ( + version: number, + tenant: Tenant, + correlationId: CorrelationId +): CreateEvent => ({ + streamId: tenant.id, + version, + event: { + event_version: 2, + type: "MaintenanceTenantUpdated", + data: { + tenant: toTenantV2(tenant), + }, + }, + correlationId, +}); + export const toCreateEventTenantVerifiedAttributeAssigned = ( version: number, updatedTenant: Tenant, diff --git a/packages/tenant-process/src/routers/TenantRouter.ts b/packages/tenant-process/src/routers/TenantRouter.ts index 7b503babf4..4300013729 100644 --- a/packages/tenant-process/src/routers/TenantRouter.ts +++ b/packages/tenant-process/src/routers/TenantRouter.ts @@ -37,6 +37,7 @@ import { internalUpsertTenantErrorMapper, m2mRevokeCertifiedAttributeErrorMapper, m2mUpsertTenantErrorMapper, + maintenanceTenantUpdatedErrorMapper, assignTenantDelegatedProducerFeatureErrorMapper, } from "../utilities/errorMappers.js"; import { readModelServiceBuilder } from "../services/readModelService.js"; @@ -442,6 +443,33 @@ const tenantsRouter = ( } } ) + .post( + "/maintenance/tenants/:tenantId", + authorizationMiddleware([MAINTENANCE_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + try { + await tenantService.maintenanceTenantUpdate( + { + tenantId: unsafeBrandId(req.params.tenantId), + version: req.body.currentVersion, + tenantUpdate: req.body.tenant, + correlationId: ctx.correlationId, + }, + ctx.logger + ); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + maintenanceTenantUpdatedErrorMapper, + ctx.logger, + ctx.correlationId + ); + return res.status(errorRes.status).send(errorRes); + } + } + ) .delete( "/tenants/:tenantId/mails/:mailId", authorizationMiddleware([ADMIN_ROLE, API_ROLE]), diff --git a/packages/tenant-process/src/services/tenantService.ts b/packages/tenant-process/src/services/tenantService.ts index 07858091a4..1c4cd1444c 100644 --- a/packages/tenant-process/src/services/tenantService.ts +++ b/packages/tenant-process/src/services/tenantService.ts @@ -51,6 +51,7 @@ import { toCreateEventTenantVerifiedAttributeAssigned, toCreateEventMaintenanceTenantPromotedToCertifier, toCreateEventTenantVerifiedAttributeRevoked, + toCreateEventMaintenanceTenantUpdated, toCreateEventTenantDelegatedProducerFeatureAdded, } from "../model/domain/toEvent.js"; import { @@ -1045,6 +1046,49 @@ export function tenantServiceBuilder( ); }, + async maintenanceTenantUpdate( + { + tenantId, + tenantUpdate, + version, + correlationId, + }: { + tenantId: TenantId; + tenantUpdate: tenantApi.MaintenanceTenantUpdate; + version: number; + correlationId: CorrelationId; + }, + logger: Logger + ): Promise { + logger.info(`Maintenance update Tenant ${tenantId}`); + + const tenant = await retrieveTenant(tenantId, readModelService); + + const convertedTenantUpdate = { + ...tenantUpdate, + mails: tenantUpdate.mails.map((mail) => ({ + ...mail, + createdAt: new Date(mail.createdAt), + })), + onboardedAt: new Date(tenantUpdate.onboardedAt), + }; + + const updatedTenant: Tenant = { + ...tenant.data, + ...convertedTenantUpdate, + subUnitType: convertedTenantUpdate.subUnitType, + updatedAt: new Date(), + }; + + await repository.createEvent( + toCreateEventMaintenanceTenantUpdated( + version, + updatedTenant, + correlationId + ) + ); + }, + async deleteTenantMailById( { tenantId, diff --git a/packages/tenant-process/src/utilities/errorMappers.ts b/packages/tenant-process/src/utilities/errorMappers.ts index 54aa74b988..fc44f5b714 100644 --- a/packages/tenant-process/src/utilities/errorMappers.ts +++ b/packages/tenant-process/src/utilities/errorMappers.ts @@ -136,6 +136,13 @@ export const maintenanceTenantDeletedErrorMapper = ( .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); +export const maintenanceTenantUpdatedErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + export const verifyVerifiedAttributeErrorMapper = ( error: ApiError ): number => diff --git a/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts b/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts index 26780629af..e5637e7943 100644 --- a/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts +++ b/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts @@ -32,6 +32,7 @@ export async function handleMessageV2( { type: "TenantVerifiedAttributeExtensionUpdated" }, { type: "TenantMailAdded" }, { type: "MaintenanceTenantPromotedToCertifier" }, + { type: "MaintenanceTenantUpdated" }, { type: "TenantMailDeleted" }, { type: "TenantKindUpdated" }, { type: "TenantDelegatedProducerFeatureAdded" }, diff --git a/packages/tenant-readmodel-writer/test/consumerServiceV2.test.ts b/packages/tenant-readmodel-writer/test/consumerServiceV2.test.ts index ee8e492078..c860c6abb4 100644 --- a/packages/tenant-readmodel-writer/test/consumerServiceV2.test.ts +++ b/packages/tenant-readmodel-writer/test/consumerServiceV2.test.ts @@ -28,6 +28,8 @@ import { TenantKindV2, tenantKind, MaintenanceTenantDeletedV2, + tenantUnitType, + MaintenanceTenantUpdatedV2, } from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; import { handleMessageV2 } from "../src/tenantConsumerServiceV2.js"; @@ -534,4 +536,59 @@ describe("Tenant Events V2", async () => { expect(retrievedTenant).toBeNull(); }); + it("MaintenanceTenantUpdated", async () => { + const mail = getMockTenantMail(); + + const tenant: Tenant = { + ...mockTenant, + selfcareId: crypto.randomUUID(), + externalId: { + origin: "o1", + value: "v1", + }, + mails: [mail], + name: "old_name", + kind: tenantKind.GSP, + onboardedAt: new Date(), + subUnitType: tenantUnitType.AOO, + }; + + await writeInReadmodel(toReadModelTenant(tenant), tenants, 1); + + const updatedTenant: Tenant = { + ...mockTenant, + selfcareId: crypto.randomUUID(), + externalId: { + origin: "o2", + value: "v2", + }, + mails: [getMockTenantMail()], + name: "new_name", + kind: tenantKind.PA, + onboardedAt: new Date(), + subUnitType: tenantUnitType.UO, + }; + + const payload: MaintenanceTenantUpdatedV2 = { + tenant: toTenantV2(updatedTenant), + }; + + const message: TenantEventEnvelopeV2 = { + ...mockMessage, + type: "MaintenanceTenantUpdated", + data: payload, + version: 2, + }; + + await handleMessageV2(message, tenants); + + const retrievedTenant = await tenants.findOne({ + "data.id": mockTenant.id, + }); + + expect(retrievedTenant?.data).toEqual(toReadModelTenant(updatedTenant)); + expect(retrievedTenant?.metadata).toEqual({ + version: 2, + }); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4d8f45bdd..1885d59312 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,8 +98,8 @@ importers: packages/agreement-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.6-b - version: 1.0.6-b + specifier: 1.0.10 + version: 1.0.10 '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -975,8 +975,8 @@ importers: packages/catalog-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.6-b - version: 1.0.6-b + specifier: 1.0.10 + version: 1.0.10 '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2449,8 +2449,8 @@ importers: packages/purpose-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.6-b - version: 1.0.6-b + specifier: 1.0.10 + version: 1.0.10 '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2754,8 +2754,8 @@ importers: packages/tenant-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.6-b - version: 1.0.6-b + specifier: 1.0.10 + version: 1.0.10 '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -3520,6 +3520,10 @@ packages: resolution: {integrity: sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==} engines: {node: '>=16.0.0'} + '@aws-sdk/types@3.667.0': + resolution: {integrity: sha512-gYq0xCsqFfQaSL/yT1Gl1vIUjtsg7d7RhnUfsXaHt8xTxOKRTdH9GjbesBjXOzgOvB0W0vfssfreSNGFlOOMJg==} + engines: {node: '>=16.0.0'} + '@aws-sdk/util-arn-parser@3.310.0': resolution: {integrity: sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==} engines: {node: '>=14.0.0'} @@ -4111,8 +4115,8 @@ packages: '@pagopa/eslint-config@3.0.0': resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} - '@pagopa/interop-outbound-models@1.0.6-b': - resolution: {integrity: sha512-yxGueush6nX1XqU4/WHueJwNcZ56XCNDkz44RPPXLWCJSucLrw9gKO9Mc3kUA/ocGd0mIsLYOCnuRpr42Pw7Iw==} + '@pagopa/interop-outbound-models@1.0.10': + resolution: {integrity: sha512-/TDVP8j+Q0ErpVs7MzH9LhfiEPcOzqea00um4sjQ9uuA6/Yg0G9A739bDOuaxih2wKpJ3hNPLnm9riM+YAz6Ow==} '@pdf-lib/standard-fonts@1.0.0': resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==} @@ -4281,6 +4285,10 @@ packages: resolution: {integrity: sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==} engines: {node: '>=16.0.0'} + '@smithy/abort-controller@3.1.5': + resolution: {integrity: sha512-DhNPnqTqPoG8aZ5dWkFOgsuY+i0GQ3CI6hMmvCoduNsnU9gUZWZBwGfDQsTTB7NvFPkom1df7jMIJWU90kuXXg==} + engines: {node: '>=16.0.0'} + '@smithy/chunked-blob-reader-native@2.2.0': resolution: {integrity: sha512-VNB5+1oCgX3Fzs072yuRsUoC2N4Zg/LJ11DTxX3+Qu+Paa6AmbIF0E9sc2wthz9Psrk/zcOlTCyuposlIhPjZQ==} @@ -4305,6 +4313,10 @@ packages: resolution: {integrity: sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==} engines: {node: '>=16.0.0'} + '@smithy/config-resolver@3.0.9': + resolution: {integrity: sha512-5d9oBf40qC7n2xUoHmntKLdqsyTMMo/r49+eqSIjJ73eDfEtljAxEhzIQ3bkgXJtR3xiv7YzMT/3FF3ORkjWdg==} + engines: {node: '>=16.0.0'} + '@smithy/core@2.2.4': resolution: {integrity: sha512-qdY3LpMOUyLM/gfjjMQZui+UTNS7kBRDWlvyIhVOql5dn2J3isk9qUTBtQ1CbDH8MTugHis1zu3h4rH+Qmmh4g==} engines: {node: '>=16.0.0'} @@ -4313,18 +4325,22 @@ packages: resolution: {integrity: sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==} engines: {node: '>=16.0.0'} + '@smithy/core@2.4.8': + resolution: {integrity: sha512-x4qWk7p/a4dcf7Vxb2MODIf4OIcqNbK182WxRvZ/3oKPrf/6Fdic5sSElhO1UtXpWKBazWfqg0ZEK9xN1DsuHA==} + engines: {node: '>=16.0.0'} + '@smithy/credential-provider-imds@2.3.0': resolution: {integrity: sha512-BWB9mIukO1wjEOo1Ojgl6LrG4avcaC7T/ZP6ptmAaW4xluhSIPZhY+/PI5YKzlk+jsm+4sQZB45Bt1OfMeQa3w==} engines: {node: '>=14.0.0'} - '@smithy/credential-provider-imds@3.1.3': - resolution: {integrity: sha512-U1Yrv6hx/mRK6k8AncuI6jLUx9rn0VVSd9NPEX6pyYFBfkSkChOc/n4zUb8alHUVg83TbI4OdZVo1X0Zfj3ijA==} - engines: {node: '>=16.0.0'} - '@smithy/credential-provider-imds@3.2.0': resolution: {integrity: sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==} engines: {node: '>=16.0.0'} + '@smithy/credential-provider-imds@3.2.4': + resolution: {integrity: sha512-S9bb0EIokfYEuar4kEbLta+ivlKCWOCFsLZuilkNy9i0uEUEHSi47IFLPaxqqCl+0ftKmcOTHayY5nQhAuq7+w==} + engines: {node: '>=16.0.0'} + '@smithy/eventstream-codec@2.2.0': resolution: {integrity: sha512-8janZoJw85nJmQZc4L8TuePp2pk1nxLgkxIR0TUjKJ5Dkj5oelB9WtiSSGXCQvNsJl0VSTvK/2ueMXxvpa9GVw==} @@ -4372,6 +4388,9 @@ packages: '@smithy/fetch-http-handler@3.2.4': resolution: {integrity: sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==} + '@smithy/fetch-http-handler@3.2.9': + resolution: {integrity: sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==} + '@smithy/hash-blob-browser@2.2.0': resolution: {integrity: sha512-SGPoVH8mdXBqrkVCJ1Hd1X7vh1zDXojNN1yZyZTZsCno99hVue9+IYzWDjq/EQDDXxmITB0gBmuyPh8oAZSTcg==} @@ -4386,6 +4405,10 @@ packages: resolution: {integrity: sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==} engines: {node: '>=16.0.0'} + '@smithy/hash-node@3.0.7': + resolution: {integrity: sha512-SAGHN+QkrwcHFjfWzs/czX94ZEjPJ0CrWJS3M43WswDXVEuP4AVy9gJ3+AF6JQHZD13bojmuf/Ap/ItDeZ+Qfw==} + engines: {node: '>=16.0.0'} + '@smithy/hash-stream-node@2.2.0': resolution: {integrity: sha512-aT+HCATOSRMGpPI7bi7NSsTNVZE/La9IaxLXWoVAYMxHT5hGO3ZOGEMZQg8A6nNL+pdFGtZQtND1eoY084HgHQ==} engines: {node: '>=14.0.0'} @@ -4400,6 +4423,9 @@ packages: '@smithy/invalid-dependency@3.0.3': resolution: {integrity: sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==} + '@smithy/invalid-dependency@3.0.7': + resolution: {integrity: sha512-Bq00GsAhHeYSuZX8Kpu4sbI9agH2BNYnqUmmbTGWOhki9NVsWn2jFr896vvoTMH8KAjNX/ErC/8t5QHuEXG+IA==} + '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} @@ -4426,6 +4452,10 @@ packages: resolution: {integrity: sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==} engines: {node: '>=16.0.0'} + '@smithy/middleware-content-length@3.0.9': + resolution: {integrity: sha512-t97PidoGElF9hTtLCrof32wfWMqC5g2SEJNxaVH3NjlatuNGsdxXRYO/t+RPnxA15RpYiS0f+zG7FuE2DeGgjA==} + engines: {node: '>=16.0.0'} + '@smithy/middleware-endpoint@2.5.1': resolution: {integrity: sha512-1/8kFp6Fl4OsSIVTWHnNjLnTL8IqpIb/D3sTSczrKFnrE9VMNWxnrRKNvpUHOJ6zpGD5f62TPm7+17ilTJpiCQ==} engines: {node: '>=14.0.0'} @@ -4438,6 +4468,10 @@ packages: resolution: {integrity: sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==} engines: {node: '>=16.0.0'} + '@smithy/middleware-endpoint@3.1.4': + resolution: {integrity: sha512-/ChcVHekAyzUbyPRI8CzPPLj6y8QRAfJngWcLMgsWxKVzw/RzBV69mSOzJYDD3pRwushA1+5tHtPF8fjmzBnrQ==} + engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@2.3.1': resolution: {integrity: sha512-P2bGufFpFdYcWvqpyqqmalRtwFUNUA8vHjJR5iGqbfR6mp65qKOLcUd6lTr4S9Gn/enynSrSf3p3FVgVAf6bXA==} engines: {node: '>=14.0.0'} @@ -4446,6 +4480,10 @@ packages: resolution: {integrity: sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==} engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@3.0.23': + resolution: {integrity: sha512-x9PbGXxkcXIpm6L26qRSCC+eaYcHwybRmqU8LO/WM2RRlW0g8lz6FIiKbKgGvHuoK3dLZRiQVSQJveiCzwnA5A==} + engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@3.0.7': resolution: {integrity: sha512-f5q7Y09G+2h5ivkSx5CHvlAT4qRR3jBFEsfXyQ9nFNiWQlr8c48blnu5cmbTQ+p1xmIO14UXzKoF8d7Tm0Gsjw==} engines: {node: '>=16.0.0'} @@ -4458,6 +4496,10 @@ packages: resolution: {integrity: sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==} engines: {node: '>=16.0.0'} + '@smithy/middleware-serde@3.0.7': + resolution: {integrity: sha512-VytaagsQqtH2OugzVTq4qvjkLNbWehHfGcGr0JLJmlDRrNCeZoWkWsSOw1nhS/4hyUUWF/TLGGml4X/OnEep5g==} + engines: {node: '>=16.0.0'} + '@smithy/middleware-stack@2.2.0': resolution: {integrity: sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA==} engines: {node: '>=14.0.0'} @@ -4466,6 +4508,10 @@ packages: resolution: {integrity: sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==} engines: {node: '>=16.0.0'} + '@smithy/middleware-stack@3.0.7': + resolution: {integrity: sha512-EyTbMCdqS1DoeQsO4gI7z2Gzq1MoRFAeS8GkFYIwbedB7Lp5zlLHJdg+56tllIIG5Hnf9ZWX48YKSHlsKvugGA==} + engines: {node: '>=16.0.0'} + '@smithy/node-config-provider@2.3.0': resolution: {integrity: sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg==} engines: {node: '>=14.0.0'} @@ -4478,6 +4524,10 @@ packages: resolution: {integrity: sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==} engines: {node: '>=16.0.0'} + '@smithy/node-config-provider@3.1.8': + resolution: {integrity: sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==} + engines: {node: '>=16.0.0'} + '@smithy/node-http-handler@2.5.0': resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==} engines: {node: '>=14.0.0'} @@ -4490,6 +4540,10 @@ packages: resolution: {integrity: sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==} engines: {node: '>=16.0.0'} + '@smithy/node-http-handler@3.2.4': + resolution: {integrity: sha512-49reY3+JgLMFNm7uTAKBWiKCA6XSvkNp9FqhVmusm2jpVnHORYFeFZ704LShtqWfjZW/nhX+7Iexyb6zQfXYIQ==} + engines: {node: '>=16.0.0'} + '@smithy/property-provider@2.2.0': resolution: {integrity: sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==} engines: {node: '>=14.0.0'} @@ -4498,6 +4552,10 @@ packages: resolution: {integrity: sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==} engines: {node: '>=16.0.0'} + '@smithy/property-provider@3.1.7': + resolution: {integrity: sha512-QfzLi1GPMisY7bAM5hOUqBdGYnY5S2JAlr201pghksrQv139f8iiiMalXtjczIP5f6owxFn3MINLNUNvUkgtPw==} + engines: {node: '>=16.0.0'} + '@smithy/protocol-http@2.0.5': resolution: {integrity: sha512-d2hhHj34mA2V86doiDfrsy2fNTnUOowGaf9hKb0hIPHqvcnShU4/OSc4Uf1FwHkAdYF3cFXTrj5VGUYbEuvMdw==} engines: {node: '>=14.0.0'} @@ -4514,6 +4572,10 @@ packages: resolution: {integrity: sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==} engines: {node: '>=16.0.0'} + '@smithy/protocol-http@4.1.4': + resolution: {integrity: sha512-MlWK8eqj0JlpZBnWmjQLqmFp71Ug00P+m72/1xQB3YByXD4zZ+y9N4hYrR0EDmrUCZIkyATWHOXFgtavwGDTzQ==} + engines: {node: '>=16.0.0'} + '@smithy/querystring-builder@2.2.0': resolution: {integrity: sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==} engines: {node: '>=14.0.0'} @@ -4522,6 +4584,10 @@ packages: resolution: {integrity: sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==} engines: {node: '>=16.0.0'} + '@smithy/querystring-builder@3.0.7': + resolution: {integrity: sha512-65RXGZZ20rzqqxTsChdqSpbhA6tdt5IFNgG6o7e1lnPVLCe6TNWQq4rTl4N87hTDD8mV4IxJJnvyE7brbnRkQw==} + engines: {node: '>=16.0.0'} + '@smithy/querystring-parser@2.2.0': resolution: {integrity: sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA==} engines: {node: '>=14.0.0'} @@ -4530,6 +4596,10 @@ packages: resolution: {integrity: sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==} engines: {node: '>=16.0.0'} + '@smithy/querystring-parser@3.0.7': + resolution: {integrity: sha512-Fouw4KJVWqqUVIu1gZW8BH2HakwLz6dvdrAhXeXfeymOBrZw+hcqaWs+cS1AZPVp4nlbeIujYrKA921ZW2WMPA==} + engines: {node: '>=16.0.0'} + '@smithy/service-error-classification@2.1.5': resolution: {integrity: sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ==} engines: {node: '>=14.0.0'} @@ -4538,18 +4608,22 @@ packages: resolution: {integrity: sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==} engines: {node: '>=16.0.0'} + '@smithy/service-error-classification@3.0.7': + resolution: {integrity: sha512-91PRkTfiBf9hxkIchhRKJfl1rsplRDyBnmyFca3y0Z3x/q0JJN480S83LBd8R6sBCkm2bBbqw2FHp0Mbh+ecSA==} + engines: {node: '>=16.0.0'} + '@smithy/shared-ini-file-loader@2.4.0': resolution: {integrity: sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA==} engines: {node: '>=14.0.0'} - '@smithy/shared-ini-file-loader@3.1.3': - resolution: {integrity: sha512-Z8Y3+08vgoDgl4HENqNnnzSISAaGrF2RoKupoC47u2wiMp+Z8P/8mDh1CL8+8ujfi2U5naNvopSBmP/BUj8b5w==} - engines: {node: '>=16.0.0'} - '@smithy/shared-ini-file-loader@3.1.4': resolution: {integrity: sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==} engines: {node: '>=16.0.0'} + '@smithy/shared-ini-file-loader@3.1.8': + resolution: {integrity: sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==} + engines: {node: '>=16.0.0'} + '@smithy/signature-v4@2.3.0': resolution: {integrity: sha512-ui/NlpILU+6HAQBfJX8BBsDXuKSNrjTSuOYArRblcrErwKFutjrCNb/OExfVRyj9+26F9J+ZmfWT+fKWuDrH3Q==} engines: {node: '>=14.0.0'} @@ -4562,6 +4636,10 @@ packages: resolution: {integrity: sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==} engines: {node: '>=16.0.0'} + '@smithy/signature-v4@4.2.0': + resolution: {integrity: sha512-LafbclHNKnsorMgUkKm7Tk7oJ7xizsZ1VwqhGKqoCIrXh4fqDDp73fK99HOEEgcsQbtemmeY/BPv0vTVYYUNEQ==} + engines: {node: '>=16.0.0'} + '@smithy/smithy-client@2.5.1': resolution: {integrity: sha512-jrbSQrYCho0yDaaf92qWgd+7nAeap5LtHTI51KXqmpIFCceKU3K9+vIVTUH72bOJngBMqa4kyu1VJhRcSrk/CQ==} engines: {node: '>=14.0.0'} @@ -4574,12 +4652,16 @@ packages: resolution: {integrity: sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==} engines: {node: '>=16.0.0'} + '@smithy/smithy-client@3.4.0': + resolution: {integrity: sha512-nOfJ1nVQsxiP6srKt43r2My0Gp5PLWCW2ASqUioxIiGmu6d32v4Nekidiv5qOmmtzIrmaD+ADX5SKHUuhReeBQ==} + engines: {node: '>=16.0.0'} + '@smithy/types@2.12.0': resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==} engines: {node: '>=14.0.0'} - '@smithy/types@3.3.0': - resolution: {integrity: sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==} + '@smithy/types@3.5.0': + resolution: {integrity: sha512-QN0twHNfe8mNJdH9unwsCK13GURU7oEAZqkBI+rsvpv1jrmserO+WnLE7jidR9W/1dxwZ0u/CB01mV2Gms/K2Q==} engines: {node: '>=16.0.0'} '@smithy/url-parser@2.2.0': @@ -4588,6 +4670,9 @@ packages: '@smithy/url-parser@3.0.3': resolution: {integrity: sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==} + '@smithy/url-parser@3.0.7': + resolution: {integrity: sha512-70UbSSR8J97c1rHZOWhl+VKiZDqHWxs/iW8ZHrHp5fCCPLSBE7GcUlUvKSle3Ca+J9LLbYCj/A79BxztBvAfpA==} + '@smithy/util-base64@2.3.0': resolution: {integrity: sha512-s3+eVwNeJuXUwuMbusncZNViuhv2LjVJ1nMwTqSA0XAC7gjKhqqxRdJPhR8+YrkoZ9IiIbFk/yK6ACe/xlF+hw==} engines: {node: '>=14.0.0'} @@ -4634,6 +4719,10 @@ packages: resolution: {integrity: sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==} engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@3.0.23': + resolution: {integrity: sha512-Y07qslyRtXDP/C5aWKqxTPBl4YxplEELG3xRrz2dnAQ6Lq/FgNrcKWmV561nNaZmFH+EzeGOX3ZRMbU8p1T6Nw==} + engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@3.0.7': resolution: {integrity: sha512-Q2txLyvQyGfmjsaDbVV7Sg8psefpFcrnlGapDzXGFRPFKRBeEg6OvFK8FljqjeHSaCZ6/UuzQExUPqBR/2qlDA==} engines: {node: '>= 10.0.0'} @@ -4646,6 +4735,10 @@ packages: resolution: {integrity: sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==} engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@3.0.23': + resolution: {integrity: sha512-9Y4WH7f0vnDGuHUa4lGX9e2p+sMwODibsceSV6rfkZOvMC+BY3StB2LdO1NHafpsyHJLpwAgChxQ38tFyd6vkg==} + engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@3.0.7': resolution: {integrity: sha512-F4Qcj1fG6MGi2BSWCslfsMSwllws/WzYONBGtLybyY+halAcXdWhcew+mej8M5SKd5hqPYp4f7b+ABQEaeytgg==} engines: {node: '>= 10.0.0'} @@ -4658,6 +4751,10 @@ packages: resolution: {integrity: sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==} engines: {node: '>=16.0.0'} + '@smithy/util-endpoints@2.1.3': + resolution: {integrity: sha512-34eACeKov6jZdHqS5hxBMJ4KyWKztTMulhuQ2UdOoP6vVxMLrOKUqIXAwJe/wiWMhXhydLW664B02CNpQBQ4Aw==} + engines: {node: '>=16.0.0'} + '@smithy/util-hex-encoding@2.2.0': resolution: {integrity: sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ==} engines: {node: '>=14.0.0'} @@ -4674,6 +4771,10 @@ packages: resolution: {integrity: sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==} engines: {node: '>=16.0.0'} + '@smithy/util-middleware@3.0.7': + resolution: {integrity: sha512-OVA6fv/3o7TMJTpTgOi1H5OTwnuUa8hzRzhSFDtZyNxi6OZ70L/FHattSmhE212I7b6WSOJAAmbYnvcjTHOJCA==} + engines: {node: '>=16.0.0'} + '@smithy/util-retry@2.2.0': resolution: {integrity: sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g==} engines: {node: '>= 14.0.0'} @@ -4682,18 +4783,22 @@ packages: resolution: {integrity: sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==} engines: {node: '>=16.0.0'} + '@smithy/util-retry@3.0.7': + resolution: {integrity: sha512-nh1ZO1vTeo2YX1plFPSe/OXaHkLAHza5jpokNiiKX2M5YpNUv6RxGJZhpfmiR4jSvVHCjIDmILjrxKmP+/Ghug==} + engines: {node: '>=16.0.0'} + '@smithy/util-stream@2.2.0': resolution: {integrity: sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA==} engines: {node: '>=14.0.0'} - '@smithy/util-stream@3.0.5': - resolution: {integrity: sha512-xC3L5PKMAT/Bh8fmHNXP9sdQ4+4aKVUU3EEJ2CF/lLk7R+wtMJM+v/1B4en7jO++Wa5spGzFDBCl0QxgbUc5Ug==} - engines: {node: '>=16.0.0'} - '@smithy/util-stream@3.1.3': resolution: {integrity: sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==} engines: {node: '>=16.0.0'} + '@smithy/util-stream@3.1.9': + resolution: {integrity: sha512-7YAR0Ub3MwTMjDfjnup4qa6W8gygZMxikBhFMPESi6ASsl/rZJhwLpF/0k9TuezScCojsM0FryGdz4LZtjKPPQ==} + engines: {node: '>=16.0.0'} + '@smithy/util-uri-escape@2.2.0': resolution: {integrity: sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==} engines: {node: '>=14.0.0'} @@ -5762,7 +5867,6 @@ packages: eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true espree@3.0.0-alpha-1: @@ -7830,25 +7934,25 @@ snapshots: '@aws-crypto/crc32@3.0.0': dependencies: '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 1.14.1 '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 2.6.3 '@aws-crypto/crc32c@3.0.0': dependencies: '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 1.14.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 2.6.3 '@aws-crypto/ie11-detection@3.0.0': @@ -7860,7 +7964,7 @@ snapshots: '@aws-crypto/ie11-detection': 3.0.0 '@aws-crypto/supports-web-crypto': 3.0.0 '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-locate-window': 3.568.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 @@ -7869,7 +7973,7 @@ snapshots: dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-locate-window': 3.568.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 @@ -7880,7 +7984,7 @@ snapshots: '@aws-crypto/sha256-js': 3.0.0 '@aws-crypto/supports-web-crypto': 3.0.0 '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-locate-window': 3.568.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 @@ -7890,7 +7994,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-locate-window': 3.568.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 @@ -7898,19 +8002,19 @@ snapshots: '@aws-crypto/sha256-js@3.0.0': dependencies: '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 1.14.1 '@aws-crypto/sha256-js@4.0.0': dependencies: '@aws-crypto/util': 4.0.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 1.14.1 '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 2.6.3 '@aws-crypto/supports-web-crypto@3.0.0': @@ -7923,19 +8027,19 @@ snapshots: '@aws-crypto/util@3.0.0': dependencies: - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 '@aws-crypto/util@4.0.0': dependencies: - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 @@ -7956,30 +8060,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8017,7 +8121,7 @@ snapshots: '@smithy/node-http-handler': 3.1.4 '@smithy/protocol-http': 4.1.0 '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 @@ -8066,7 +8170,7 @@ snapshots: '@smithy/node-http-handler': 3.1.4 '@smithy/protocol-http': 4.1.0 '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 @@ -8115,7 +8219,7 @@ snapshots: '@smithy/node-http-handler': 3.1.4 '@smithy/protocol-http': 4.1.0 '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 @@ -8149,30 +8253,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.598.0 '@aws-sdk/util-user-agent-browser': 3.598.0 '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.4 - '@smithy/core': 2.2.4 - '@smithy/fetch-http-handler': 3.2.0 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.3 - '@smithy/middleware-endpoint': 3.0.4 - '@smithy/middleware-retry': 3.0.7 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.3 - '@smithy/node-http-handler': 3.1.1 - '@smithy/protocol-http': 4.0.3 - '@smithy/smithy-client': 3.1.5 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.7 - '@smithy/util-defaults-mode-node': 3.0.7 - '@smithy/util-endpoints': 2.0.4 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8285,7 +8389,7 @@ snapshots: '@smithy/node-http-handler': 3.1.4 '@smithy/protocol-http': 4.1.0 '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 @@ -8332,7 +8436,7 @@ snapshots: '@smithy/node-http-handler': 3.1.4 '@smithy/protocol-http': 4.1.0 '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 @@ -8380,7 +8484,7 @@ snapshots: '@smithy/node-http-handler': 3.1.1 '@smithy/protocol-http': 4.0.3 '@smithy/smithy-client': 3.1.5 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 @@ -8411,30 +8515,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.598.0 '@aws-sdk/util-user-agent-browser': 3.598.0 '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8457,30 +8561,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8502,30 +8606,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.614.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8547,30 +8651,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.637.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8592,30 +8696,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.645.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8673,30 +8777,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.598.0 '@aws-sdk/util-user-agent-browser': 3.598.0 '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8716,30 +8820,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8759,30 +8863,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.614.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8802,30 +8906,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.637.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8845,30 +8949,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.645.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8932,30 +9036,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.598.0 '@aws-sdk/util-user-agent-browser': 3.598.0 '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8977,30 +9081,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -9022,30 +9126,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.614.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -9067,30 +9171,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.637.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -9112,30 +9216,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.645.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -9143,46 +9247,46 @@ snapshots: '@aws-sdk/core@3.598.0': dependencies: - '@smithy/core': 2.4.0 - '@smithy/protocol-http': 4.1.0 + '@smithy/core': 2.4.8 + '@smithy/protocol-http': 4.1.4 '@smithy/signature-v4': 3.1.2 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 fast-xml-parser: 4.2.5 tslib: 2.6.3 '@aws-sdk/core@3.609.0': dependencies: - '@smithy/core': 2.4.0 - '@smithy/protocol-http': 4.1.0 + '@smithy/core': 2.4.8 + '@smithy/protocol-http': 4.1.4 '@smithy/signature-v4': 3.1.2 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 fast-xml-parser: 4.2.5 tslib: 2.6.3 '@aws-sdk/core@3.620.1': dependencies: - '@smithy/core': 2.4.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/signature-v4': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/core': 2.4.8 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/signature-v4': 4.2.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 fast-xml-parser: 4.2.5 tslib: 2.6.3 '@aws-sdk/core@3.635.0': dependencies: - '@smithy/core': 2.4.0 - '@smithy/node-config-provider': 3.1.4 + '@smithy/core': 2.4.8 + '@smithy/node-config-provider': 3.1.8 '@smithy/property-provider': 3.1.3 - '@smithy/protocol-http': 4.1.0 + '@smithy/protocol-http': 4.1.4 '@smithy/signature-v4': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 fast-xml-parser: 4.4.1 tslib: 2.6.3 @@ -9190,8 +9294,8 @@ snapshots: dependencies: '@aws-sdk/client-cognito-identity': 3.609.0 '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - aws-crt @@ -9206,70 +9310,70 @@ snapshots: '@aws-sdk/credential-provider-env@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-env@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-env@3.620.1': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-http@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-stream': 3.1.3 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 tslib: 2.6.3 '@aws-sdk/credential-provider-http@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-stream': 3.1.3 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 tslib: 2.6.3 '@aws-sdk/credential-provider-http@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-stream': 3.1.3 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 tslib: 2.6.3 '@aws-sdk/credential-provider-http@3.635.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-stream': 3.1.3 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 tslib: 2.6.3 '@aws-sdk/credential-provider-ini@3.387.0': @@ -9296,10 +9400,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0) '@aws-sdk/types': 3.598.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9314,10 +9418,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9332,10 +9436,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.620.1) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9350,10 +9454,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9368,10 +9472,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9402,10 +9506,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0) '@aws-sdk/types': 3.598.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9421,10 +9525,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9443,7 +9547,7 @@ snapshots: '@smithy/credential-provider-imds': 3.2.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9459,10 +9563,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9478,10 +9582,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9499,25 +9603,25 @@ snapshots: '@aws-sdk/credential-provider-process@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-process@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-process@3.620.1': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-sso@3.387.0': @@ -9537,9 +9641,9 @@ snapshots: '@aws-sdk/client-sso': 3.598.0 '@aws-sdk/token-providers': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9550,9 +9654,9 @@ snapshots: '@aws-sdk/client-sso': 3.609.0 '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9563,9 +9667,9 @@ snapshots: '@aws-sdk/client-sso': 3.620.1 '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9576,9 +9680,9 @@ snapshots: '@aws-sdk/client-sso': 3.637.0 '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9589,9 +9693,9 @@ snapshots: '@aws-sdk/client-sso': 3.645.0 '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9608,40 +9712,40 @@ snapshots: dependencies: '@aws-sdk/client-sts': 3.600.0 '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.620.1)': dependencies: '@aws-sdk/client-sts': 3.620.1 '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/client-sts': 3.637.0 '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.645.0)': dependencies: '@aws-sdk/client-sts': 3.645.0 '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': @@ -9658,9 +9762,9 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.1.3 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9684,9 +9788,9 @@ snapshots: dependencies: '@aws-sdk/types': 3.598.0 '@aws-sdk/util-arn-parser': 3.568.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 tslib: 2.6.3 @@ -9694,18 +9798,18 @@ snapshots: dependencies: '@aws-sdk/endpoint-cache': 3.572.0 '@aws-sdk/types': 3.598.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-endpoint-discovery@3.620.0': dependencies: '@aws-sdk/endpoint-cache': 3.572.0 '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-expect-continue@3.387.0': @@ -9718,8 +9822,8 @@ snapshots: '@aws-sdk/middleware-expect-continue@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.0.3 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-flexible-checksums@3.387.0': @@ -9739,8 +9843,8 @@ snapshots: '@aws-crypto/crc32c': 5.2.0 '@aws-sdk/types': 3.598.0 '@smithy/is-array-buffer': 3.0.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -9754,22 +9858,22 @@ snapshots: '@aws-sdk/middleware-host-header@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-host-header@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-host-header@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-location-constraint@3.387.0': @@ -9781,7 +9885,7 @@ snapshots: '@aws-sdk/middleware-location-constraint@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-logger@3.387.0': @@ -9793,13 +9897,13 @@ snapshots: '@aws-sdk/middleware-logger@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-logger@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-recursion-detection@3.387.0': @@ -9812,22 +9916,22 @@ snapshots: '@aws-sdk/middleware-recursion-detection@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-recursion-detection@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-recursion-detection@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-sdk-s3@3.387.0': @@ -9842,11 +9946,11 @@ snapshots: dependencies: '@aws-sdk/types': 3.598.0 '@aws-sdk/util-arn-parser': 3.568.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 '@smithy/signature-v4': 3.1.2 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 tslib: 2.6.3 @@ -9854,21 +9958,21 @@ snapshots: dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-arn-parser': 3.568.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/signature-v4': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/signature-v4': 4.2.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-stream': 3.1.3 + '@smithy/util-stream': 3.1.9 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 '@aws-sdk/middleware-sdk-sqs@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/smithy-client': 3.1.5 - '@smithy/types': 3.3.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 '@smithy/util-hex-encoding': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -9894,10 +9998,10 @@ snapshots: dependencies: '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.3 - '@smithy/protocol-http': 4.1.0 + '@smithy/protocol-http': 4.1.4 '@smithy/signature-v4': 3.1.2 - '@smithy/types': 3.3.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@aws-sdk/middleware-ssec@3.387.0': @@ -9909,7 +10013,7 @@ snapshots: '@aws-sdk/middleware-ssec@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-user-agent@3.387.0': @@ -9924,67 +10028,67 @@ snapshots: dependencies: '@aws-sdk/types': 3.598.0 '@aws-sdk/util-endpoints': 3.598.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-user-agent@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.609.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-user-agent@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.614.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-user-agent@3.637.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.637.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-user-agent@3.645.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.645.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/region-config-resolver@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@aws-sdk/region-config-resolver@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@aws-sdk/region-config-resolver@3.614.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@aws-sdk/s3-request-presigner@3.623.0': @@ -9995,7 +10099,7 @@ snapshots: '@smithy/middleware-endpoint': 3.1.0 '@smithy/protocol-http': 4.1.0 '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/signature-v4-multi-region@3.387.0': @@ -10010,18 +10114,18 @@ snapshots: dependencies: '@aws-sdk/middleware-sdk-s3': 3.598.0 '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.1.0 + '@smithy/protocol-http': 4.1.4 '@smithy/signature-v4': 3.1.2 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/signature-v4-multi-region@3.622.0': dependencies: '@aws-sdk/middleware-sdk-s3': 3.622.0 '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/signature-v4': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/signature-v4': 4.2.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/token-providers@3.387.0': @@ -10036,45 +10140,45 @@ snapshots: dependencies: '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))': dependencies: '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.620.1) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.637.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/types@3.387.0': @@ -10084,12 +10188,17 @@ snapshots: '@aws-sdk/types@3.598.0': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/types@3.609.0': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@aws-sdk/types@3.667.0': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/util-arn-parser@3.310.0': @@ -10128,43 +10237,43 @@ snapshots: '@aws-sdk/util-endpoints@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.3.0 - '@smithy/util-endpoints': 2.0.5 + '@smithy/types': 3.5.0 + '@smithy/util-endpoints': 2.1.3 tslib: 2.6.3 '@aws-sdk/util-endpoints@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.3.0 - '@smithy/util-endpoints': 2.0.5 + '@smithy/types': 3.5.0 + '@smithy/util-endpoints': 2.1.3 tslib: 2.6.3 '@aws-sdk/util-endpoints@3.614.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.3.0 - '@smithy/util-endpoints': 2.0.5 + '@smithy/types': 3.5.0 + '@smithy/util-endpoints': 2.1.3 tslib: 2.6.3 '@aws-sdk/util-endpoints@3.637.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.3.0 - '@smithy/util-endpoints': 2.0.5 + '@smithy/types': 3.5.0 + '@smithy/util-endpoints': 2.1.3 tslib: 2.6.3 '@aws-sdk/util-endpoints@3.645.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.3.0 - '@smithy/util-endpoints': 2.0.5 + '@smithy/types': 3.5.0 + '@smithy/util-endpoints': 2.1.3 tslib: 2.6.3 '@aws-sdk/util-format-url@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/querystring-builder': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/util-locate-window@3.568.0': @@ -10181,14 +10290,14 @@ snapshots: '@aws-sdk/util-user-agent-browser@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 bowser: 2.11.0 tslib: 2.6.3 '@aws-sdk/util-user-agent-browser@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 bowser: 2.11.0 tslib: 2.6.3 @@ -10202,22 +10311,22 @@ snapshots: '@aws-sdk/util-user-agent-node@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/util-user-agent-node@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/util-user-agent-node@3.614.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/util-utf8-browser@3.259.0': @@ -10230,7 +10339,7 @@ snapshots: '@aws-sdk/xml-builder@3.598.0': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@babel/code-frame@7.24.7': @@ -10640,7 +10749,7 @@ snapshots: - tsutils - typescript - '@pagopa/interop-outbound-models@1.0.6-b': + '@pagopa/interop-outbound-models@1.0.10': dependencies: '@protobuf-ts/runtime': 2.9.4 ts-pattern: 5.2.0 @@ -10794,7 +10903,12 @@ snapshots: '@smithy/abort-controller@3.1.1': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/abort-controller@3.1.5': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/chunked-blob-reader-native@2.2.0': @@ -10825,41 +10939,62 @@ snapshots: '@smithy/config-resolver@3.0.4': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@smithy/config-resolver@3.0.5': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 + tslib: 2.6.3 + + '@smithy/config-resolver@3.0.9': + dependencies: + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 + '@smithy/util-config-provider': 3.0.0 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@smithy/core@2.2.4': dependencies: - '@smithy/middleware-endpoint': 3.0.4 - '@smithy/middleware-retry': 3.0.7 - '@smithy/middleware-serde': 3.0.3 - '@smithy/protocol-http': 4.0.3 - '@smithy/smithy-client': 3.1.5 - '@smithy/types': 3.3.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@smithy/core@2.4.0': dependencies: - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.3 + + '@smithy/core@2.4.8': + dependencies: + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-middleware': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -10871,20 +11006,20 @@ snapshots: '@smithy/url-parser': 2.2.0 tslib: 2.6.3 - '@smithy/credential-provider-imds@3.1.3': + '@smithy/credential-provider-imds@3.2.0': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/node-config-provider': 3.1.8 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 tslib: 2.6.3 - '@smithy/credential-provider-imds@3.2.0': + '@smithy/credential-provider-imds@3.2.4': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/node-config-provider': 3.1.8 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 tslib: 2.6.3 '@smithy/eventstream-codec@2.2.0': @@ -10897,7 +11032,7 @@ snapshots: '@smithy/eventstream-codec@3.1.2': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/util-hex-encoding': 3.0.0 tslib: 2.6.3 @@ -10910,7 +11045,7 @@ snapshots: '@smithy/eventstream-serde-browser@3.0.4': dependencies: '@smithy/eventstream-serde-universal': 3.0.4 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/eventstream-serde-config-resolver@2.2.0': @@ -10920,7 +11055,7 @@ snapshots: '@smithy/eventstream-serde-config-resolver@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/eventstream-serde-node@2.2.0': @@ -10932,7 +11067,7 @@ snapshots: '@smithy/eventstream-serde-node@3.0.4': dependencies: '@smithy/eventstream-serde-universal': 3.0.4 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/eventstream-serde-universal@2.2.0': @@ -10944,7 +11079,7 @@ snapshots: '@smithy/eventstream-serde-universal@3.0.4': dependencies: '@smithy/eventstream-codec': 3.1.2 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/fetch-http-handler@2.5.0': @@ -10957,17 +11092,25 @@ snapshots: '@smithy/fetch-http-handler@3.2.0': dependencies: - '@smithy/protocol-http': 4.0.3 - '@smithy/querystring-builder': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/querystring-builder': 3.0.7 + '@smithy/types': 3.5.0 '@smithy/util-base64': 3.0.0 tslib: 2.6.3 '@smithy/fetch-http-handler@3.2.4': dependencies: - '@smithy/protocol-http': 4.1.0 + '@smithy/protocol-http': 4.1.4 '@smithy/querystring-builder': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + '@smithy/util-base64': 3.0.0 + tslib: 2.6.3 + + '@smithy/fetch-http-handler@3.2.9': + dependencies: + '@smithy/protocol-http': 4.1.4 + '@smithy/querystring-builder': 3.0.7 + '@smithy/types': 3.5.0 '@smithy/util-base64': 3.0.0 tslib: 2.6.3 @@ -10982,7 +11125,7 @@ snapshots: dependencies: '@smithy/chunked-blob-reader': 3.0.0 '@smithy/chunked-blob-reader-native': 3.0.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/hash-node@2.2.0': @@ -10994,7 +11137,14 @@ snapshots: '@smithy/hash-node@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.3 + + '@smithy/hash-node@3.0.7': + dependencies: + '@smithy/types': 3.5.0 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -11007,7 +11157,7 @@ snapshots: '@smithy/hash-stream-node@3.1.2': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -11018,7 +11168,12 @@ snapshots: '@smithy/invalid-dependency@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/invalid-dependency@3.0.7': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/is-array-buffer@2.2.0': @@ -11037,7 +11192,7 @@ snapshots: '@smithy/md5-js@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -11049,14 +11204,20 @@ snapshots: '@smithy/middleware-content-length@3.0.3': dependencies: - '@smithy/protocol-http': 4.0.3 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/middleware-content-length@3.0.5': dependencies: - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/middleware-content-length@3.0.9': + dependencies: + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/middleware-endpoint@2.5.1': @@ -11071,22 +11232,32 @@ snapshots: '@smithy/middleware-endpoint@3.0.4': dependencies: - '@smithy/middleware-serde': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/shared-ini-file-loader': 3.1.3 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-middleware': 3.0.3 + '@smithy/middleware-serde': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@smithy/middleware-endpoint@3.1.0': dependencies: - '@smithy/middleware-serde': 3.0.3 - '@smithy/node-config-provider': 3.1.4 + '@smithy/middleware-serde': 3.0.7 + '@smithy/node-config-provider': 3.1.8 '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-middleware': 3.0.3 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 + '@smithy/util-middleware': 3.0.7 + tslib: 2.6.3 + + '@smithy/middleware-endpoint@3.1.4': + dependencies: + '@smithy/middleware-serde': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@smithy/middleware-retry@2.3.1': @@ -11103,25 +11274,37 @@ snapshots: '@smithy/middleware-retry@3.0.15': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 '@smithy/service-error-classification': 3.0.3 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 + tslib: 2.6.3 + uuid: 9.0.1 + + '@smithy/middleware-retry@3.0.23': + dependencies: + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/service-error-classification': 3.0.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 tslib: 2.6.3 uuid: 9.0.1 '@smithy/middleware-retry@3.0.7': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/service-error-classification': 3.0.3 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/service-error-classification': 3.0.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 tslib: 2.6.3 uuid: 9.0.1 @@ -11132,7 +11315,12 @@ snapshots: '@smithy/middleware-serde@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/middleware-serde@3.0.7': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/middleware-stack@2.2.0': @@ -11142,7 +11330,12 @@ snapshots: '@smithy/middleware-stack@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/middleware-stack@3.0.7': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/node-config-provider@2.3.0': @@ -11154,16 +11347,23 @@ snapshots: '@smithy/node-config-provider@3.1.3': dependencies: - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/node-config-provider@3.1.4': dependencies: '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/node-config-provider@3.1.8': + dependencies: + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/node-http-handler@2.5.0': @@ -11176,18 +11376,26 @@ snapshots: '@smithy/node-http-handler@3.1.1': dependencies: - '@smithy/abort-controller': 3.1.1 - '@smithy/protocol-http': 4.1.0 - '@smithy/querystring-builder': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/abort-controller': 3.1.5 + '@smithy/protocol-http': 4.1.4 + '@smithy/querystring-builder': 3.0.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/node-http-handler@3.1.4': dependencies: '@smithy/abort-controller': 3.1.1 - '@smithy/protocol-http': 4.1.0 + '@smithy/protocol-http': 4.1.4 '@smithy/querystring-builder': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/node-http-handler@3.2.4': + dependencies: + '@smithy/abort-controller': 3.1.5 + '@smithy/protocol-http': 4.1.4 + '@smithy/querystring-builder': 3.0.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/property-provider@2.2.0': @@ -11197,7 +11405,12 @@ snapshots: '@smithy/property-provider@3.1.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/property-provider@3.1.7': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/protocol-http@2.0.5': @@ -11212,12 +11425,17 @@ snapshots: '@smithy/protocol-http@4.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/protocol-http@4.1.0': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/protocol-http@4.1.4': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/querystring-builder@2.2.0': @@ -11228,7 +11446,13 @@ snapshots: '@smithy/querystring-builder@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + '@smithy/util-uri-escape': 3.0.0 + tslib: 2.6.3 + + '@smithy/querystring-builder@3.0.7': + dependencies: + '@smithy/types': 3.5.0 '@smithy/util-uri-escape': 3.0.0 tslib: 2.6.3 @@ -11239,7 +11463,12 @@ snapshots: '@smithy/querystring-parser@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/querystring-parser@3.0.7': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/service-error-classification@2.1.5': @@ -11248,21 +11477,25 @@ snapshots: '@smithy/service-error-classification@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + + '@smithy/service-error-classification@3.0.7': + dependencies: + '@smithy/types': 3.5.0 '@smithy/shared-ini-file-loader@2.4.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - '@smithy/shared-ini-file-loader@3.1.3': + '@smithy/shared-ini-file-loader@3.1.4': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/shared-ini-file-loader@3.1.4': + '@smithy/shared-ini-file-loader@3.1.8': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/signature-v4@2.3.0': @@ -11278,9 +11511,9 @@ snapshots: '@smithy/signature-v4@3.1.2': dependencies: '@smithy/is-array-buffer': 3.0.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 '@smithy/util-uri-escape': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -11288,10 +11521,21 @@ snapshots: '@smithy/signature-v4@4.1.0': dependencies: '@smithy/is-array-buffer': 3.0.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-uri-escape': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.3 + + '@smithy/signature-v4@4.2.0': + dependencies: + '@smithy/is-array-buffer': 3.0.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 + '@smithy/util-hex-encoding': 3.0.0 + '@smithy/util-middleware': 3.0.7 '@smithy/util-uri-escape': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -11307,27 +11551,36 @@ snapshots: '@smithy/smithy-client@3.1.5': dependencies: - '@smithy/middleware-endpoint': 3.0.4 - '@smithy/middleware-stack': 3.0.3 - '@smithy/protocol-http': 4.0.3 - '@smithy/types': 3.3.0 - '@smithy/util-stream': 3.0.5 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-stack': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 tslib: 2.6.3 '@smithy/smithy-client@3.2.0': dependencies: - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-stack': 3.0.3 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-stack': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 '@smithy/util-stream': 3.1.3 tslib: 2.6.3 + '@smithy/smithy-client@3.4.0': + dependencies: + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-stack': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 + tslib: 2.6.3 + '@smithy/types@2.12.0': dependencies: tslib: 2.6.3 - '@smithy/types@3.3.0': + '@smithy/types@3.5.0': dependencies: tslib: 2.6.3 @@ -11340,7 +11593,13 @@ snapshots: '@smithy/url-parser@3.0.3': dependencies: '@smithy/querystring-parser': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/url-parser@3.0.7': + dependencies: + '@smithy/querystring-parser': 3.0.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-base64@2.3.0': @@ -11400,16 +11659,24 @@ snapshots: '@smithy/util-defaults-mode-browser@3.0.15': dependencies: '@smithy/property-provider': 3.1.3 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + bowser: 2.11.0 + tslib: 2.6.3 + + '@smithy/util-defaults-mode-browser@3.0.23': + dependencies: + '@smithy/property-provider': 3.1.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 bowser: 2.11.0 tslib: 2.6.3 '@smithy/util-defaults-mode-browser@3.0.7': dependencies: - '@smithy/property-provider': 3.1.3 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 bowser: 2.11.0 tslib: 2.6.3 @@ -11425,34 +11692,50 @@ snapshots: '@smithy/util-defaults-mode-node@3.0.15': dependencies: - '@smithy/config-resolver': 3.0.5 + '@smithy/config-resolver': 3.0.9 '@smithy/credential-provider-imds': 3.2.0 - '@smithy/node-config-provider': 3.1.4 + '@smithy/node-config-provider': 3.1.8 '@smithy/property-provider': 3.1.3 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/util-defaults-mode-node@3.0.23': + dependencies: + '@smithy/config-resolver': 3.0.9 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/node-config-provider': 3.1.8 + '@smithy/property-provider': 3.1.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-defaults-mode-node@3.0.7': dependencies: - '@smithy/config-resolver': 3.0.5 - '@smithy/credential-provider-imds': 3.1.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/config-resolver': 3.0.9 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/node-config-provider': 3.1.8 + '@smithy/property-provider': 3.1.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-endpoints@2.0.4': dependencies: - '@smithy/node-config-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-endpoints@2.0.5': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/util-endpoints@2.1.3': + dependencies: + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-hex-encoding@2.2.0': @@ -11470,7 +11753,12 @@ snapshots: '@smithy/util-middleware@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/util-middleware@3.0.7': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-retry@2.2.0': @@ -11482,7 +11770,13 @@ snapshots: '@smithy/util-retry@3.0.3': dependencies: '@smithy/service-error-classification': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/util-retry@3.0.7': + dependencies: + '@smithy/service-error-classification': 3.0.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-stream@2.2.0': @@ -11496,22 +11790,22 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - '@smithy/util-stream@3.0.5': + '@smithy/util-stream@3.1.3': dependencies: - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.4 + '@smithy/types': 3.5.0 '@smithy/util-base64': 3.0.0 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-hex-encoding': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/util-stream@3.1.3': + '@smithy/util-stream@3.1.9': dependencies: - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.4 + '@smithy/types': 3.5.0 '@smithy/util-base64': 3.0.0 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-hex-encoding': 3.0.0 @@ -11544,8 +11838,8 @@ snapshots: '@smithy/util-waiter@3.1.2': dependencies: - '@smithy/abort-controller': 3.1.1 - '@smithy/types': 3.3.0 + '@smithy/abort-controller': 3.1.5 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@testcontainers/postgresql@10.9.0': @@ -12223,7 +12517,7 @@ snapshots: get-func-name: 2.0.2 loupe: 2.3.7 pathval: 1.1.1 - type-detect: 4.0.8 + type-detect: 4.1.0 chalk@2.4.2: dependencies: From 495e5c6ae2d2606b347839c0a389032475f256a6 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Wed, 13 Nov 2024 16:23:07 +0100 Subject: [PATCH 28/34] PIN-5659 - Added `DELETE /tenants/delegatedProducer` in bff and tenant process (#1189) --- .../tenant/Add delegated producer feature.bru | 16 +++ .../Remove delegated producer feature.bru | 16 +++ packages/api-clients/open-api/bffApi.yml | 59 ++++++++++ packages/api-clients/open-api/tenantApi.yml | 22 ++++ .../src/routers/tenantRouter.ts | 18 +++ .../src/services/tenantService.ts | 10 ++ .../compute-agreements-consumer/src/index.ts | 3 +- packages/models/proto/v2/tenant/events.proto | 4 + packages/models/src/tenant/tenantEvents.ts | 9 ++ .../src/converters/toOutboundEventV2.ts | 1 + .../tenant-process/src/model/domain/errors.ts | 11 ++ .../src/model/domain/toEvent.ts | 17 +++ .../src/routers/TenantRouter.ts | 25 ++++ .../src/services/tenantService.ts | 42 +++++++ .../tenant-process/src/services/validators.ts | 7 ++ .../src/utilities/errorMappers.ts | 10 ++ ...moveTenantDelegatedProducerFeature.test.ts | 111 ++++++++++++++++++ .../src/tenantConsumerServiceV2.ts | 1 + 18 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 collections/tenant/Add delegated producer feature.bru create mode 100644 collections/tenant/Remove delegated producer feature.bru create mode 100644 packages/tenant-process/test/removeTenantDelegatedProducerFeature.test.ts diff --git a/collections/tenant/Add delegated producer feature.bru b/collections/tenant/Add delegated producer feature.bru new file mode 100644 index 0000000000..d7790c4887 --- /dev/null +++ b/collections/tenant/Add delegated producer feature.bru @@ -0,0 +1,16 @@ +meta { + name: Add delegated producer feature from tenant caller + type: http + seq: 12 +} + +post { + url: {{host-tenant}}/tenants/delegatedProducer + body: json + auth: none +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/tenant/Remove delegated producer feature.bru b/collections/tenant/Remove delegated producer feature.bru new file mode 100644 index 0000000000..bada9133ea --- /dev/null +++ b/collections/tenant/Remove delegated producer feature.bru @@ -0,0 +1,16 @@ +meta { + name: Remove delegated producer feature from tenant caller + type: http + seq: 12 +} + +delete { + url: {{host-tenant}}/tenants/delegatedProducer + body: json + auth: none +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index 2c9ab3e0e7..9542323821 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -8382,6 +8382,65 @@ paths: schema: type: integer description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + delete: + tags: + - tenants + summary: Delete delegated producer feature to tenant caller + operationId: deleteTenantDelegatedProducerFeature + responses: + "204": + description: Delegated producer feature deleted + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "409": + description: Feature not assigned to tenant + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available /clients: parameters: - $ref: "#/components/parameters/CorrelationIdHeader" diff --git a/packages/api-clients/open-api/tenantApi.yml b/packages/api-clients/open-api/tenantApi.yml index 5cf569ea6d..b82f6cf08c 100644 --- a/packages/api-clients/open-api/tenantApi.yml +++ b/packages/api-clients/open-api/tenantApi.yml @@ -996,6 +996,8 @@ paths: schema: $ref: "#/components/schemas/Problem" /tenants/delegatedProducer: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" post: tags: - tenant @@ -1016,6 +1018,26 @@ paths: application/json: schema: $ref: "#/components/schemas/Problem" + delete: + tags: + - tenant + summary: Remove delegated producer feature to tenant caller + operationId: removeTenantDelegatedProducerFeature + responses: + "204": + description: Delegated producer feature removed + "409": + description: Feature not assigned to tenant + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" /status: get: security: [] diff --git a/packages/backend-for-frontend/src/routers/tenantRouter.ts b/packages/backend-for-frontend/src/routers/tenantRouter.ts index ef647d3b7f..5f93f610e5 100644 --- a/packages/backend-for-frontend/src/routers/tenantRouter.ts +++ b/packages/backend-for-frontend/src/routers/tenantRouter.ts @@ -423,6 +423,24 @@ const tenantRouter = ( ); return res.status(errorRes.status).send(errorRes); } + }) + .delete("/tenants/delegatedProducer", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + const tenantId = ctx.authData.organizationId; + + try { + await tenantService.removeTenantDelegatedProducerFeature(tenantId, ctx); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error while removing delegated producer feature to ${tenantId}` + ); + return res.status(errorRes.status).send(errorRes); + } }); return tenantRouter; diff --git a/packages/backend-for-frontend/src/services/tenantService.ts b/packages/backend-for-frontend/src/services/tenantService.ts index a545f32ef9..82dac3d5f6 100644 --- a/packages/backend-for-frontend/src/services/tenantService.ts +++ b/packages/backend-for-frontend/src/services/tenantService.ts @@ -428,6 +428,16 @@ export function tenantServiceBuilder( { headers } ); }, + async removeTenantDelegatedProducerFeature( + tenantId: TenantId, + { logger, headers }: WithLogger + ): Promise { + logger.info(`Removing delegated producer feature to tenant ${tenantId}`); + await tenantProcessClient.tenant.removeTenantDelegatedProducerFeature( + undefined, + { headers } + ); + }, }; } diff --git a/packages/compute-agreements-consumer/src/index.ts b/packages/compute-agreements-consumer/src/index.ts index 116ddc08b9..886b17f248 100644 --- a/packages/compute-agreements-consumer/src/index.ts +++ b/packages/compute-agreements-consumer/src/index.ts @@ -96,7 +96,8 @@ async function processMessage({ "TenantMailDeleted", "TenantMailAdded", "MaintenanceTenantPromotedToCertifier", - "TenantDelegatedProducerFeatureAdded" + "TenantDelegatedProducerFeatureAdded", + "TenantDelegatedProducerFeatureRemoved" ), }, () => Promise.resolve() diff --git a/packages/models/proto/v2/tenant/events.proto b/packages/models/proto/v2/tenant/events.proto index 70fb333ef8..55a2b71317 100644 --- a/packages/models/proto/v2/tenant/events.proto +++ b/packages/models/proto/v2/tenant/events.proto @@ -83,3 +83,7 @@ message MaintenanceTenantPromotedToCertifierV2{ message TenantDelegatedProducerFeatureAddedV2 { TenantV2 tenant = 1; } + +message TenantDelegatedProducerFeatureRemovedV2 { + TenantV2 tenant = 1; +} diff --git a/packages/models/src/tenant/tenantEvents.ts b/packages/models/src/tenant/tenantEvents.ts index 14321eb4d4..cf30f12a6d 100644 --- a/packages/models/src/tenant/tenantEvents.ts +++ b/packages/models/src/tenant/tenantEvents.ts @@ -26,6 +26,7 @@ import { TenantMailDeletedV2, TenantKindUpdatedV2, TenantDelegatedProducerFeatureAddedV2, + TenantDelegatedProducerFeatureRemovedV2, MaintenanceTenantUpdatedV2, } from "../gen/v2/tenant/events.js"; import { protobufDecoder } from "../protobuf/protobuf.js"; @@ -117,6 +118,9 @@ export function tenantEventToBinaryDataV2(event: TenantEventV2): Uint8Array { .with({ type: "TenantDelegatedProducerFeatureAdded" }, ({ data }) => TenantDelegatedProducerFeatureAddedV2.toBinary(data) ) + .with({ type: "TenantDelegatedProducerFeatureRemoved" }, ({ data }) => + TenantDelegatedProducerFeatureRemovedV2.toBinary(data) + ) .exhaustive(); } @@ -246,6 +250,11 @@ export const TenantEventV2 = z.discriminatedUnion("type", [ type: z.literal("TenantDelegatedProducerFeatureAdded"), data: protobufDecoder(TenantDelegatedProducerFeatureAddedV2), }), + z.object({ + event_version: z.literal(2), + type: z.literal("TenantDelegatedProducerFeatureRemoved"), + data: protobufDecoder(TenantDelegatedProducerFeatureRemovedV2), + }), ]); export type TenantEventV2 = z.infer; diff --git a/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts b/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts index 4ba155f4d4..cb9eeb2b6d 100644 --- a/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts +++ b/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts @@ -47,6 +47,7 @@ export function toOutboundEventV2( { type: "MaintenanceTenantPromotedToCertifier" }, { type: "MaintenanceTenantUpdated" }, { type: "TenantDelegatedProducerFeatureAdded" }, + { type: "TenantDelegatedProducerFeatureRemoved" }, (msg) => ({ event_version: msg.event_version, type: msg.type, diff --git a/packages/tenant-process/src/model/domain/errors.ts b/packages/tenant-process/src/model/domain/errors.ts index f150139a78..1f07279c8c 100644 --- a/packages/tenant-process/src/model/domain/errors.ts +++ b/packages/tenant-process/src/model/domain/errors.ts @@ -34,6 +34,7 @@ export const errorCodes = { attributeNotFoundInTenant: "0025", tenantNotFoundByExternalId: "0026", tenantAlreadyHasDelegatedProducerFeature: "0027", + tenantHasNoDelegatedProducerFeature: "0028", }; export type ErrorCodes = keyof typeof errorCodes; @@ -303,3 +304,13 @@ export function tenantAlreadyHasDelegatedProducerFeature( title: "Feature already assigned", }); } + +export function tenantHasNoDelegatedProducerFeature( + tenantId: TenantId +): ApiError { + return new ApiError({ + detail: `Tenant ${tenantId} has no delegated producer feature assigned`, + code: "tenantHasNoDelegatedProducerFeature", + title: "Feature not assigned", + }); +} diff --git a/packages/tenant-process/src/model/domain/toEvent.ts b/packages/tenant-process/src/model/domain/toEvent.ts index 5b65157807..6d889c6265 100644 --- a/packages/tenant-process/src/model/domain/toEvent.ts +++ b/packages/tenant-process/src/model/domain/toEvent.ts @@ -319,3 +319,20 @@ export const toCreateEventTenantDelegatedProducerFeatureAdded = ( }, correlationId, }); + +export const toCreateEventTenantDelegatedProducerFeatureRemoved = ( + version: number, + updatedTenant: Tenant, + correlationId: CorrelationId +): CreateEvent => ({ + streamId: updatedTenant.id, + version, + event: { + type: "TenantDelegatedProducerFeatureRemoved", + event_version: 2, + data: { + tenant: toTenantV2(updatedTenant), + }, + }, + correlationId, +}); diff --git a/packages/tenant-process/src/routers/TenantRouter.ts b/packages/tenant-process/src/routers/TenantRouter.ts index 4300013729..2416c5457d 100644 --- a/packages/tenant-process/src/routers/TenantRouter.ts +++ b/packages/tenant-process/src/routers/TenantRouter.ts @@ -39,6 +39,7 @@ import { m2mUpsertTenantErrorMapper, maintenanceTenantUpdatedErrorMapper, assignTenantDelegatedProducerFeatureErrorMapper, + removeTenantDelegatedProducerFeatureErrorMapper, } from "../utilities/errorMappers.js"; import { readModelServiceBuilder } from "../services/readModelService.js"; import { config } from "../config/config.js"; @@ -521,6 +522,30 @@ const tenantsRouter = ( return res.status(errorRes.status).send(errorRes); } } + ) + .delete( + "/tenants/delegatedProducer", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + try { + await tenantService.removeTenantDelegatedProducerFeature({ + organizationId: req.ctx.authData.organizationId, + correlationId: req.ctx.correlationId, + authData: ctx.authData, + logger: ctx.logger, + }); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + removeTenantDelegatedProducerFeatureErrorMapper, + ctx.logger, + ctx.correlationId + ); + return res.status(errorRes.status).send(errorRes); + } + } ); const m2mRouter = ctx.router(tenantApi.m2mApi.api, { diff --git a/packages/tenant-process/src/services/tenantService.ts b/packages/tenant-process/src/services/tenantService.ts index 1c4cd1444c..0b1181838c 100644 --- a/packages/tenant-process/src/services/tenantService.ts +++ b/packages/tenant-process/src/services/tenantService.ts @@ -53,6 +53,7 @@ import { toCreateEventTenantVerifiedAttributeRevoked, toCreateEventMaintenanceTenantUpdated, toCreateEventTenantDelegatedProducerFeatureAdded, + toCreateEventTenantDelegatedProducerFeatureRemoved, } from "../model/domain/toEvent.js"; import { attributeAlreadyRevoked, @@ -87,6 +88,7 @@ import { assertRequesterIPAOrigin, assertDelegatedProducerFeatureNotAssigned, getTenantKind, + assertDelegatedProducerFeatureAssigned, } from "./validators.js"; import { ReadModelService } from "./readModelService.js"; @@ -1669,6 +1671,46 @@ export function tenantServiceBuilder( ) ); }, + async removeTenantDelegatedProducerFeature({ + organizationId, + correlationId, + authData, + logger, + }: { + organizationId: TenantId; + correlationId: CorrelationId; + authData: AuthData; + logger: Logger; + }): Promise { + logger.info( + `Removing delegated producer feature to tenant ${organizationId}` + ); + + assertRequesterIPAOrigin(authData); + + const requesterTenant = await retrieveTenant( + organizationId, + readModelService + ); + + assertDelegatedProducerFeatureAssigned(requesterTenant.data); + + const updatedTenant: Tenant = { + ...requesterTenant.data, + features: requesterTenant.data.features.filter( + (f) => f.type !== "DelegatedProducer" + ), + updatedAt: new Date(), + }; + + await repository.createEvent( + toCreateEventTenantDelegatedProducerFeatureRemoved( + requesterTenant.metadata.version, + updatedTenant, + correlationId + ) + ); + }, }; } diff --git a/packages/tenant-process/src/services/validators.ts b/packages/tenant-process/src/services/validators.ts index d99263f4da..706d86e79f 100644 --- a/packages/tenant-process/src/services/validators.ts +++ b/packages/tenant-process/src/services/validators.ts @@ -32,6 +32,7 @@ import { verifiedAttributeSelfVerificationNotAllowed, attributeNotFound, tenantAlreadyHasDelegatedProducerFeature, + tenantHasNoDelegatedProducerFeature, } from "../model/domain/errors.js"; import { ReadModelService } from "./readModelService.js"; @@ -263,3 +264,9 @@ export function assertDelegatedProducerFeatureNotAssigned( throw tenantAlreadyHasDelegatedProducerFeature(tenant.id); } } + +export function assertDelegatedProducerFeatureAssigned(tenant: Tenant): void { + if (!tenant.features.some((f) => f.type === "DelegatedProducer")) { + throw tenantHasNoDelegatedProducerFeature(tenant.id); + } +} diff --git a/packages/tenant-process/src/utilities/errorMappers.ts b/packages/tenant-process/src/utilities/errorMappers.ts index fc44f5b714..f34e04ec9e 100644 --- a/packages/tenant-process/src/utilities/errorMappers.ts +++ b/packages/tenant-process/src/utilities/errorMappers.ts @@ -241,8 +241,18 @@ export const assignTenantDelegatedProducerFeatureErrorMapper = ( error: ApiError ): number => match(error.code) + .with("operationForbidden", () => HTTP_STATUS_FORBIDDEN) .with( "tenantAlreadyHasDelegatedProducerFeature", () => HTTP_STATUS_CONFLICT ) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const removeTenantDelegatedProducerFeatureErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("operationForbidden", () => HTTP_STATUS_FORBIDDEN) + .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("tenantHasNoDelegatedProducerFeature", () => HTTP_STATUS_CONFLICT) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/tenant-process/test/removeTenantDelegatedProducerFeature.test.ts b/packages/tenant-process/test/removeTenantDelegatedProducerFeature.test.ts new file mode 100644 index 0000000000..6f90ebaded --- /dev/null +++ b/packages/tenant-process/test/removeTenantDelegatedProducerFeature.test.ts @@ -0,0 +1,111 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { + generateId, + Tenant, + protobufDecoder, + toTenantV2, + TenantDelegatedProducerFeatureRemovedV2, + operationForbidden, +} from "pagopa-interop-models"; +import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; +import { readLastEventByStreamId } from "pagopa-interop-commons-test/dist/eventStoreTestUtils.js"; +import { getMockAuthData, getMockTenant } from "pagopa-interop-commons-test"; +import { tenantHasNoDelegatedProducerFeature } from "../src/model/domain/errors.js"; +import { addOneTenant, postgresDB, tenantService } from "./utils.js"; + +describe("removeTenantDelegatedProducerFeature", async () => { + beforeAll(async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + }); + + afterAll(() => { + vi.useRealTimers(); + }); + + it("Should correctly remove the feature", async () => { + const mockTenant: Tenant = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer", + availabilityTimestamp: new Date(), + }, + ], + }; + await addOneTenant(mockTenant); + await tenantService.removeTenantDelegatedProducerFeature({ + organizationId: mockTenant.id, + correlationId: generateId(), + authData: getMockAuthData(), + logger: genericLogger, + }); + const writtenEvent = await readLastEventByStreamId( + mockTenant.id, + "tenant", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockTenant.id, + version: "1", + type: "TenantDelegatedProducerFeatureRemoved", + event_version: 2, + }); + + const writtenPayload: TenantDelegatedProducerFeatureRemovedV2 | undefined = + protobufDecoder(TenantDelegatedProducerFeatureRemovedV2).parse( + writtenEvent.data + ); + + const updatedTenant: Tenant = { + ...mockTenant, + features: [], + updatedAt: new Date(), + }; + expect(writtenPayload.tenant).toEqual(toTenantV2(updatedTenant)); + }); + + it("Should throw tenantHasNoDelegatedProducerFeature if the requester tenant already has the delegated producer feature", async () => { + const tenant: Tenant = { + ...getMockTenant(), + features: [], + }; + + await addOneTenant(tenant); + + expect( + tenantService.removeTenantDelegatedProducerFeature({ + organizationId: tenant.id, + correlationId: generateId(), + authData: getMockAuthData(), + logger: genericLogger, + }) + ).rejects.toThrowError(tenantHasNoDelegatedProducerFeature(tenant.id)); + }); + it("Should throw operationForbidden if the requester tenant is not a public administration", async () => { + const tenant: Tenant = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer", + availabilityTimestamp: new Date(), + }, + ], + }; + await addOneTenant(tenant); + + expect( + tenantService.removeTenantDelegatedProducerFeature({ + organizationId: tenant.id, + correlationId: generateId(), + authData: { + ...getMockAuthData(), + externalId: { origin: "UNKNOWN", value: "test" }, + }, + logger: genericLogger, + }) + ).rejects.toThrowError(operationForbidden); + }); +}); diff --git a/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts b/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts index e5637e7943..85acd740db 100644 --- a/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts +++ b/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts @@ -36,6 +36,7 @@ export async function handleMessageV2( { type: "TenantMailDeleted" }, { type: "TenantKindUpdated" }, { type: "TenantDelegatedProducerFeatureAdded" }, + { type: "TenantDelegatedProducerFeatureRemoved" }, async (message) => await tenants.updateOne( { From 303e6a2af806202075eb460ef3ea482ac671740f Mon Sep 17 00:00:00 2001 From: Michele De Simone Date: Mon, 18 Nov 2024 15:05:23 +0100 Subject: [PATCH 29/34] feat(workflows): adapt build&push for GitFlow --- .github/workflows/build-push.yaml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-push.yaml b/.github/workflows/build-push.yaml index 4909509cc3..be359746bd 100644 --- a/.github/workflows/build-push.yaml +++ b/.github/workflows/build-push.yaml @@ -3,7 +3,8 @@ name: "Build & Push" on: push: branches: - - "main" + - "develop" + - "hotfix/*" tags: - "*" paths: @@ -55,8 +56,16 @@ jobs: id: login-ecr uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2 - - name: (latest) Docker metadata - id: meta_latest + - name: Normalize ref name + id: norm_ref + run: | + set -euo pipefail + + NORM_REF="$(echo ${{ github.ref_name }} | sed -e 's/\//-/g')" + echo "NORM_REF=$NORM_REF" >> $GITHUB_ENV + + - name: (branch) Docker metadata + id: meta_branch if: ${{ github.ref_type == 'branch' }} uses: docker/metadata-action@60a0d343a0d8a18aedee9d34e62251f752153bdb with: @@ -66,7 +75,7 @@ jobs: prefix= suffix= tags: | - type=raw,value=2.x-latest + type=raw,value=${{ env.NORM_REF }} - name: (tag) Docker metadata id: meta_tag @@ -88,5 +97,5 @@ jobs: context: . file: packages/${{ matrix.package }}/Dockerfile push: true - tags: ${{ github.ref_type == 'branch' && steps.meta_latest.outputs.tags || steps.meta_tag.outputs.tags }} - labels: ${{ github.ref_type == 'branch' && steps.meta_latest.outputs.labels || steps.meta_tag.outputs.labels }} + tags: ${{ github.ref_type == 'branch' && steps.meta_branch.outputs.tags || steps.meta_tag.outputs.tags }} + labels: ${{ github.ref_type == 'branch' && steps.meta_branch.outputs.labels || steps.meta_tag.outputs.labels }} From 1d1987f11c82a1f9813c2b0ef44849a3fc5f8256 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 18 Nov 2024 18:23:17 +0100 Subject: [PATCH 30/34] IMN-521 Authorization-server (#1134) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- docker/docker-compose.yml | 2 +- package.json | 1 + .../open-api/authorizationServerApi.yml | 215 +++++ packages/api-clients/src/index.ts | 1 + .../test/sample.integration.test.ts | 46 - packages/authorization-server/.env | 40 + packages/authorization-server/Dockerfile | 51 ++ .../authorization-server/aws.config.local | 12 + packages/authorization-server/package.json | 53 ++ packages/authorization-server/src/app.ts | 24 + .../authorization-server/src/config/config.ts | 43 + packages/authorization-server/src/index.ts | 7 + .../src/model/domain/errors.ts | 124 +++ .../src/model/domain/models.ts | 5 + .../src/routers/AuthorizationServerRouter.ts | 104 +++ .../src/routers/HealthRouter.ts | 8 + .../src/services/tokenService.ts | 412 +++++++++ .../src/utilities/errorMappers.ts | 23 + .../authorization-server/test/.eslintrc.json | 7 + .../authorizationServer.integration.test.ts | 808 ++++++++++++++++++ .../test/authorizationServer.unit.test.ts | 272 ++++++ .../authorization-server/test/tsconfig.json | 4 + packages/authorization-server/test/utils.ts | 127 +++ .../test/vitestGlobalSetup.ts | 3 + .../authorization-server/tsconfig.check.json | 7 + packages/authorization-server/tsconfig.json | 7 + .../authorization-server/vitest.config.ts | 11 + packages/backend-for-frontend/.env | 2 +- .../src/services/toolService.ts | 86 +- .../client-assertion-validation/src/errors.ts | 88 +- .../client-assertion-validation/src/types.ts | 76 +- .../client-assertion-validation/src/utils.ts | 8 +- .../src/validation.ts | 50 +- .../client-assertion-validation/test/utils.ts | 131 +-- .../test/validation.test.ts | 197 +++-- packages/commons-test/package.json | 1 + packages/commons-test/src/testUtils.ts | 118 ++- .../src/tokenGenerationReadmodelUtils.ts | 36 + ...uthorizationServerTokenGenerationConfig.ts | 22 + packages/commons/src/config/index.ts | 1 + .../src/interop-token/interopTokenService.ts | 175 +++- packages/commons/src/interop-token/models.ts | 28 + packages/commons/src/utils/date.ts | 12 + packages/models/src/brandedIds.ts | 4 +- .../clientAssertionValidation.ts | 41 + packages/models/src/index.ts | 2 + .../src/token-generation-audit/audit.ts | 48 ++ .../token-generation-states-entry.ts | 6 + pnpm-lock.yaml | 137 ++- 49 files changed, 3229 insertions(+), 457 deletions(-) create mode 100644 packages/api-clients/open-api/authorizationServerApi.yml delete mode 100644 packages/authorization-platformstate-writer/test/sample.integration.test.ts create mode 100644 packages/authorization-server/.env create mode 100644 packages/authorization-server/Dockerfile create mode 100644 packages/authorization-server/aws.config.local create mode 100644 packages/authorization-server/package.json create mode 100644 packages/authorization-server/src/app.ts create mode 100644 packages/authorization-server/src/config/config.ts create mode 100644 packages/authorization-server/src/index.ts create mode 100644 packages/authorization-server/src/model/domain/errors.ts create mode 100644 packages/authorization-server/src/model/domain/models.ts create mode 100644 packages/authorization-server/src/routers/AuthorizationServerRouter.ts create mode 100644 packages/authorization-server/src/routers/HealthRouter.ts create mode 100644 packages/authorization-server/src/services/tokenService.ts create mode 100644 packages/authorization-server/src/utilities/errorMappers.ts create mode 100644 packages/authorization-server/test/.eslintrc.json create mode 100644 packages/authorization-server/test/authorizationServer.integration.test.ts create mode 100644 packages/authorization-server/test/authorizationServer.unit.test.ts create mode 100644 packages/authorization-server/test/tsconfig.json create mode 100644 packages/authorization-server/test/utils.ts create mode 100644 packages/authorization-server/test/vitestGlobalSetup.ts create mode 100644 packages/authorization-server/tsconfig.check.json create mode 100644 packages/authorization-server/tsconfig.json create mode 100644 packages/authorization-server/vitest.config.ts create mode 100644 packages/commons/src/config/authorizationServerTokenGenerationConfig.ts create mode 100644 packages/models/src/client-assertion/clientAssertionValidation.ts create mode 100644 packages/models/src/token-generation-audit/audit.ts diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 1d08bc9c6e..c4f5c4a841 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -78,7 +78,7 @@ services: - dynamodb-local restart: always ports: - - "8002:8002" + - "8002:8001" environment: - DYNAMO_ENDPOINT=http://dynamodb-local:8000 - AWS_REGION=eu-south-1 diff --git a/package.json b/package.json index 9abb632b87..e7ac4acb3b 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "start:delegation": "turbo start --filter pagopa-interop-delegation-process", "start:delegation-readmodel-writer": "turbo start --filter pagopa-interop-delegation-readmodel-writer", "start:datalake-data-export": "turbo start --filter pagopa-interop-datalake-data-export", + "start:authorization-server": "turbo start --filter pagopa-interop-authorization-server", "test": "turbo test", "build": "turbo build", "check": "turbo check", diff --git a/packages/api-clients/open-api/authorizationServerApi.yml b/packages/api-clients/open-api/authorizationServerApi.yml new file mode 100644 index 0000000000..3fcbf37b1e --- /dev/null +++ b/packages/api-clients/open-api/authorizationServerApi.yml @@ -0,0 +1,215 @@ +openapi: 3.0.3 +info: + title: Interoperability Authorization Server Micro Service + description: Provides endpoints to request an interoperability token + version: "0.1.0" + contact: + name: API Support + url: "http://www.example.com/support" + email: support@example.com + termsOfService: "http://swagger.io/terms/" + x-api-id: an x-api-id + x-summary: an x-summary +servers: + - url: "/authorization-server" + description: Interoperability Authorization Server +tags: + - name: auth + description: Get security information + externalDocs: + description: Find out more + url: http://swagger.io + - name: health + description: Verify service status + externalDocs: + description: Find out more + url: http://swagger.io +paths: + "/token.oauth2": + post: + tags: + - auth + summary: Create a new access token + description: Return the generated access token + operationId: createToken + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/AccessTokenRequest" + responses: + "200": + description: The Access token + headers: + Cache-Control: + schema: + type: string + default: no-cache, no-store + description: no-cache, no-store + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + content: + application/json: + schema: + $ref: "#/components/schemas/ClientCredentialsResponse" + "400": + description: Bad request + x-noqa: RFC6749 + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "401": + description: Unauthorized + x-noqa: RFC6749 + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "429": + description: Too Many Requests + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + /status: + get: + security: [] + summary: Returns the application status + description: Returns the application status + operationId: get_status + tags: + - health + responses: + "200": + description: This is the valid status from the server. + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" +components: + schemas: + AccessTokenRequest: + type: object + required: + - client_assertion + - client_assertion_type + - grant_type + properties: + client_id: + type: string + example: e58035ce-c753-4f72-b613-46f8a17b71cc + client_assertion: + type: string + format: jws + client_assertion_type: + type: string + example: urn:ietf:params:oauth:client-assertion-type:jwt-bearer + grant_type: + type: string + enum: + - client_credentials + TokenType: + type: string + description: Represents the token type + enum: + - Bearer + ClientCredentialsResponse: + type: object + required: + - access_token + - token_type + - expires_in + properties: + access_token: + type: string + format: jws + token_type: + $ref: "#/components/schemas/TokenType" + expires_in: + type: integer + format: int32 + maximum: 600 + Problem: + properties: + type: + description: URI reference of type definition + type: string + status: + description: The HTTP status code generated by the origin server for this occurrence of the problem. + example: 400 + exclusiveMaximum: true + format: int32 + maximum: 600 + minimum: 100 + type: integer + title: + description: A short, summary of the problem type. Written in english and readable + example: Service Unavailable + maxLength: 64 + pattern: "^[ -~]{0,64}$" + type: string + correlationId: + description: Unique identifier of the request + example: "53af4f2d-0c87-41ef-a645-b726a821852b" + maxLength: 64 + type: string + detail: + description: A human readable explanation of the problem. + example: Request took too long to complete. + maxLength: 4096 + pattern: "^.{0,1024}$" + type: string + errors: + type: array + minItems: 0 + items: + $ref: "#/components/schemas/ProblemError" + additionalProperties: false + required: + - type + - status + - title + - errors + ProblemError: + properties: + code: + description: Internal code of the error + example: 123-4567 + minLength: 8 + maxLength: 8 + pattern: "^[0-9]{3}-[0-9]{4}$" + type: string + detail: + description: A human readable explanation specific to this occurrence of the problem. + example: Parameter not valid + maxLength: 4096 + pattern: "^.{0,1024}$" + type: string + required: + - code + - detail diff --git a/packages/api-clients/src/index.ts b/packages/api-clients/src/index.ts index 2a5174bbba..cc32f1e16f 100644 --- a/packages/api-clients/src/index.ts +++ b/packages/api-clients/src/index.ts @@ -11,3 +11,4 @@ export * as apiGatewayApi from "./apiGatewayApi.js"; export * as notifierApi from "./generated/notifierApi.js"; export * as delegationApi from "./generated/delegationApi.js"; export * from "./selfcareClients.js"; +export * as authorizationServerApi from "./generated/authorizationServerApi.js"; diff --git a/packages/authorization-platformstate-writer/test/sample.integration.test.ts b/packages/authorization-platformstate-writer/test/sample.integration.test.ts deleted file mode 100644 index a4e96fa400..0000000000 --- a/packages/authorization-platformstate-writer/test/sample.integration.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -/* eslint-disable @typescript-eslint/no-floating-promises */ -import { fail } from "assert"; -import { - afterAll, - afterEach, - beforeAll, - beforeEach, - describe, - expect, - it, - vi, -} from "vitest"; -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { - buildDynamoDBTables, - deleteDynamoDBTables, -} from "pagopa-interop-commons-test"; -import { config } from "./utils.js"; - -describe("integration tests V2 events", async () => { - if (!config) { - fail(); - } - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); - beforeEach(async () => { - await buildDynamoDBTables(dynamoDBClient); - }); - afterEach(async () => { - await deleteDynamoDBTables(dynamoDBClient); - }); - const mockDate = new Date(); - beforeAll(() => { - vi.useFakeTimers(); - vi.setSystemTime(mockDate); - }); - afterAll(() => { - vi.useRealTimers(); - }); - - it("sample", () => { - expect(1).toBe(1); - }); -}); diff --git a/packages/authorization-server/.env b/packages/authorization-server/.env new file mode 100644 index 0000000000..eec5e7bf87 --- /dev/null +++ b/packages/authorization-server/.env @@ -0,0 +1,40 @@ +HOST=0.0.0.0 +PORT=3300 +LOG_LEVEL=info + +AWS_CONFIG_FILE=aws.config.local +TOKEN_GENERATION_READMODEL_TABLE_NAME_PLATFORM="platform-states" +TOKEN_GENERATION_READMODEL_TABLE_NAME_TOKEN_GENERATION="token-generation-states" + +AWS_REGION="eu-south-1" + +CLIENT_ASSERTION_AUDIENCE="test.interop.pagopa.it" + +GENERATED_INTEROP_TOKEN_ALGORITHM="RS256" +GENERATED_INTEROP_TOKEN_KID="ffcc9b5b-4612-49b1-9374-9d203a3834f2" +GENERATED_INTEROP_TOKEN_ISSUER="test" +GENERATED_INTEROP_TOKEN_M2M_AUDIENCE="test.interop.pagopa.it" +GENERATED_INTEROP_TOKEN_M2M_DURATION_SECONDS=60 +TOKEN_AUDITING_TOPIC="authorization-server.generated-jwt" + +RATE_LIMITER_BURST_PERCENTAGE="0" +RATE_LIMITER_MAX_REQUESTS="10" +RATE_LIMITER_RATE_INTERVAL_MILLIS="1000" +RATE_LIMITER_REDIS_HOST="localhost" +RATE_LIMITER_REDIS_PORT="6379" +RATE_LIMITER_TIMEOUT_MILLIS="300" + +PRODUCER_KAFKA_CLIENT_ID="authorization-server" +PRODUCER_KAFKA_BROKERS="localhost:9092" +PRODUCER_KAFKA_DISABLE_AWS_IAM_AUTH="true" + +S3_BUCKET=interop-local-bucket +S3_CUSTOM_SERVER=true +S3_SERVER_HOST=http://localhost +S3_SERVER_PORT=9000 + +KAFKA_CLIENT_ID="authorization-server" +KAFKA_GROUP_ID="authorization-server-group" +KAFKA_BROKERS="localhost:9092" +KAFKA_DISABLE_AWS_IAM_AUTH="true" + diff --git a/packages/authorization-server/Dockerfile b/packages/authorization-server/Dockerfile new file mode 100644 index 0000000000..6c1c4d89ea --- /dev/null +++ b/packages/authorization-server/Dockerfile @@ -0,0 +1,51 @@ +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ + +COPY ./packages/authorization-server/package.json /app/packages/authorization-server/package.json +COPY ./packages/commons/package.json /app/packages/commons/package.json +COPY ./packages/models/package.json /app/packages/models/package.json +COPY ./packages/client-assertion-validation/package.json /app/packages/client-assertion-validation/package.json +COPY ./packages/kafka-iam-auth/package.json /app/packages/kafka-iam-auth/package.json +COPY ./packages/api-clients/package.json /app/packages/api-clients/package.json + +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile + +COPY tsconfig.json /app/ +COPY turbo.json /app/ +COPY ./packages/authorization-server /app/packages/authorization-server +COPY ./packages/commons /app/packages/commons +COPY ./packages/models /app/packages/models +COPY ./packages/client-assertion-validation /app/packages/client-assertion-validation +COPY ./packages/kafka-iam-auth /app/packages/kafka-iam-auth +COPY ./packages/api-clients /app/packages/api-clients + +RUN pnpm build && \ + rm -rf /app/node_modules/.modules.yaml && \ + rm -rf /app/node_modules/.cache && \ + mkdir /out && \ + cp -a --parents -t /out \ + node_modules packages/authorization-server/node_modules \ + package*.json packages/authorization-server/package*.json \ + packages/commons/ \ + packages/models/ \ + packages/client-assertion-validation/ \ + packages/kafka-iam-auth/ \ + packages/api-clients \ + packages/authorization-server/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final + +COPY --from=build /out /app + +WORKDIR /app/packages/authorization-server +EXPOSE 3300 + +CMD [ "node", "." ] diff --git a/packages/authorization-server/aws.config.local b/packages/authorization-server/aws.config.local new file mode 100644 index 0000000000..042982b52d --- /dev/null +++ b/packages/authorization-server/aws.config.local @@ -0,0 +1,12 @@ +[default] +aws_access_key_id=testawskey +aws_secret_access_key=testawssecret +region=eu-south-1 +services=local + +[services local] +dynamodb= + endpoint_url=http://localhost:8085 + +kms= + endpoint_url=http://localhost:4566 diff --git a/packages/authorization-server/package.json b/packages/authorization-server/package.json new file mode 100644 index 0000000000..0fd0589788 --- /dev/null +++ b/packages/authorization-server/package.json @@ -0,0 +1,53 @@ +{ + "name": "pagopa-interop-authorization-server", + "version": "1.0.0", + "description": "PagoPA Interoperability service for authorization", + "main": "dist", + "type": "module", + "scripts": { + "test": "vitest", + "lint": "eslint . --ext .ts,.tsx", + "lint:autofix": "eslint . --ext .ts,.tsx --fix", + "format:check": "prettier --check src", + "format:write": "prettier --write src", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@pagopa/eslint-config": "3.0.0", + "@protobuf-ts/runtime": "2.9.4", + "@types/express": "4.17.21", + "@types/node": "20.14.6", + "@types/uuid": "9.0.8", + "pagopa-interop-commons-test": "workspace:*", + "prettier": "2.8.8", + "tsx": "4.19.1", + "typescript": "5.4.5", + "vitest": "1.6.0", + "uuid": "10.0.0", + "jose": "5.9.4" + }, + "dependencies": { + "@aws-sdk/client-dynamodb": "3.637.0", + "@aws-sdk/client-kms": "3.600.0", + "@aws-sdk/util-dynamodb": "3.637.0", + "@zodios/core": "10.9.6", + "@zodios/express": "10.6.1", + "axios": "1.7.4", + "connection-string": "4.4.0", + "dotenv-flow": "4.1.0", + "express": "4.20.0", + "kafka-iam-auth": "workspace:*", + "openapi-zod-client": "1.18.1", + "pagopa-interop-api-clients": "workspace:*", + "pagopa-interop-client-assertion-validation": "workspace:*", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "ts-pattern": "5.2.0", + "zod": "3.23.8" + } +} diff --git a/packages/authorization-server/src/app.ts b/packages/authorization-server/src/app.ts new file mode 100644 index 0000000000..31e2d6bca3 --- /dev/null +++ b/packages/authorization-server/src/app.ts @@ -0,0 +1,24 @@ +import { + contextMiddleware, + loggerMiddleware, + zodiosCtx, +} from "pagopa-interop-commons"; +import express from "express"; +import healthRouter from "./routers/HealthRouter.js"; +import authorizationServerRouter from "./routers/AuthorizationServerRouter.js"; + +const serviceName = "authorization-server"; + +const app = zodiosCtx.app(); + +// Disable the "X-Powered-By: Express" HTTP header for security reasons. +// See https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#recommendation_16 +app.disable("x-powered-by"); + +app.use(healthRouter); +app.use(contextMiddleware(serviceName, false)); +app.use(express.urlencoded({ extended: true })); +app.use(loggerMiddleware(serviceName)); +app.use(authorizationServerRouter(zodiosCtx)); + +export default app; diff --git a/packages/authorization-server/src/config/config.ts b/packages/authorization-server/src/config/config.ts new file mode 100644 index 0000000000..8e81b097a3 --- /dev/null +++ b/packages/authorization-server/src/config/config.ts @@ -0,0 +1,43 @@ +import { + FileManagerConfig, + KafkaProducerConfig, + RedisRateLimiterConfig, + S3Config, + AuthorizationServerTokenGenerationConfig, + HTTPServerConfig, + LoggerConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +const AuthorizationServerConfig = HTTPServerConfig.and(LoggerConfig) + .and(RedisRateLimiterConfig) + .and(KafkaProducerConfig) + .and(FileManagerConfig) + .and(S3Config) + .and( + z + .object({ + TOKEN_AUDITING_TOPIC: z.string(), + }) + .transform((c) => ({ + tokenAuditingTopic: c.TOKEN_AUDITING_TOPIC, + })) + ) + .and(AuthorizationServerTokenGenerationConfig) + .and( + z + .object({ + TOKEN_GENERATION_READMODEL_TABLE_NAME_TOKEN_GENERATION: z.string(), + }) + .transform((c) => ({ + tokenGenerationStatesTable: + c.TOKEN_GENERATION_READMODEL_TABLE_NAME_TOKEN_GENERATION, + })) + ); + +export type AuthorizationServerConfig = z.infer< + typeof AuthorizationServerConfig +>; + +export const config: AuthorizationServerConfig = + AuthorizationServerConfig.parse(process.env); diff --git a/packages/authorization-server/src/index.ts b/packages/authorization-server/src/index.ts new file mode 100644 index 0000000000..160d2c4711 --- /dev/null +++ b/packages/authorization-server/src/index.ts @@ -0,0 +1,7 @@ +import { genericLogger } from "pagopa-interop-commons"; +import { config } from "./config/config.js"; +import app from "./app.js"; + +app.listen(config.port, config.host, () => { + genericLogger.info(`listening on ${config.host}:${config.port}`); +}); diff --git a/packages/authorization-server/src/model/domain/errors.ts b/packages/authorization-server/src/model/domain/errors.ts new file mode 100644 index 0000000000..f263765cb1 --- /dev/null +++ b/packages/authorization-server/src/model/domain/errors.ts @@ -0,0 +1,124 @@ +import { + ApiError, + ClientId, + ClientKindTokenStates, + makeApiProblemBuilder, + TokenGenerationStatesClientKidPK, + TokenGenerationStatesClientKidPurposePK, +} from "pagopa-interop-models"; + +export const errorCodes = { + clientAssertionRequestValidationFailed: "0001", + clientAssertionValidationFailed: "0002", + clientAssertionSignatureValidationFailed: "0003", + kafkaAuditingFailed: "0004", + fallbackAuditFailed: "0005", + tokenGenerationStatesEntryNotFound: "0006", + invalidTokenClientKidPurposeEntry: "0007", + keyTypeMismatch: "0008", + unexpectedTokenGenerationStatesEntry: "0009", + platformStateValidationFailed: "0010", +}; + +export type ErrorCodes = keyof typeof errorCodes; + +export const makeApiProblem = makeApiProblemBuilder(errorCodes); + +export function clientAssertionRequestValidationFailed( + clientId: string | undefined +): ApiError { + return new ApiError({ + detail: `Client assertion request validation failed for request by client ${clientId}`, + code: "clientAssertionRequestValidationFailed", + title: "Client assertion request validation failed", + }); +} + +export function clientAssertionValidationFailed( + clientId: string | undefined +): ApiError { + return new ApiError({ + detail: `Client assertion validation failed for clientId: ${clientId}`, + code: "clientAssertionValidationFailed", + title: "Client assertion validation failed", + }); +} + +export function clientAssertionSignatureValidationFailed( + clientId: string | undefined +): ApiError { + return new ApiError({ + detail: `Client assertion signature validation failed for client ${clientId}`, + code: "clientAssertionSignatureValidationFailed", + title: "Client assertion signature validation failed", + }); +} + +export function kafkaAuditingFailed(): ApiError { + return new ApiError({ + detail: "Kafka auditing failed ", + code: "kafkaAuditingFailed", + title: "Kafka auditing failed", + }); +} + +export function fallbackAuditFailed(clientId: ClientId): ApiError { + return new ApiError({ + detail: `Fallback audit failed for client ${clientId}`, + code: "fallbackAuditFailed", + title: "Fallback audit failed", + }); +} + +export function tokenGenerationStatesEntryNotFound( + pk: TokenGenerationStatesClientKidPurposePK | TokenGenerationStatesClientKidPK +): ApiError { + return new ApiError({ + detail: `Entry with PK ${pk} not found in token-generation-states table`, + code: "tokenGenerationStatesEntryNotFound", + title: "token-generation-states entry not found", + }); +} + +export function invalidTokenClientKidPurposeEntry( + pk: TokenGenerationStatesClientKidPurposePK | TokenGenerationStatesClientKidPK +): ApiError { + return new ApiError({ + detail: `Missing data in client-kid-purpose entry from token-generation-states table. Primary key: ${pk}`, + code: "invalidTokenClientKidPurposeEntry", + title: "Invalid token client-kid-purpose entry", + }); +} + +export function keyTypeMismatch( + pk: + | TokenGenerationStatesClientKidPurposePK + | TokenGenerationStatesClientKidPK, + clientKind: ClientKindTokenStates +): ApiError { + return new ApiError({ + detail: `Token-generation entry ${pk} can't have client kind: ${clientKind}`, + code: "keyTypeMismatch", + title: "Key type mismatch", + }); +} + +export function unexpectedTokenGenerationStatesEntry( + pk: TokenGenerationStatesClientKidPK | TokenGenerationStatesClientKidPurposePK +): ApiError { + return new ApiError({ + detail: `Unexpected token-generation-states entry, primary key: ${pk}`, + code: "unexpectedTokenGenerationStatesEntry", + title: "Unexpected token-generation-states entry", + }); +} + +export function platformStateValidationFailed( + details: string[] +): ApiError { + return new ApiError({ + detail: `Platform state validation failed - reasons: ${details}`, + code: "platformStateValidationFailed", + title: "Platform state validation failed", + }); +} diff --git a/packages/authorization-server/src/model/domain/models.ts b/packages/authorization-server/src/model/domain/models.ts new file mode 100644 index 0000000000..09fefba9f0 --- /dev/null +++ b/packages/authorization-server/src/model/domain/models.ts @@ -0,0 +1,5 @@ +export interface InteropTokenResponse { + access_token: string; + token_type: string; + expires_in: number; +} diff --git a/packages/authorization-server/src/routers/AuthorizationServerRouter.ts b/packages/authorization-server/src/routers/AuthorizationServerRouter.ts new file mode 100644 index 0000000000..ec49766192 --- /dev/null +++ b/packages/authorization-server/src/routers/AuthorizationServerRouter.ts @@ -0,0 +1,104 @@ +import { + ExpressContext, + fromAppContext, + initFileManager, + initRedisRateLimiter, + InteropTokenGenerator, + rateLimiterHeadersFromStatus, + ZodiosContext, + zodiosValidationErrorToApiProblem, +} from "pagopa-interop-commons"; +import { tooManyRequestsError } from "pagopa-interop-models"; +import { authorizationServerApi } from "pagopa-interop-api-clients"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { initProducer } from "kafka-iam-auth"; +import { ZodiosEndpointDefinitions } from "@zodios/core"; +import { ZodiosRouter } from "@zodios/express"; +import { makeApiProblem } from "../model/domain/errors.js"; +import { authorizationServerErrorMapper } from "../utilities/errorMappers.js"; +import { tokenServiceBuilder } from "../services/tokenService.js"; +import { config } from "../config/config.js"; + +const dynamoDBClient = new DynamoDBClient({}); +const redisRateLimiter = await initRedisRateLimiter({ + limiterGroup: "AUTHSERVER", + maxRequests: config.rateLimiterMaxRequests, + rateInterval: config.rateLimiterRateInterval, + burstPercentage: config.rateLimiterBurstPercentage, + redisHost: config.rateLimiterRedisHost, + redisPort: config.rateLimiterRedisPort, + timeout: config.rateLimiterTimeout, +}); +const producer = await initProducer(config, config.tokenAuditingTopic); +const fileManager = initFileManager(config); + +const tokenGenerator = new InteropTokenGenerator({ + generatedInteropTokenKid: config.generatedInteropTokenKid, + generatedInteropTokenIssuer: config.generatedInteropTokenIssuer, + generatedInteropTokenM2MAudience: config.generatedInteropTokenM2MAudience, + generatedInteropTokenM2MDurationSeconds: + config.generatedInteropTokenM2MDurationSeconds, +}); + +const tokenService = tokenServiceBuilder({ + tokenGenerator, + dynamoDBClient, + redisRateLimiter, + producer, + fileManager, +}); + +const authorizationServerRouter = ( + ctx: ZodiosContext +): ZodiosRouter => { + const authorizationServerRouter = ctx.router( + authorizationServerApi.authApi.api, + { + validationErrorHandler: zodiosValidationErrorToApiProblem, + } + ); + authorizationServerRouter.post("/token.oauth2", async (req, res) => { + const ctx = fromAppContext(req.ctx); + + try { + const tokenResult = await tokenService.generateToken( + req.body, + ctx.correlationId, + ctx.logger + ); + + const headers = rateLimiterHeadersFromStatus( + tokenResult.rateLimiterStatus + ); + res.set(headers); + + if (tokenResult.limitReached) { + const errorRes = makeApiProblem( + tooManyRequestsError(tokenResult.rateLimitedTenantId), + authorizationServerErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + + return res.status(200).send({ + access_token: tokenResult.token.serialized, + token_type: "Bearer", + expires_in: tokenResult.token.payload.exp, + }); + } catch (err) { + const errorRes = makeApiProblem( + err, + authorizationServerErrorMapper, + ctx.logger, + ctx.correlationId + ); + return res.status(errorRes.status).send(errorRes); + } + }); + return authorizationServerRouter; +}; + +export default authorizationServerRouter; diff --git a/packages/authorization-server/src/routers/HealthRouter.ts b/packages/authorization-server/src/routers/HealthRouter.ts new file mode 100644 index 0000000000..6658f9ab36 --- /dev/null +++ b/packages/authorization-server/src/routers/HealthRouter.ts @@ -0,0 +1,8 @@ +import { zodiosRouter } from "@zodios/express"; +import { authorizationServerApi } from "pagopa-interop-api-clients"; + +const healthRouter = zodiosRouter(authorizationServerApi.healthApi.api); + +healthRouter.get("/status", async (_, res) => res.status(200).send()); + +export default healthRouter; diff --git a/packages/authorization-server/src/services/tokenService.ts b/packages/authorization-server/src/services/tokenService.ts new file mode 100644 index 0000000000..e582016a03 --- /dev/null +++ b/packages/authorization-server/src/services/tokenService.ts @@ -0,0 +1,412 @@ +import { + validateClientKindAndPlatformState, + validateRequestParameters, + verifyClientAssertion, + verifyClientAssertionSignature, +} from "pagopa-interop-client-assertion-validation"; +import { authorizationServerApi } from "pagopa-interop-api-clients"; +import { + clientKidPrefix, + clientKidPurposePrefix, + clientKindTokenStates, + DescriptorId, + EServiceId, + generateId, + genericInternalError, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + TenantId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientKidPK, + TokenGenerationStatesClientKidPurposePK, + TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesGenericEntry, + unsafeBrandId, + GeneratedTokenAuditDetails, + GSIPKEServiceIdDescriptorId, + ClientAssertion, + FullTokenGenerationStatesClientPurposeEntry, +} from "pagopa-interop-models"; +import { + DynamoDBClient, + GetItemCommand, + GetItemCommandOutput, + GetItemInput, +} from "@aws-sdk/client-dynamodb"; +import { unmarshall } from "@aws-sdk/util-dynamodb"; +import { match } from "ts-pattern"; +import { + FileManager, + formatDateyyyyMMdd, + formatTimehhmmss, + InteropApiToken, + InteropConsumerToken, + InteropTokenGenerator, + Logger, + RateLimiter, + RateLimiterStatus, +} from "pagopa-interop-commons"; +import { initProducer } from "kafka-iam-auth"; +import { config } from "../config/config.js"; +import { + clientAssertionRequestValidationFailed, + clientAssertionSignatureValidationFailed, + clientAssertionValidationFailed, + fallbackAuditFailed, + invalidTokenClientKidPurposeEntry, + kafkaAuditingFailed, + tokenGenerationStatesEntryNotFound, + keyTypeMismatch, + unexpectedTokenGenerationStatesEntry, + platformStateValidationFailed, +} from "../model/domain/errors.js"; + +export type GenerateTokenReturnType = + | { + limitReached: true; + token: undefined; + rateLimitedTenantId: TenantId; + rateLimiterStatus: Omit; + } + | { + limitReached: false; + token: InteropConsumerToken | InteropApiToken; + rateLimiterStatus: Omit; + }; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function tokenServiceBuilder({ + tokenGenerator, + dynamoDBClient, + redisRateLimiter, + producer, + fileManager, +}: { + tokenGenerator: InteropTokenGenerator; + dynamoDBClient: DynamoDBClient; + redisRateLimiter: RateLimiter; + producer: Awaited>; + fileManager: FileManager; +}) { + return { + async generateToken( + request: authorizationServerApi.AccessTokenRequest, + correlationId: string, + logger: Logger + ): Promise { + const { errors: parametersErrors } = validateRequestParameters({ + client_assertion: request.client_assertion, + client_assertion_type: request.client_assertion_type, + grant_type: request.grant_type, + client_id: request.client_id, + }); + + if (parametersErrors) { + throw clientAssertionRequestValidationFailed(request.client_id); + } + + const { data: jwt, errors: clientAssertionErrors } = + verifyClientAssertion(request.client_assertion, request.client_id); + + if (clientAssertionErrors) { + // TODO double check if errors have to be logged or put inside the error below (check the same for parameters errors) + logger.warn(clientAssertionErrors.map((error) => error.detail)); + throw clientAssertionValidationFailed(request.client_id); + } + + const clientId = jwt.payload.sub; + const kid = jwt.header.kid; + const purposeId = jwt.payload.purposeId; + + const pk = purposeId + ? makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }) + : makeTokenGenerationStatesClientKidPK({ clientId, kid }); + + const key = await retrieveKey(dynamoDBClient, pk); + + const { errors: clientAssertionSignatureErrors } = + await verifyClientAssertionSignature( + request.client_assertion, + key, + jwt.header.alg + ); + + if (clientAssertionSignatureErrors) { + throw clientAssertionSignatureValidationFailed(request.client_id); + } + + const { errors: platformStateErrors } = + validateClientKindAndPlatformState(key, jwt); + if (platformStateErrors) { + throw platformStateValidationFailed( + platformStateErrors.map((error) => error.detail) + ); + } + + const { limitReached, ...rateLimiterStatus } = + await redisRateLimiter.rateLimitByOrganization(key.consumerId, logger); + if (limitReached) { + return { + limitReached: true, + token: undefined, + rateLimitedTenantId: key.consumerId, + rateLimiterStatus, + }; + } + + return await match(key.clientKind) + .with(clientKindTokenStates.consumer, async () => { + const parsedKey = + FullTokenGenerationStatesClientPurposeEntry.safeParse(key); + if (parsedKey.success) { + const token = await tokenGenerator.generateInteropConsumerToken({ + sub: jwt.payload.sub, + audience: parsedKey.data.descriptorAudience, + purposeId: parsedKey.data.GSIPK_purposeId, + tokenDurationInSeconds: parsedKey.data.descriptorVoucherLifespan, + digest: jwt.payload.digest, + }); + + await publishAudit({ + producer, + generatedToken: token, + key: parsedKey.data, + clientAssertion: jwt, + correlationId, + fileManager, + logger, + }); + + return { + limitReached: false as const, + token, + rateLimiterStatus, + }; + } + throw invalidTokenClientKidPurposeEntry(key.PK); + }) + .with(clientKindTokenStates.api, async () => { + const token = await tokenGenerator.generateInteropApiToken({ + sub: jwt.payload.sub, + consumerId: key.consumerId, + }); + + return { + limitReached: false as const, + token, + rateLimiterStatus, + }; + }) + .exhaustive(); + }, + }; +} + +export const retrieveKey = async ( + dynamoDBClient: DynamoDBClient, + pk: TokenGenerationStatesClientKidPurposePK | TokenGenerationStatesClientKidPK +): Promise< + TokenGenerationStatesClientEntry | TokenGenerationStatesClientPurposeEntry +> => { + const input: GetItemInput = { + Key: { + PK: { S: pk }, + }, + TableName: config.tokenGenerationStatesTable, + }; + + const command = new GetItemCommand(input); + const data: GetItemCommandOutput = await dynamoDBClient.send(command); + + if (!data.Item) { + throw tokenGenerationStatesEntryNotFound(pk); + } else { + const unmarshalled = unmarshall(data.Item); + const tokenGenerationEntry = + TokenGenerationStatesGenericEntry.safeParse(unmarshalled); + + if (!tokenGenerationEntry.success) { + throw genericInternalError( + `Unable to parse token generation entry item: result ${JSON.stringify( + tokenGenerationEntry + )} - data ${JSON.stringify(data)} ` + ); + } + + return match(tokenGenerationEntry.data) + .when( + (entry) => + entry.clientKind === clientKindTokenStates.consumer && + entry.PK.startsWith(clientKidPurposePrefix), + () => { + const clientKidPurposeEntry = + FullTokenGenerationStatesClientPurposeEntry.safeParse( + tokenGenerationEntry.data + ); + if (!clientKidPurposeEntry.success) { + throw invalidTokenClientKidPurposeEntry( + tokenGenerationEntry.data.PK + ); + } + + return clientKidPurposeEntry.data; + } + ) + .when( + (entry) => + entry.clientKind === clientKindTokenStates.consumer && + entry.PK.startsWith(clientKidPrefix), + (entry) => { + throw keyTypeMismatch(entry.PK, entry.clientKind); + } + ) + .when( + (entry) => + entry.clientKind === clientKindTokenStates.api && + entry.PK.startsWith(clientKidPurposePrefix), + (entry) => { + throw keyTypeMismatch(entry.PK, entry.clientKind); + } + ) + .when( + (entry) => + entry.clientKind === clientKindTokenStates.api && + entry.PK.startsWith(clientKidPrefix), + () => tokenGenerationEntry.data as TokenGenerationStatesClientEntry + ) + .otherwise(() => { + throw unexpectedTokenGenerationStatesEntry( + tokenGenerationEntry.data.PK + ); + }); + } +}; + +export const publishAudit = async ({ + producer, + generatedToken, + key, + clientAssertion, + correlationId, + fileManager, + logger, +}: { + producer: Awaited>; + generatedToken: InteropConsumerToken | InteropApiToken; + key: FullTokenGenerationStatesClientPurposeEntry; + clientAssertion: ClientAssertion; + correlationId: string; + fileManager: FileManager; + logger: Logger; +}): Promise => { + const messageBody: GeneratedTokenAuditDetails = { + jwtId: generatedToken.payload.jti, + correlationId, + issuedAt: generatedToken.payload.iat, + clientId: clientAssertion.payload.sub, + organizationId: key.consumerId, + agreementId: key.agreementId, + eserviceId: deconstructGSIPK_eserviceId_descriptorId( + key.GSIPK_eserviceId_descriptorId + ).eserviceId, + descriptorId: deconstructGSIPK_eserviceId_descriptorId( + key.GSIPK_eserviceId_descriptorId + ).descriptorId, + purposeId: key.GSIPK_purposeId, + purposeVersionId: unsafeBrandId(key.purposeVersionId), + algorithm: generatedToken.header.alg, + keyId: generatedToken.header.kid, + audience: generatedToken.payload.aud.join(","), + subject: generatedToken.payload.sub, + notBefore: generatedToken.payload.nbf, + expirationTime: generatedToken.payload.exp, + issuer: generatedToken.payload.iss, + clientAssertion: { + algorithm: clientAssertion.header.alg, + audience: [clientAssertion.payload.aud].flat().join(","), + expirationTime: clientAssertion.payload.exp, + issuedAt: clientAssertion.payload.iat, + issuer: clientAssertion.payload.iss, + jwtId: clientAssertion.payload.jti, + keyId: clientAssertion.header.kid, + subject: clientAssertion.payload.sub, + }, + }; + + try { + const res = await producer.send({ + messages: [ + { + key: generatedToken.payload.jti, + value: JSON.stringify(messageBody), + }, + ], + }); + if (res.length === 0 || res[0].errorCode !== 0) { + throw kafkaAuditingFailed(); + } + } catch (e) { + logger.info("main auditing flow failed, going through fallback"); + await fallbackAudit(messageBody, fileManager, logger); + } +}; + +export const fallbackAudit = async ( + messageBody: GeneratedTokenAuditDetails, + fileManager: FileManager, + logger: Logger +): Promise => { + const date = new Date(); + const ymdDate = formatDateyyyyMMdd(date); + const hmsTime = formatTimehhmmss(date); + + const fileName = `${ymdDate}_${hmsTime}_${generateId()}.ndjson`; + const filePath = `token-details/${ymdDate}`; + + try { + await fileManager.storeBytes( + { + bucket: config.s3Bucket, + path: filePath, + name: fileName, + content: Buffer.from(JSON.stringify(messageBody)), + }, + logger + ); + logger.info("auditing succeeded through fallback"); + } catch { + throw fallbackAuditFailed(messageBody.clientId); + } +}; + +const deconstructGSIPK_eserviceId_descriptorId = ( + gsi: GSIPKEServiceIdDescriptorId +): { eserviceId: EServiceId; descriptorId: DescriptorId } => { + const substrings = gsi.split("#"); + const eserviceId = substrings[0]; + const descriptorId = substrings[1]; + const parsedEserviceId = EServiceId.safeParse(eserviceId); + + if (!parsedEserviceId.success) { + throw genericInternalError( + `Unable to parse extract eserviceId from GSIPKEServiceIdDescriptorId: ${GSIPKEServiceIdDescriptorId}` + ); + } + + const parsedDescriptorId = DescriptorId.safeParse(descriptorId); + + if (!parsedDescriptorId.success) { + throw genericInternalError( + `Unable to parse extract descriptorId from GSIPKEServiceIdDescriptorId: ${GSIPKEServiceIdDescriptorId}` + ); + } + + return { + eserviceId: parsedEserviceId.data, + descriptorId: parsedDescriptorId.data, + }; +}; diff --git a/packages/authorization-server/src/utilities/errorMappers.ts b/packages/authorization-server/src/utilities/errorMappers.ts new file mode 100644 index 0000000000..f97666fb03 --- /dev/null +++ b/packages/authorization-server/src/utilities/errorMappers.ts @@ -0,0 +1,23 @@ +import { constants } from "http2"; +import { ApiError, CommonErrorCodes } from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { ErrorCodes as LocalErrorCodes } from "../model/domain/errors.js"; + +type ErrorCodes = LocalErrorCodes | CommonErrorCodes; + +const { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_BAD_REQUEST } = + constants; + +export const authorizationServerErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with( + "tokenGenerationStatesEntryNotFound", + "clientAssertionRequestValidationFailed", + "clientAssertionSignatureValidationFailed", + "clientAssertionValidationFailed", + "platformStateValidationFailed", + () => HTTP_STATUS_BAD_REQUEST + ) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/authorization-server/test/.eslintrc.json b/packages/authorization-server/test/.eslintrc.json new file mode 100644 index 0000000000..6135a5ce08 --- /dev/null +++ b/packages/authorization-server/test/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "extends": ["../../../.eslintrc.cjs"], + "rules": { + "functional/immutable-data": "off", + "sonarjs/no-identical-functions": "off" + } +} diff --git a/packages/authorization-server/test/authorizationServer.integration.test.ts b/packages/authorization-server/test/authorizationServer.integration.test.ts new file mode 100644 index 0000000000..158b500d10 --- /dev/null +++ b/packages/authorization-server/test/authorizationServer.integration.test.ts @@ -0,0 +1,808 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import crypto from "crypto"; +import { fail } from "assert"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockTokenStatesClientPurposeEntry, + getMockPurpose, + getMockPurposeVersion, + getMockTokenStatesClientEntry, + writeTokenStateEntry, + writeTokenStateClientEntry, + getMockClientAssertion, +} from "pagopa-interop-commons-test"; +import { + AgreementId, + ClientId, + clientKindTokenStates, + EServiceId, + GeneratedTokenAuditDetails, + generateId, + itemState, + makeGSIPKKid, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + Purpose, + PurposeId, + purposeVersionState, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, + unsafeBrandId, +} from "pagopa-interop-models"; +import { formatDateyyyyMMdd, genericLogger } from "pagopa-interop-commons"; +import { authorizationServerApi } from "pagopa-interop-api-clients"; +import { config } from "../src/config/config.js"; +import { + clientAssertionRequestValidationFailed, + clientAssertionSignatureValidationFailed, + clientAssertionValidationFailed, + fallbackAuditFailed, + invalidTokenClientKidPurposeEntry, + keyTypeMismatch, + platformStateValidationFailed, + tokenGenerationStatesEntryNotFound, +} from "../src/model/domain/errors.js"; +import { inactiveEService } from "../../client-assertion-validation/dist/errors.js"; +import { + configTokenGenerationStates, + dynamoDBClient, + fileManager, + getMockAccessTokenRequest, + mockKMSClient, + mockProducer, + tokenService, +} from "./utils.js"; + +describe("authorization server tests", () => { + if (!configTokenGenerationStates) { + fail(); + } + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + mockKMSClient.send.mockImplementation(async () => ({ + Signature: "mock signature", + })); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + vi.restoreAllMocks(); + }); + + it("should throw clientAssertionRequestValidationFailed", async () => { + const { jws } = await getMockClientAssertion(); + + const clientId = generateId(); + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion_type: "wrong-client-assertion-type", + client_assertion: jws, + client_id: clientId, + }; + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError(clientAssertionRequestValidationFailed(clientId)); + }); + + it("should throw clientAssertionValidationFailed", async () => { + const { jws } = await getMockClientAssertion({ + standardClaimsOverride: { iat: undefined }, + }); + + const clientId = generateId(); + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError(clientAssertionValidationFailed(clientId)); + }); + + it("should throw tokenGenerationStatesEntryNotFound", async () => { + const purposeId = generateId(); + const clientId = generateId(); + const { jws, clientAssertion } = await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const entryPK = makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid: clientAssertion.header.kid!, + purposeId, + }); + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError(tokenGenerationStatesEntryNotFound(entryPK)); + }); + + it("should throw invalidTokenClientKidPurposeEntry", async () => { + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion } = await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + agreementId: undefined, + }; + + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + invalidTokenClientKidPurposeEntry(tokenClientPurposeEntry.PK) + ); + }); + + it("should throw keyTypeMismatch - clientKid entry with consumer kind", async () => { + const clientId = generateId(); + const { jws, clientAssertion } = await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId, + kid: clientAssertion.header.kid!, + }); + + const tokenClientKidEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + clientKind: clientKindTokenStates.consumer, + }; + + await writeTokenStateClientEntry(tokenClientKidEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + keyTypeMismatch(tokenClientKidEntry.PK, clientKindTokenStates.consumer) + ); + }); + + it("should throw keyTypeMismatch - clientKidPurpose entry with api kind", async () => { + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion } = await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + clientKind: clientKindTokenStates.api, + }; + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + keyTypeMismatch(tokenClientKidPurposeEntry.PK, clientKindTokenStates.api) + ); + }); + + it("should throw clientAssertionSignatureValidationFailed", async () => { + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion } = await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const splitJws = jws.split("."); + const jwsWithWrongSignature = `${splitJws[0]}.${splitJws[1]}.wrong-singature`; + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jwsWithWrongSignature, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK); + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + clientAssertionSignatureValidationFailed(request.client_id) + ); + }); + + it("should throw platformStateValidationFailed", async () => { + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + descriptorState: itemState.inactive, + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + platformStateValidationFailed([inactiveEService().detail]) + ); + }); + + it("should block the request because of the rate limiter", async () => { + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + // eslint-disable-next-line functional/no-let + for (let i = 0; i < config.rateLimiterMaxRequests; i++) { + const response = await tokenService.generateToken( + request, + generateId(), + genericLogger + ); + expect(response.limitReached).toBe(false); + expect(response.rateLimiterStatus.remainingRequests).toBe( + config.rateLimiterMaxRequests - i - 1 + ); + } + + const responseAfterLimitExceeded = await tokenService.generateToken( + request, + generateId(), + genericLogger + ); + + expect(responseAfterLimitExceeded).toEqual({ + limitReached: true, + rateLimitedTenantId: tokenClientKidPurposeEntry.consumerId, + token: undefined, + rateLimiterStatus: { + maxRequests: config.rateLimiterMaxRequests, + rateInterval: config.rateLimiterRateInterval, + remainingRequests: 0, + }, + }); + }); + + it("should throw error during token signing - consumer key", async () => { + const uuid = crypto.randomUUID(); + const uuidSpy = vi.spyOn(crypto, "randomUUID"); + uuidSpy.mockReturnValue(uuid); + + mockKMSClient.send.mockImplementationOnce(() => + Promise.resolve({ signature: undefined }) + ); + + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + clientKind: clientKindTokenStates.consumer, + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + Error("JWT Signature failed. Empty signature returned") + ); + }); + + it("should throw tokenSigningFailed - api key", async () => { + const uuid = crypto.randomUUID(); + const uuidSpy = vi.spyOn(crypto, "randomUUID"); + uuidSpy.mockReturnValue(uuid); + + mockKMSClient.send.mockImplementationOnce(() => + Promise.resolve({ signature: undefined }) + ); + + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId, + kid: clientAssertion.header.kid!, + }); + + const tokenClientKidEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + clientKind: clientKindTokenStates.api, + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateClientEntry(tokenClientKidEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + Error("JWT Signature failed. Empty signature returned") + ); + }); + + it("should throw fallbackAuditFailed - consumer key - kafka audit failed and fallback audit failed", async () => { + const uuid = crypto.randomUUID(); + const uuidSpy = vi.spyOn(crypto, "randomUUID"); + uuidSpy.mockReturnValue(uuid); + + mockProducer.send.mockImplementationOnce(async () => Promise.reject()); + vi.spyOn(fileManager, "storeBytes").mockImplementationOnce(() => + Promise.reject() + ); + + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError(fallbackAuditFailed(clientId)); + }); + + it("should succeed - consumer key - kafka audit failed and fallback audit succeeded", async () => { + mockProducer.send.mockImplementation(async () => Promise.reject()); + + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + + const fileListBeforeAudit = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileListBeforeAudit).toHaveLength(0); + + const uuid = crypto.randomUUID(); + const uuidSpy = vi.spyOn(crypto, "randomUUID"); + uuidSpy.mockReturnValue(uuid); + + const correlationId = generateId(); + const response = await tokenService.generateToken( + request, + correlationId, + genericLogger + ); + + const date = new Date(); + const ymdDate = formatDateyyyyMMdd(date); + + const fileListAfterAudit = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileListAfterAudit).toHaveLength(1); + const file = fileListAfterAudit[0]; + const split = file.split("_"); + expect(split[0]).toEqual(`token-details/${ymdDate}/${ymdDate}`); + + const fileContent = await fileManager.get( + config.s3Bucket, + file, + genericLogger + ); + + const decodedFileContent = new TextDecoder().decode(fileContent); + const parsedDecodedFileContent = JSON.parse(decodedFileContent); + + const expectedMessageBody: GeneratedTokenAuditDetails = { + jwtId: generateId(), + correlationId, + issuedAt: parsedDecodedFileContent.issuedAt, + clientId, + organizationId: tokenClientKidPurposeEntry.consumerId, + agreementId: unsafeBrandId( + tokenClientKidPurposeEntry.agreementId! + ), + eserviceId: unsafeBrandId( + tokenClientKidPurposeEntry.GSIPK_eserviceId_descriptorId!.split("#")[0] + ), + descriptorId: unsafeBrandId( + tokenClientKidPurposeEntry.GSIPK_eserviceId_descriptorId!.split("#")[1] + ), + purposeId: tokenClientKidPurposeEntry.GSIPK_purposeId!, + purposeVersionId: tokenClientKidPurposeEntry.purposeVersionId!, + algorithm: "RS256", + keyId: config.generatedInteropTokenKid, + audience: tokenClientKidPurposeEntry.descriptorAudience!.join(","), + subject: clientId, + notBefore: parsedDecodedFileContent.notBefore, + expirationTime: parsedDecodedFileContent.expirationTime, + issuer: config.generatedInteropTokenIssuer, + clientAssertion: { + algorithm: clientAssertion.header.alg, + audience: [clientAssertion.payload.aud].flat().join(","), + expirationTime: clientAssertion.payload.exp!, + issuedAt: clientAssertion.payload.iat!, + issuer: clientAssertion.payload.iss!, + jwtId: clientAssertion.payload.jti!, + keyId: clientAssertion.header.kid!, + subject: unsafeBrandId(clientAssertion.payload.sub!), + }, + }; + expect(parsedDecodedFileContent).toEqual(expectedMessageBody); + expect(response.limitReached).toBe(false); + expect(response.token).toBeDefined(); + expect(response.rateLimiterStatus).toEqual({ + maxRequests: config.rateLimiterMaxRequests, + rateInterval: config.rateLimiterRateInterval, + remainingRequests: config.rateLimiterMaxRequests - 1, + }); + }); + + it("should succeed - consumer key - kafka audit succeeded", async () => { + mockProducer.send.mockImplementationOnce(async () => [ + { topic: config.tokenAuditingTopic, partition: 0, errorCode: 0 }, + ]); + mockKMSClient.send.mockImplementationOnce(async () => ({ + Signature: "mock signature", + })); + + vi.spyOn(mockProducer, "send"); + vi.spyOn(fileManager, "storeBytes"); + + const purpose: Purpose = { + ...getMockPurpose(), + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { + sub: clientId, + }, + customClaims: { purposeId: purpose.id }, + }); + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId: purpose.id, + } + ); + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + consumerId: purpose.consumerId, + GSIPK_purposeId: purpose.id, + purposeState: itemState.active, + purposeVersionId: purpose.versions[0].id, + agreementState: itemState.active, + descriptorState: itemState.active, + GSIPK_clientId: clientId, + GSIPK_kid: makeGSIPKKid(clientAssertion.header.kid!), + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + + const request = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const uuid = crypto.randomUUID(); + const uuidSpy = vi.spyOn(crypto, "randomUUID"); + uuidSpy.mockReturnValue(uuid); + + const correlationId = generateId(); + const result = await tokenService.generateToken( + request, + correlationId, + genericLogger + ); + + expect(result.token).toBeDefined(); + + expect(result.limitReached).toBe(false); + expect(result.token).toBeDefined(); + expect(result.rateLimiterStatus).toEqual({ + maxRequests: config.rateLimiterMaxRequests, + rateInterval: config.rateLimiterRateInterval, + remainingRequests: config.rateLimiterMaxRequests - 1, + }); + + const fileList = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileList).toHaveLength(0); + expect(fileManager.storeBytes).not.toHaveBeenCalled(); + + const actualMessageSent = mockProducer.send.mock.calls[0][0] + .messages[0] as { key: string; value: string }; + + const parsedAuditSent = JSON.parse(actualMessageSent.value); + + const expectedMessageBody: GeneratedTokenAuditDetails = { + jwtId: generateId(), + correlationId, + issuedAt: parsedAuditSent.issuedAt, + clientId, + organizationId: tokenClientPurposeEntry.consumerId, + agreementId: unsafeBrandId( + tokenClientPurposeEntry.agreementId! + ), + eserviceId: unsafeBrandId( + tokenClientPurposeEntry.GSIPK_eserviceId_descriptorId!.split("#")[0] + ), + descriptorId: unsafeBrandId( + tokenClientPurposeEntry.GSIPK_eserviceId_descriptorId!.split("#")[1] + ), + purposeId: tokenClientPurposeEntry.GSIPK_purposeId!, + purposeVersionId: tokenClientPurposeEntry.purposeVersionId!, + algorithm: "RS256", + keyId: config.generatedInteropTokenKid, + audience: tokenClientPurposeEntry.descriptorAudience!.join(","), + subject: clientId, + notBefore: parsedAuditSent.notBefore, + expirationTime: parsedAuditSent.expirationTime, + issuer: config.generatedInteropTokenIssuer, + clientAssertion: { + algorithm: clientAssertion.header.alg, + audience: [clientAssertion.payload.aud].flat().join(","), + expirationTime: clientAssertion.payload.exp!, + issuedAt: clientAssertion.payload.iat!, + issuer: clientAssertion.payload.iss!, + jwtId: clientAssertion.payload.jti!, + keyId: clientAssertion.header.kid!, + subject: unsafeBrandId(clientAssertion.payload.sub!), + }, + }; + + expect(parsedAuditSent).toEqual(expectedMessageBody); + }); + + it("should succeed - api key - no audit", async () => { + vi.spyOn(fileManager, "storeBytes"); + + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidK = makeTokenGenerationStatesClientKidPK({ + clientId, + kid: clientAssertion.header.kid!, + }); + + const tokenClientKidEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidK), + clientKind: clientKindTokenStates.api, + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateClientEntry(tokenClientKidEntry, dynamoDBClient); + + const fileListBefore = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileListBefore).toHaveLength(0); + + const response = await tokenService.generateToken( + request, + generateId(), + genericLogger + ); + + const fileListAfter = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileListAfter).toHaveLength(0); + expect(fileManager.storeBytes).not.toHaveBeenCalled(); + + expect(response.limitReached).toBe(false); + expect(response.token).toBeDefined(); + expect(response.rateLimiterStatus).toEqual({ + maxRequests: config.rateLimiterMaxRequests, + rateInterval: config.rateLimiterRateInterval, + remainingRequests: config.rateLimiterMaxRequests - 1, + }); + }); +}); diff --git a/packages/authorization-server/test/authorizationServer.unit.test.ts b/packages/authorization-server/test/authorizationServer.unit.test.ts new file mode 100644 index 0000000000..ddfd05fc33 --- /dev/null +++ b/packages/authorization-server/test/authorizationServer.unit.test.ts @@ -0,0 +1,272 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockTokenStatesClientEntry, + getMockTokenStatesClientPurposeEntry, + writeTokenStateClientEntry, + writeTokenStateEntry, +} from "pagopa-interop-commons-test"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + ClientId, + clientKindTokenStates, + generateId, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + PurposeId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, +} from "pagopa-interop-models"; +import {} from "pagopa-interop-client-assertion-validation"; +import { genericLogger } from "pagopa-interop-commons"; +import { fallbackAudit, retrieveKey } from "../src/services/tokenService.js"; +import { + fallbackAuditFailed, + invalidTokenClientKidPurposeEntry, + keyTypeMismatch, + tokenGenerationStatesEntryNotFound, +} from "../src/model/domain/errors.js"; +import { config } from "../src/config/config.js"; +import { + dynamoDBClient, + fileManager, + getMockAuditMessage, + mockKMSClient, + mockProducer, +} from "./utils.js"; + +describe("unit tests", () => { + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + mockKMSClient.send.mockImplementation(async () => ({ + Signature: "mock signature", + })); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + vi.restoreAllMocks(); + }); + + describe("retrieveKey", () => { + it("should throw tokenGenerationStatesEntryNotFound if the clientKidPurpose entry doesn't exist in token-generation-states", async () => { + const clientId1 = generateId(); + const kid = "kid"; + const purposeId1 = generateId(); + const clientId2 = generateId(); + const purposeId2 = generateId(); + + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: clientId1, + kid, + purposeId: purposeId1, + }); + + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: clientId2, + kid, + purposeId: purposeId2, + }); + + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1); + + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + + expect( + retrieveKey(dynamoDBClient, tokenClientKidPurposePK2) + ).rejects.toThrowError( + tokenGenerationStatesEntryNotFound(tokenClientKidPurposePK2) + ); + }); + + it("should throw tokenGenerationStatesEntryNotFound if the clientKid entry doesn't exist in token-generation-states", async () => { + const clientId1 = generateId(); + const kid = "kid"; + const clientId2 = generateId(); + + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPK({ + clientId: clientId1, + kid, + }); + + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPK({ + clientId: clientId2, + kid, + }); + + const tokenClientEntry1: TokenGenerationStatesClientEntry = + getMockTokenStatesClientEntry(tokenClientKidPK1); + + await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); + + expect( + retrieveKey(dynamoDBClient, tokenClientKidPK2) + ).rejects.toThrowError( + tokenGenerationStatesEntryNotFound(tokenClientKidPK2) + ); + }); + + it("should throw invalidTokenClientKidPurposeEntry - clientKidPurpose entry - consumer key - missing info", async () => { + const clientId = generateId(); + const kid = "kid"; + const purposeId = generateId(); + + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }); + + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + agreementId: undefined, + }; + + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + expect( + retrieveKey(dynamoDBClient, tokenClientKidPurposePK) + ).rejects.toThrowError( + invalidTokenClientKidPurposeEntry(tokenClientPurposeEntry.PK) + ); + }); + + it("should succeed - clientKidPurpose entry - consumer key", async () => { + const clientId = generateId(); + const kid = "kid"; + const purposeId = generateId(); + + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }); + + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + clientKind: clientKindTokenStates.consumer, + }; + + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + const key = await retrieveKey(dynamoDBClient, tokenClientKidPurposePK); + + expect(key).toEqual(tokenClientPurposeEntry); + }); + + it("should throw keyTypeMismatch - clientKid entry with consumer key", async () => { + const clientId = generateId(); + const kid = "kid"; + + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId, + kid, + }); + + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + clientKind: clientKindTokenStates.consumer, + }; + + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + expect( + retrieveKey(dynamoDBClient, tokenClientKidPK) + ).rejects.toThrowError( + keyTypeMismatch(tokenClientEntry.PK, clientKindTokenStates.consumer) + ); + }); + + it("should throw keyTypeMismatch - clientKidPurpose entry with api key", async () => { + const clientId = generateId(); + const kid = "kid"; + const purposeId = generateId(); + + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }); + + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + clientKind: clientKindTokenStates.api, + }; + + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + expect( + retrieveKey(dynamoDBClient, tokenClientKidPurposePK) + ).rejects.toThrowError( + keyTypeMismatch(tokenClientPurposeEntry.PK, clientKindTokenStates.api) + ); + }); + + it("should succeed - clientKid entry - api key", async () => { + const clientId = generateId(); + const kid = "kid"; + + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId, + kid, + }); + + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + clientKind: clientKindTokenStates.api, + }; + + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + const key = await retrieveKey(dynamoDBClient, tokenClientKidPK); + + expect(key).toEqual(tokenClientEntry); + }); + }); + + describe("fallbackAudit", () => { + it("should write the audit message to the file storage", async () => { + const mockAuditMessage = getMockAuditMessage(); + + const fileListBeforeAudit = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileListBeforeAudit).toHaveLength(0); + + await fallbackAudit(mockAuditMessage, fileManager, genericLogger); + + const fileListAfterAudit = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileListAfterAudit).toHaveLength(1); + + const fileContent = await fileManager.get( + config.s3Bucket, + fileListAfterAudit[0], + genericLogger + ); + + const expectedFileContent = JSON.stringify(mockAuditMessage); + + const decodedFileContent = new TextDecoder().decode(fileContent); + expect(decodedFileContent).toEqual(expectedFileContent); + }); + + it("should throw fallbackAuditFailed in case of unsuccessful file write operation", async () => { + const mockAuditMessage = getMockAuditMessage(); + + mockProducer.send.mockImplementationOnce(async () => Promise.reject()); + vi.spyOn(fileManager, "storeBytes").mockImplementationOnce(() => + Promise.reject() + ); + + expect( + fallbackAudit(mockAuditMessage, fileManager, genericLogger) + ).rejects.toThrowError(fallbackAuditFailed(mockAuditMessage.clientId)); + }); + }); +}); diff --git a/packages/authorization-server/test/tsconfig.json b/packages/authorization-server/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/authorization-server/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/authorization-server/test/utils.ts b/packages/authorization-server/test/utils.ts new file mode 100644 index 0000000000..ecbdd4daf7 --- /dev/null +++ b/packages/authorization-server/test/utils.ts @@ -0,0 +1,127 @@ +import { + getMockClientAssertion, + setupTestContainersVitest, +} from "pagopa-interop-commons-test"; +import { + AgreementId, + ClientId, + DescriptorId, + EServiceId, + GeneratedTokenAuditDetails, + generateId, + PurposeId, + PurposeVersionId, + TenantId, +} from "pagopa-interop-models"; +import { afterEach, inject, vi } from "vitest"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { KMSClient } from "@aws-sdk/client-kms"; +import { initProducer } from "kafka-iam-auth"; +import { authorizationServerApi } from "pagopa-interop-api-clients"; +import { dateToSeconds, InteropTokenGenerator } from "pagopa-interop-commons"; +import { tokenServiceBuilder } from "../src/services/tokenService.js"; +import { config } from "../src/config/config.js"; + +export const configTokenGenerationStates = inject( + "tokenGenerationReadModelConfig" +); + +export const { cleanup, fileManager, redisRateLimiter } = + await setupTestContainersVitest( + undefined, + undefined, + inject("fileManagerConfig"), + undefined, + inject("redisRateLimiterConfig") + ); + +afterEach(cleanup); + +if (configTokenGenerationStates === undefined) { + throw new Error("configTokenGenerationStates is undefined"); +} + +export const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${configTokenGenerationStates.tokenGenerationReadModelDbPort}`, +}); + +export const mockProducer = { + send: vi.fn(), +}; +export const mockKMSClient = { + send: vi.fn(), +}; + +const tokenGenerator = new InteropTokenGenerator( + { + generatedInteropTokenKid: config.generatedInteropTokenKid, + generatedInteropTokenIssuer: config.generatedInteropTokenIssuer, + generatedInteropTokenM2MAudience: config.generatedInteropTokenM2MAudience, + generatedInteropTokenM2MDurationSeconds: + config.generatedInteropTokenM2MDurationSeconds, + }, + mockKMSClient as unknown as KMSClient +); + +export const tokenService = tokenServiceBuilder({ + tokenGenerator, + dynamoDBClient, + redisRateLimiter, + producer: mockProducer as unknown as Awaited>, + fileManager, +}); + +export const getMockAccessTokenRequest = + async (): Promise => { + const { jws } = await getMockClientAssertion(); + return { + client_id: generateId(), + client_assertion_type: + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + client_assertion: jws, + grant_type: "client_credentials", + }; + }; + +export const getMockAuditMessage = (): GeneratedTokenAuditDetails => { + const correlationId = generateId(); + const eserviceId = generateId(); + const descriptorId = generateId(); + const agreementId = generateId(); + const clientId = generateId(); + const purposeId = generateId(); + const kid = "kid"; + const purposeVersionId = generateId(); + const consumerId = generateId(); + const clientAssertionJti = generateId(); + + return { + correlationId, + eserviceId, + descriptorId, + agreementId, + subject: clientId, + audience: "pagopa.it", + purposeId, + algorithm: "RS256", + clientId, + keyId: kid, + purposeVersionId, + jwtId: generateId(), + issuedAt: dateToSeconds(new Date()), + issuer: "interop jwt issuer", + expirationTime: dateToSeconds(new Date()), + organizationId: consumerId, + notBefore: 0, + clientAssertion: { + subject: clientId, + audience: "pagopa.it", + algorithm: "RS256", + keyId: kid, + jwtId: clientAssertionJti, + issuedAt: dateToSeconds(new Date()), + issuer: consumerId, + expirationTime: dateToSeconds(new Date()), + }, + }; +}; diff --git a/packages/authorization-server/test/vitestGlobalSetup.ts b/packages/authorization-server/test/vitestGlobalSetup.ts new file mode 100644 index 0000000000..32972b5c32 --- /dev/null +++ b/packages/authorization-server/test/vitestGlobalSetup.ts @@ -0,0 +1,3 @@ +import { setupTestContainersVitestGlobal } from "pagopa-interop-commons-test"; + +export default setupTestContainersVitestGlobal(); diff --git a/packages/authorization-server/tsconfig.check.json b/packages/authorization-server/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/authorization-server/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/authorization-server/tsconfig.json b/packages/authorization-server/tsconfig.json new file mode 100644 index 0000000000..039e0b4d16 --- /dev/null +++ b/packages/authorization-server/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/packages/authorization-server/vitest.config.ts b/packages/authorization-server/vitest.config.ts new file mode 100644 index 0000000000..d1a4f22cc5 --- /dev/null +++ b/packages/authorization-server/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globalSetup: ["./test/vitestGlobalSetup.ts"], + testTimeout: 60000, + hookTimeout: 60000, + fileParallelism: false, + pool: "forks" + }, +}); diff --git a/packages/backend-for-frontend/.env b/packages/backend-for-frontend/.env index 33fe161b6b..4457ca94c7 100644 --- a/packages/backend-for-frontend/.env +++ b/packages/backend-for-frontend/.env @@ -80,4 +80,4 @@ IMPORT_ESERVICE_PATH="local/eservices-import" PRESIGNED_URL_GET_DURATION_MINUTES=5000 PRESIGNED_URL_PUT_DURATION_MINUTES= 5000 -CLIENT_ASSERTION_AUDIENCE="auth.refactor.dev.interop.pagopa.it/client-assertion" +CLIENT_ASSERTION_AUDIENCE="dev.interop.pagopa.it" diff --git a/packages/backend-for-frontend/src/services/toolService.ts b/packages/backend-for-frontend/src/services/toolService.ts index d286488c7a..d6e851a0ea 100644 --- a/packages/backend-for-frontend/src/services/toolService.ts +++ b/packages/backend-for-frontend/src/services/toolService.ts @@ -2,9 +2,6 @@ import { isAxiosError } from "axios"; import { - ApiKey, - ClientAssertion, - ConsumerKey, FailedValidation, SuccessfulValidation, validateClientKindAndPlatformState, @@ -15,11 +12,21 @@ import { import { AgreementId, ApiError, + ClientAssertion, ClientId, + DescriptorId, EServiceId, + GSIPKKid, ItemState, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, PurposeId, TenantId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, unsafeBrandId, } from "pagopa-interop-models"; import { WithLogger } from "pagopa-interop-commons"; @@ -96,7 +103,11 @@ export function toolsServiceBuilder(clients: PagoPAInteropBeClients) { : undefined; const { errors: clientAssertionSignatureErrors } = - await verifyClientAssertionSignature(clientAssertion, key); + await verifyClientAssertionSignature( + clientAssertion, + key, + jwt.header.alg + ); if (clientAssertionSignatureErrors) { return handleValidationResults( { @@ -209,7 +220,9 @@ async function retrieveKeyAndEservice( ctx: WithLogger ): Promise< | SuccessfulValidation<{ - key: ApiKey | ConsumerKey; + key: + | TokenGenerationStatesClientEntry + | TokenGenerationStatesClientPurposeEntry; eservice?: catalogApi.EService; descriptor?: catalogApi.EServiceDescriptor; }> @@ -241,26 +254,29 @@ async function retrieveKeyAndEservice( assertIsConsumer(ctx.authData.organizationId, keyWithClient); - const { encodedPem, algorithm } = - await authorizationClient.client.getClientKeyById({ - headers: ctx.headers, - params: { - clientId: keyWithClient.client.id, - keyId: jwt.header.kid, - }, - }); + const { encodedPem } = await authorizationClient.client.getClientKeyById({ + headers: ctx.headers, + params: { + clientId: keyWithClient.client.id, + keyId: jwt.header.kid, + }, + }); if (keyWithClient.client.kind === authorizationApi.ClientKind.enum.API) { return { errors: undefined, data: { key: { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: unsafeBrandId(keyWithClient.client.id), + kid: jwt.header.kid, + }), clientKind: authorizationApi.ClientKind.enum.API, - kid: jwt.header.kid, - algorithm, + GSIPK_kid: unsafeBrandId(jwt.header.kid), publicKey: encodedPem, - clientId: unsafeBrandId(keyWithClient.client.id), + GSIPK_clientId: unsafeBrandId(keyWithClient.client.id), consumerId: unsafeBrandId(keyWithClient.client.consumerId), + updatedAt: new Date().toISOString(), }, }, }; @@ -311,18 +327,36 @@ async function retrieveKeyAndEservice( errors: undefined, data: { key: { + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: unsafeBrandId(keyWithClient.client.id), + kid: jwt.header.kid, + purposeId, + }), clientKind: authorizationApi.ClientKind.enum.CONSUMER, - clientId: unsafeBrandId(keyWithClient.client.id), - kid: jwt.header.kid, - algorithm, + GSIPK_clientId: unsafeBrandId(keyWithClient.client.id), + GSIPK_kid: unsafeBrandId(jwt.header.kid), publicKey: encodedPem, - purposeId, + GSIPK_purposeId: purposeId, consumerId: unsafeBrandId(keyWithClient.client.consumerId), agreementId: unsafeBrandId(agreement.id), - eServiceId: unsafeBrandId(agreement.eserviceId), + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: unsafeBrandId(agreement.eserviceId), + descriptorId: unsafeBrandId(agreement.descriptorId), + }), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + eserviceId: unsafeBrandId(agreement.eserviceId), + consumerId: unsafeBrandId(keyWithClient.client.consumerId), + }), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: unsafeBrandId(keyWithClient.client.id), + purposeId, + }), agreementState: agreementStateToItemState(agreement.state), - purposeState: retrievePurposeItemState(purpose), + purposeState: purposeToItemState(purpose), descriptorState: descriptorStateToItemState(descriptor.state), + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + updatedAt: new Date().toISOString(), }, eservice, descriptor, @@ -374,7 +408,7 @@ async function retrieveDescriptor( return descriptor; } -function retrievePurposeItemState(purpose: purposeApi.Purpose): ItemState { +function purposeToItemState(purpose: purposeApi.Purpose): ItemState { const purposeVersion = [...purpose.versions] .sort( (a, b) => @@ -416,10 +450,10 @@ const agreementStateToItemState = ( : ItemState.Enum.INACTIVE; const descriptorStateToItemState = ( - state: catalogApi.EServiceDescriptorState + descriptorState: catalogApi.EServiceDescriptorState ): ItemState => - state === catalogApi.EServiceDescriptorState.Enum.PUBLISHED || - state === catalogApi.EServiceDescriptorState.Enum.DEPRECATED + descriptorState === catalogApi.EServiceDescriptorState.Enum.PUBLISHED || + descriptorState === catalogApi.EServiceDescriptorState.Enum.DEPRECATED ? ItemState.Enum.ACTIVE : ItemState.Enum.INACTIVE; diff --git a/packages/client-assertion-validation/src/errors.ts b/packages/client-assertion-validation/src/errors.ts index 1ba5d7589b..1f1617a1f7 100644 --- a/packages/client-assertion-validation/src/errors.ts +++ b/packages/client-assertion-validation/src/errors.ts @@ -1,55 +1,45 @@ import { ApiError } from "pagopa-interop-models"; export const errorCodes = { - clientAssertionValidationFailure: "0001", - unexpectedClientAssertionSignatureVerificationError: "0002", - invalidAssertionType: "0003", - invalidGrantType: "0004", - invalidAudienceFormat: "0005", - invalidAudience: "0006", - audienceNotFound: "0007", - invalidClientAssertionFormat: "0008", - unexpectedClientAssertionPayload: "0009", - jtiNotFound: "00010", - issuedAtNotFound: "0011", - expNotFound: "0012", - issuerNotFound: "0013", - subjectNotFound: "0014", - invalidSubject: "0015", - invalidPurposeIdClaimFormat: "0016", - kidNotFound: "0017", - clientAssertionSignatureVerificationError: "0018", - tokenExpiredError: "0019", - jsonWebTokenError: "0020", - notBeforeError: "0021", - inactivePurpose: "0022", - inactiveAgreement: "0023", - inactiveEService: "0024", - invalidClientIdFormat: "0025", - invalidSubjectFormat: "0026", - digestClaimNotFound: "0027", - invalidHashLength: "0028", - invalidHashAlgorithm: "0029", - algorithmNotFound: "0030", - algorithmNotAllowed: "0031", - purposeIdNotProvided: "0032", - invalidKidFormat: "0033", - clientAssertionInvalidClaims: "0034", - invalidSignature: "0035", + unexpectedClientAssertionSignatureVerificationError: "0001", + invalidAssertionType: "0002", + invalidGrantType: "0003", + invalidAudienceFormat: "0004", + invalidAudience: "0005", + audienceNotFound: "0006", + invalidClientAssertionFormat: "0007", + unexpectedClientAssertionPayload: "0008", + jtiNotFound: "0009", + issuedAtNotFound: "0010", + expNotFound: "0011", + issuerNotFound: "0012", + subjectNotFound: "0013", + invalidSubject: "0014", + invalidPurposeIdClaimFormat: "0015", + kidNotFound: "0016", + clientAssertionSignatureVerificationError: "0017", + tokenExpiredError: "0018", + jsonWebTokenError: "0019", + notBeforeError: "0020", + inactivePurpose: "0021", + inactiveAgreement: "0022", + inactiveEService: "0023", + invalidClientIdFormat: "0024", + invalidSubjectFormat: "0025", + digestClaimNotFound: "0026", + invalidHashLength: "0027", + invalidHashAlgorithm: "0028", + algorithmNotFound: "0029", + algorithmNotAllowed: "0030", + purposeIdNotProvided: "0031", + invalidKidFormat: "0032", + clientAssertionInvalidClaims: "0033", + invalidSignature: "0034", + missingPlatformStates: "0035", }; export type ErrorCodes = keyof typeof errorCodes; -export function clientAssertionValidationFailure( - details: string -): ApiError { - return new ApiError({ - detail: `Client assertion validation failed: ${details}`, - code: "clientAssertionValidationFailure", - title: "Client assertion validation failed", - }); -} - export function unexpectedClientAssertionSignatureVerificationError( message: string ): ApiError { @@ -336,3 +326,11 @@ export function invalidSignature(): ApiError { title: "Invalid signature", }); } + +export function missingPlatformStates(): ApiError { + return new ApiError({ + detail: "Platform states not available for the entry", + code: "missingPlatformStates", + title: "Missing platform states", + }); +} diff --git a/packages/client-assertion-validation/src/types.ts b/packages/client-assertion-validation/src/types.ts index 38a8729a83..4d9020e990 100644 --- a/packages/client-assertion-validation/src/types.ts +++ b/packages/client-assertion-validation/src/types.ts @@ -1,82 +1,8 @@ -import { - AgreementId, - ApiError, - ClientId, - clientKindTokenStates, - EServiceId, - ItemState, - PurposeId, - TenantId, -} from "pagopa-interop-models"; +import { ApiError } from "pagopa-interop-models"; import { z } from "zod"; import { ErrorCodes } from "./errors.js"; -export const ClientAssertionDigest = z - .object({ - alg: z.string(), - value: z.string(), - }) - .strict(); -export type ClientAssertionDigest = z.infer; - -export const ClientAssertionHeader = z - .object({ - kid: z.string(), - alg: z.string(), - typ: z.string().optional(), - }) - .strict(); -export type ClientAssertionHeader = z.infer; - -export const ClientAssertionPayload = z - .object({ - sub: ClientId, - jti: z.string(), - iat: z.number(), - iss: z.string(), - aud: z.array(z.string()).or(z.string()), - exp: z.number(), - digest: ClientAssertionDigest.optional(), - purposeId: PurposeId.optional(), - }) - .strict(); -export type ClientAssertionPayload = z.infer; - -export const ClientAssertion = z - .object({ - header: ClientAssertionHeader, - payload: ClientAssertionPayload, - }) - .strict(); -export type ClientAssertion = z.infer; - export const Base64Encoded = z.string().base64().min(1); -export const Key = z - .object({ - clientId: ClientId, - consumerId: TenantId, - kid: z.string(), - publicKey: Base64Encoded, - algorithm: z.string(), - }) - .strict(); -export type Key = z.infer; - -export const ConsumerKey = Key.extend({ - clientKind: z.literal(clientKindTokenStates.consumer), - purposeId: PurposeId, - purposeState: ItemState, - agreementId: AgreementId, - agreementState: ItemState, - eServiceId: EServiceId, - descriptorState: ItemState, -}).strict(); -export type ConsumerKey = z.infer; - -export const ApiKey = Key.extend({ - clientKind: z.literal(clientKindTokenStates.api), -}).strict(); -export type ApiKey = z.infer; export type ValidationResult = | SuccessfulValidation diff --git a/packages/client-assertion-validation/src/utils.ts b/packages/client-assertion-validation/src/utils.ts index 6bfa958313..44f16d1692 100644 --- a/packages/client-assertion-validation/src/utils.ts +++ b/packages/client-assertion-validation/src/utils.ts @@ -3,11 +3,11 @@ import { ClientId, itemState, PurposeId, + TokenGenerationStatesClientPurposeEntry, unsafeBrandId, + ClientAssertionDigest, } from "pagopa-interop-models"; import { - ClientAssertionDigest, - ConsumerKey, FailedValidation, ValidationResult, SuccessfulValidation, @@ -190,8 +190,8 @@ export const validateDigest = ( }; export const validatePlatformState = ( - key: ConsumerKey -): ValidationResult => { + key: TokenGenerationStatesClientPurposeEntry +): ValidationResult => { const agreementError = key.agreementState !== itemState.active ? inactiveAgreement() : undefined; diff --git a/packages/client-assertion-validation/src/validation.ts b/packages/client-assertion-validation/src/validation.ts index 6db2e9f4ac..27041566c5 100644 --- a/packages/client-assertion-validation/src/validation.ts +++ b/packages/client-assertion-validation/src/validation.ts @@ -1,5 +1,13 @@ import { match } from "ts-pattern"; -import { clientKindTokenStates } from "pagopa-interop-models"; +import { + clientKidPurposePrefix, + clientKindTokenStates, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, + ClientAssertion, + ClientAssertionHeader, + ClientAssertionPayload, +} from "pagopa-interop-models"; import * as jose from "jose"; import { JOSEError, @@ -29,14 +37,8 @@ import { ALLOWED_ALGORITHM, } from "./utils.js"; import { - ApiKey, Base64Encoded, - ClientAssertion, - ClientAssertionHeader, - ClientAssertionPayload, ClientAssertionValidationRequest, - ConsumerKey, - Key, ValidationResult, } from "./types.js"; import { @@ -53,6 +55,7 @@ import { clientAssertionInvalidClaims, algorithmNotAllowed, clientAssertionSignatureVerificationError, + missingPlatformStates, } from "./errors.js"; export const validateRequestParameters = ( @@ -184,11 +187,14 @@ export const verifyClientAssertion = ( export const verifyClientAssertionSignature = async ( clientAssertionJws: string, - key: Key + key: + | TokenGenerationStatesClientPurposeEntry + | TokenGenerationStatesClientEntry, + clientAssertionAlgorithm: string ): Promise> => { try { - if (key.algorithm !== ALLOWED_ALGORITHM) { - return failedValidation([algorithmNotAllowed(key.algorithm)]); + if (clientAssertionAlgorithm !== ALLOWED_ALGORITHM) { + return failedValidation([algorithmNotAllowed(clientAssertionAlgorithm)]); } if (!Base64Encoded.safeParse(key.publicKey).success) { @@ -211,7 +217,7 @@ export const verifyClientAssertionSignature = async ( const publicKey = createPublicKey(key.publicKey); const result = await jose.jwtVerify(clientAssertionJws, publicKey, { - algorithms: [key.algorithm], + algorithms: [clientAssertionAlgorithm], }); return successfulValidation(result.payload); @@ -241,7 +247,9 @@ export const verifyClientAssertionSignature = async ( }; export const validateClientKindAndPlatformState = ( - key: ApiKey | ConsumerKey, + key: + | TokenGenerationStatesClientEntry + | TokenGenerationStatesClientPurposeEntry, jwt: ClientAssertion ): ValidationResult => match(key) @@ -249,14 +257,18 @@ export const validateClientKindAndPlatformState = ( successfulValidation(jwt) ) .with({ clientKind: clientKindTokenStates.consumer }, (key) => { - const { errors: platformStateErrors } = validatePlatformState(key); - const purposeIdError = jwt.payload.purposeId - ? undefined - : purposeIdNotProvided(); + if (key.PK.startsWith(clientKidPurposePrefix)) { + const parsed = key as TokenGenerationStatesClientPurposeEntry; + const { errors: platformStateErrors } = validatePlatformState(parsed); + const purposeIdError = jwt.payload.purposeId + ? undefined + : purposeIdNotProvided(); - if (!platformStateErrors && !purposeIdError) { - return successfulValidation(jwt); + if (!platformStateErrors && !purposeIdError) { + return successfulValidation(jwt); + } + return failedValidation([platformStateErrors, purposeIdError]); } - return failedValidation([platformStateErrors, purposeIdError]); + return failedValidation([missingPlatformStates()]); }) .exhaustive(); diff --git a/packages/client-assertion-validation/test/utils.ts b/packages/client-assertion-validation/test/utils.ts index 2ad7728e8f..4f35335375 100644 --- a/packages/client-assertion-validation/test/utils.ts +++ b/packages/client-assertion-validation/test/utils.ts @@ -1,19 +1,7 @@ import crypto from "crypto"; -import { - ClientId, - clientKindTokenStates, - generateId, - itemState, - PurposeId, - TenantId, -} from "pagopa-interop-models"; -import * as jose from "jose"; -import { - ApiKey, - ClientAssertionValidationRequest, - ConsumerKey, - Key, -} from ".././src/types.js"; +import { ClientId, generateId } from "pagopa-interop-models"; +import { getMockClientAssertion } from "pagopa-interop-commons-test"; +import { ClientAssertionValidationRequest } from ".././src/types.js"; import { EXPECTED_CLIENT_ASSERTION_TYPE, EXPECTED_CLIENT_CREDENTIALS_GRANT_TYPE, @@ -21,119 +9,6 @@ import { export const value64chars = crypto.randomBytes(32).toString("hex"); -export const getMockClientAssertion = async (props?: { - standardClaimsOverride?: Partial; - customClaims?: { [k: string]: unknown }; - customHeader?: { [k: string]: unknown }; -}): Promise<{ - jws: string; - publicKeyEncodedPem: string; -}> => { - const { keySet, publicKeyEncodedPem } = generateKeySet(); - - const clientId = generateId(); - const defaultPayload: jose.JWTPayload = { - iss: clientId, - sub: clientId, - aud: ["test.interop.pagopa.it", "dev.interop.pagopa.it"], - exp: 60, - jti: generateId(), - iat: 5, - }; - - const actualPayload: jose.JWTPayload = { - ...defaultPayload, - ...props?.standardClaimsOverride, - ...props?.customClaims, - }; - - const headers: jose.JWTHeaderParameters = { - alg: "RS256", - kid: "kid", - ...props?.customHeader, - }; - - const jws = await signClientAssertion({ - payload: actualPayload, - headers, - keySet, - }); - - return { - jws, - publicKeyEncodedPem, - }; -}; - -export const generateKeySet = (): { - keySet: crypto.KeyPairKeyObjectResult; - publicKeyEncodedPem: string; -} => { - const keySet: crypto.KeyPairKeyObjectResult = crypto.generateKeyPairSync( - "rsa", - { - modulusLength: 2048, - } - ); - - const pemPublicKey = keySet.publicKey - .export({ - type: "spki", - format: "pem", - }) - .toString(); - - const publicKeyEncodedPem = Buffer.from(pemPublicKey).toString("base64"); - return { - keySet, - publicKeyEncodedPem, - }; -}; - -const signClientAssertion = async ({ - payload, - headers, - keySet, -}: { - payload: jose.JWTPayload; - headers: jose.JWTHeaderParameters; - keySet: crypto.KeyPairKeyObjectResult; -}): Promise => { - const pemPrivateKey = keySet.privateKey.export({ - type: "pkcs8", - format: "pem", - }); - - const privateKey = crypto.createPrivateKey(pemPrivateKey); - return await new jose.SignJWT(payload) - .setProtectedHeader(headers) - .sign(privateKey); -}; - -export const getMockKey = (): Key => ({ - clientId: generateId(), - consumerId: generateId(), - kid: "kid", - publicKey: generateKeySet().publicKeyEncodedPem, - algorithm: "RS256", -}); - -export const getMockConsumerKey = (): ConsumerKey => ({ - ...getMockKey(), - purposeId: generateId(), - clientKind: clientKindTokenStates.consumer, - purposeState: itemState.active, - agreementId: generateId(), - agreementState: itemState.active, - eServiceId: generateId(), - descriptorState: itemState.active, -}); - -export const getMockApiKey = (): ApiKey => ({ - ...getMockKey(), - clientKind: clientKindTokenStates.api, -}); - export const getMockAccessTokenRequest = async (): Promise => ({ client_id: generateId(), diff --git a/packages/client-assertion-validation/test/validation.test.ts b/packages/client-assertion-validation/test/validation.test.ts index d698ffe1d0..22d1ced539 100644 --- a/packages/client-assertion-validation/test/validation.test.ts +++ b/packages/client-assertion-validation/test/validation.test.ts @@ -3,11 +3,21 @@ import { fail } from "assert"; import { describe, expect, it } from "vitest"; import { ClientId, + clientKindTokenStates, generateId, itemState, PurposeId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, } from "pagopa-interop-models"; import * as jsonwebtoken from "jsonwebtoken"; +import { + generateKeySet, + getMockClientAssertion, + getMockTokenStatesClientEntry, + getMockTokenStatesClientPurposeEntry, +} from "pagopa-interop-commons-test"; +import { dateToSeconds } from "pagopa-interop-commons"; import { validateClientKindAndPlatformState, validateRequestParameters, @@ -46,21 +56,10 @@ import { clientAssertionInvalidClaims, invalidAudienceFormat, unexpectedClientAssertionSignatureVerificationError, + missingPlatformStates, } from "../src/errors.js"; -import { - ClientAssertionValidationRequest, - ConsumerKey, - Key, -} from "../src/types.js"; -import { - generateKeySet, - getMockAccessTokenRequest, - getMockApiKey, - getMockClientAssertion, - getMockConsumerKey, - getMockKey, - value64chars, -} from "./utils.js"; +import { ClientAssertionValidationRequest } from "../src/types.js"; +import { getMockAccessTokenRequest, value64chars } from "./utils.js"; describe("validation test", async () => { describe("validateRequestParameters", async () => { @@ -488,26 +487,35 @@ describe("validation test", async () => { const { jws, publicKeyEncodedPem } = await getMockClientAssertion({ standardClaimsOverride: { - iat: new Date().getTime() / 1000, - exp: threeHourLater.getTime() / 1000, + iat: dateToSeconds(new Date()), + exp: dateToSeconds(threeHourLater), }, }); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; - const { errors } = await verifyClientAssertionSignature(jws, mockKey); + const { errors } = await verifyClientAssertionSignature( + jws, + mockKey, + "RS256" + ); expect(errors).toBeUndefined(); }); it("unexpectedClientAssertionSignatureVerificationError - base64 key expected", async () => { const { jws, publicKeyEncodedPem } = await getMockClientAssertion(); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: Buffer.from(publicKeyEncodedPem, "base64").toString("utf8"), }; - const { errors } = await verifyClientAssertionSignature(jws, mockKey); + + const { errors } = await verifyClientAssertionSignature( + jws, + mockKey, + "RS256" + ); expect(errors).toHaveLength(1); expect(errors![0]).toEqual( unexpectedClientAssertionSignatureVerificationError( @@ -527,16 +535,20 @@ describe("validation test", async () => { alg: notAllowedAlg, }, standardClaimsOverride: { - iat: new Date().getTime() / 1000, - exp: threeHourLater.getTime() / 1000, + iat: dateToSeconds(new Date()), + exp: dateToSeconds(threeHourLater), }, }); - const mockKey: Key = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, - algorithm: notAllowedAlg, }; - const { errors } = await verifyClientAssertionSignature(jws, mockKey); + + const { errors } = await verifyClientAssertionSignature( + jws, + mockKey, + notAllowedAlg + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(algorithmNotAllowed(notAllowedAlg)); @@ -551,29 +563,35 @@ describe("validation test", async () => { const { jws, publicKeyEncodedPem } = await getMockClientAssertion({ standardClaimsOverride: { - iat: sixHoursAgo.getTime() / 1000, - exp: threeHourAgo.getTime() / 1000, + iat: dateToSeconds(sixHoursAgo), + exp: dateToSeconds(threeHourAgo), }, }); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; - const { errors } = await verifyClientAssertionSignature(jws, mockKey); + const { errors } = await verifyClientAssertionSignature( + jws, + mockKey, + "RS256" + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(tokenExpiredError()); }); it("jsonWebTokenError", async () => { const { publicKeyEncodedPem } = generateKeySet(); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; + const { errors } = await verifyClientAssertionSignature( "not-a-valid-jws", - mockKey + mockKey, + "RS256" ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); @@ -582,17 +600,19 @@ describe("validation test", async () => { it("invalidSignature", async () => { const { publicKeyEncodedPem } = generateKeySet(); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; + const { jws } = await getMockClientAssertion(); const subStrings = jws.split("."); const clientAssertionWithWrongSignature = `${subStrings[0]}.${subStrings[1]}.wrong-signature`; const { errors } = await verifyClientAssertionSignature( clientAssertionWithWrongSignature, - mockKey + mockKey, + "RS256" ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); @@ -600,13 +620,15 @@ describe("validation test", async () => { }); it("jsonWebTokenError - malformed jwt", async () => { const { publicKeyEncodedPem } = generateKeySet(); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; + const { errors } = await verifyClientAssertionSignature( "too.many.substrings.in.client.assertion", - mockKey + mockKey, + "RS256" ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); @@ -617,8 +639,8 @@ describe("validation test", async () => { const { jws: clientAssertion1, publicKeyEncodedPem } = await getMockClientAssertion(); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; @@ -629,7 +651,8 @@ describe("validation test", async () => { const clientAssertionWithWrongSignature = `${subStrings1[0]}.${subStrings1[1]}.${subStrings2[2]}`; const { errors } = await verifyClientAssertionSignature( clientAssertionWithWrongSignature, - mockKey + mockKey, + "RS256" ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); @@ -648,17 +671,21 @@ describe("validation test", async () => { const { jws, publicKeyEncodedPem } = await getMockClientAssertion({ standardClaimsOverride: { - iat: threeHoursAgo.getTime() / 1000, - exp: sixHoursLater.getTime() / 1000, - nbf: threeHoursLater.getTime() / 1000, + iat: dateToSeconds(threeHoursAgo), + exp: dateToSeconds(sixHoursLater), + nbf: dateToSeconds(threeHoursLater), }, }); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; - const { errors } = await verifyClientAssertionSignature(jws, mockKey); + const { errors } = await verifyClientAssertionSignature( + jws, + mockKey, + "RS256" + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(notBeforeError()); @@ -671,11 +698,8 @@ describe("validation test", async () => { describe("validatePlatformState", async () => { it("success", async () => { - const mockKey: ConsumerKey = { - ...getMockConsumerKey(), - agreementState: itemState.active, - descriptorState: itemState.active, - purposeState: itemState.active, + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), }; validatePlatformState(mockKey); const { errors } = validatePlatformState(mockKey); @@ -683,8 +707,8 @@ describe("validation test", async () => { }); it("inactiveAgreement", async () => { - const mockKey: ConsumerKey = { - ...getMockConsumerKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), agreementState: itemState.inactive, }; validatePlatformState(mockKey); @@ -695,9 +719,11 @@ describe("validation test", async () => { expect(errors![0]).toEqual(inactiveAgreement()); }); it("inactiveEservice", async () => { - const mockKey: ConsumerKey = { - ...getMockConsumerKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), descriptorState: itemState.inactive, + descriptorAudience: ["test.interop.pagopa.it"], + descriptorVoucherLifespan: 60, }; validatePlatformState(mockKey); const { errors } = validatePlatformState(mockKey); @@ -707,10 +733,11 @@ describe("validation test", async () => { expect(errors![0]).toEqual(inactiveEService()); }); it("inactivePurpose", async () => { - const mockKey: ConsumerKey = { - ...getMockConsumerKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), purposeState: itemState.inactive, }; + validatePlatformState(mockKey); const { errors } = validatePlatformState(mockKey); @@ -719,8 +746,8 @@ describe("validation test", async () => { expect(errors![0]).toEqual(inactivePurpose()); }); it("inactiveAgreement and inactiveEservice and inactivePurpose", async () => { - const mockKey: ConsumerKey = { - ...getMockConsumerKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), agreementState: itemState.inactive, descriptorState: itemState.inactive, purposeState: itemState.inactive, @@ -739,8 +766,8 @@ describe("validation test", async () => { }); describe("validateClientKindAndPlatformState", async () => { - it("success (consumerKey with consumer client kind; valid platform states)", async () => { - const mockConsumerKey = getMockConsumerKey(); + it("success (clientKidPurpose entry with consumer client kind; valid platform states)", async () => { + const mockConsumerKey = getMockTokenStatesClientPurposeEntry(); const { data: mockClientAssertion } = verifyClientAssertion( ( await getMockClientAssertion({ @@ -760,8 +787,8 @@ describe("validation test", async () => { }); it("inactiveEService (consumerKey with consumer client kind; invalid platform states)", async () => { - const mockConsumerKey: ConsumerKey = { - ...getMockConsumerKey(), + const mockConsumerKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), descriptorState: itemState.inactive, }; const { data: mockClientAssertion } = verifyClientAssertion( @@ -784,8 +811,11 @@ describe("validation test", async () => { expect(errors![0]).toEqual(inactiveEService()); }); - it("success (apiKey with api client kind)", async () => { - const mockApiKey = getMockApiKey(); + it("success (clientEntry with api client kind)", async () => { + const mockApiKey: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + clientKind: clientKindTokenStates.api, + }; const { data: mockClientAssertion } = verifyClientAssertion( (await getMockClientAssertion()).jws, undefined @@ -800,8 +830,29 @@ describe("validation test", async () => { expect(errors).toBeUndefined(); }); + it("missingPlatformStates (clientEntry with consumer client kind)", async () => { + const mockApiKey: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + clientKind: clientKindTokenStates.consumer, + }; + const { data: mockClientAssertion } = verifyClientAssertion( + (await getMockClientAssertion()).jws, + undefined + ); + if (!mockClientAssertion) { + fail(); + } + const { errors } = validateClientKindAndPlatformState( + mockApiKey, + mockClientAssertion + ); + expect(errors).toBeDefined(); + expect(errors).toHaveLength(1); + expect(errors![0]).toEqual(missingPlatformStates()); + }); + it("purposeIdNotProvided for Client Kind Consumer", async () => { - const mockConsumerKey = getMockConsumerKey(); + const mockConsumerKey = getMockTokenStatesClientPurposeEntry(); const { data: mockClientAssertion } = verifyClientAssertion( ( await getMockClientAssertion({ @@ -823,8 +874,8 @@ describe("validation test", async () => { }); it("purposeIdNotProvided and platformStateError", async () => { - const mockConsumerKey: ConsumerKey = { - ...getMockConsumerKey(), + const mockConsumerKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), agreementState: itemState.inactive, }; const { data: mockClientAssertion } = verifyClientAssertion( diff --git a/packages/commons-test/package.json b/packages/commons-test/package.json index 55e91be360..d474e20cba 100644 --- a/packages/commons-test/package.json +++ b/packages/commons-test/package.json @@ -31,6 +31,7 @@ "aws-sdk-client-mock": "4.0.1", "axios": "1.7.4", "dotenv-flow": "4.1.0", + "jose": "5.9.4", "jsonwebtoken": "9.0.2", "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index dbb0143273..2d8a8d7692 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -1,3 +1,4 @@ +import crypto from "crypto"; import { fail } from "assert"; import { generateMock } from "@anatine/zod-mock"; import { @@ -67,9 +68,11 @@ import { PlatformStatesClientPK, PlatformStatesClientEntry, makePlatformStatesClientPK, + unsafeBrandId, } from "pagopa-interop-models"; -import { AuthData } from "pagopa-interop-commons"; +import { AuthData, dateToSeconds } from "pagopa-interop-commons"; import { z } from "zod"; +import * as jose from "jose"; import { match } from "ts-pattern"; export function expectPastTimestamp(timestamp: bigint): boolean { @@ -419,7 +422,9 @@ export const getMockDelegationDocument = ( export const getMockTokenStatesClientPurposeEntry = ( tokenStateEntryPK?: TokenGenerationStatesClientKidPurposePK ): TokenGenerationStatesClientPurposeEntry => { - const clientId = generateId(); + const clientId = tokenStateEntryPK + ? unsafeBrandId(tokenStateEntryPK.split("#")[1]) + : generateId(); const purposeId = generateId(); const consumerId = generateId(); const eserviceId = generateId(); @@ -436,7 +441,7 @@ export const getMockTokenStatesClientPurposeEntry = ( kid, purposeId, }), - descriptorState: itemState.inactive, + descriptorState: itemState.active, descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], descriptorVoucherLifespan: 60, updatedAt: new Date().toISOString(), @@ -457,7 +462,7 @@ export const getMockTokenStatesClientPurposeEntry = ( descriptorId, }), GSIPK_purposeId: purposeId, - purposeState: itemState.inactive, + purposeState: itemState.active, GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId, purposeId, @@ -486,7 +491,10 @@ export const getMockAgreementEntry = ( export const getMockTokenStatesClientEntry = ( tokenStateEntryPK?: TokenGenerationStatesClientKidPK ): TokenGenerationStatesClientEntry => { - const clientId = generateId(); + const clientId = tokenStateEntryPK + ? unsafeBrandId(tokenStateEntryPK.split("#")[1]) + : generateId(); + const consumerId = generateId(); const kid = `kid ${Math.random()}`; @@ -517,3 +525,103 @@ export const getMockPlatformStatesClientEntry = ( clientConsumerId: generateId(), clientPurposesIds: [], }); + +export const getMockClientAssertion = async (props?: { + standardClaimsOverride?: Partial; + customClaims?: { [k: string]: unknown }; + customHeader?: { [k: string]: unknown }; +}): Promise<{ + jws: string; + clientAssertion: { + payload: jose.JWTPayload; + header: jose.JWTHeaderParameters; + }; + publicKeyEncodedPem: string; +}> => { + const { keySet, publicKeyEncodedPem } = generateKeySet(); + + const threeHourLater = new Date(); + threeHourLater.setHours(threeHourLater.getHours() + 3); + + const clientId = generateId(); + const defaultPayload: jose.JWTPayload = { + iss: clientId, + sub: clientId, + aud: ["test.interop.pagopa.it", "dev.interop.pagopa.it"], + exp: dateToSeconds(threeHourLater), + jti: generateId(), + iat: dateToSeconds(new Date()), + }; + + const actualPayload: jose.JWTPayload = { + ...defaultPayload, + ...props?.standardClaimsOverride, + ...props?.customClaims, + }; + + const headers: jose.JWTHeaderParameters = { + alg: "RS256", + kid: "kid", + ...props?.customHeader, + }; + + const jws = await signClientAssertion({ + payload: actualPayload, + headers, + keySet, + }); + + return { + jws, + clientAssertion: { + payload: actualPayload, + header: headers, + }, + publicKeyEncodedPem, + }; +}; + +export const generateKeySet = (): { + keySet: crypto.KeyPairKeyObjectResult; + publicKeyEncodedPem: string; +} => { + const keySet: crypto.KeyPairKeyObjectResult = crypto.generateKeyPairSync( + "rsa", + { + modulusLength: 2048, + } + ); + + const pemPublicKey = keySet.publicKey + .export({ + type: "spki", + format: "pem", + }) + .toString(); + + const publicKeyEncodedPem = Buffer.from(pemPublicKey).toString("base64"); + return { + keySet, + publicKeyEncodedPem, + }; +}; + +const signClientAssertion = async ({ + payload, + headers, + keySet, +}: { + payload: jose.JWTPayload; + headers: jose.JWTHeaderParameters; + keySet: crypto.KeyPairKeyObjectResult; +}): Promise => { + const pemPrivateKey = keySet.privateKey.export({ + type: "pkcs8", + format: "pem", + }); + + const privateKey = crypto.createPrivateKey(pemPrivateKey); + return await new jose.SignJWT(payload) + .setProtectedHeader(headers) + .sign(privateKey); +}; diff --git a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts index 18f5e7df98..ca231469ad 100644 --- a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts +++ b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts @@ -20,10 +20,46 @@ import { PlatformStatesPurposeEntry, PlatformStatesAgreementEntry, TokenGenerationStatesGenericEntry, + TokenGenerationStatesClientEntry, } from "pagopa-interop-models"; import { unmarshall } from "@aws-sdk/util-dynamodb"; import { z } from "zod"; +export const writeTokenStateClientEntry = async ( + tokenStateEntry: TokenGenerationStatesClientEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: tokenStateEntry.PK, + }, + updatedAt: { + S: tokenStateEntry.updatedAt, + }, + consumerId: { + S: tokenStateEntry.consumerId, + }, + clientKind: { + S: tokenStateEntry.clientKind, + }, + publicKey: { + S: tokenStateEntry.publicKey, + }, + GSIPK_clientId: { + S: tokenStateEntry.GSIPK_clientId, + }, + GSIPK_kid: { + S: tokenStateEntry.GSIPK_kid, + }, + }, + TableName: "token-generation-states", + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + export const writeTokenStateEntry = async ( tokenStateEntry: TokenGenerationStatesClientPurposeEntry, dynamoDBClient: DynamoDBClient diff --git a/packages/commons/src/config/authorizationServerTokenGenerationConfig.ts b/packages/commons/src/config/authorizationServerTokenGenerationConfig.ts new file mode 100644 index 0000000000..405d6999eb --- /dev/null +++ b/packages/commons/src/config/authorizationServerTokenGenerationConfig.ts @@ -0,0 +1,22 @@ +import { z } from "zod"; + +export const AuthorizationServerTokenGenerationConfig = z + .object({ + GENERATED_INTEROP_TOKEN_KID: z.string(), + GENERATED_INTEROP_TOKEN_ISSUER: z.string(), + GENERATED_INTEROP_TOKEN_M2M_AUDIENCE: z.string(), + GENERATED_INTEROP_TOKEN_M2M_DURATION_SECONDS: z.string(), + }) + .transform((c) => ({ + generatedInteropTokenKid: c.GENERATED_INTEROP_TOKEN_KID, + generatedInteropTokenIssuer: c.GENERATED_INTEROP_TOKEN_ISSUER, + generatedInteropTokenM2MAudience: c.GENERATED_INTEROP_TOKEN_M2M_AUDIENCE, + generatedInteropTokenM2MDurationSeconds: parseInt( + c.GENERATED_INTEROP_TOKEN_M2M_DURATION_SECONDS, + 10 + ), + })); + +export type AuthorizationServerTokenGenerationConfig = z.infer< + typeof AuthorizationServerTokenGenerationConfig +>; diff --git a/packages/commons/src/config/index.ts b/packages/commons/src/config/index.ts index 7cba6068cc..6993d3b5d9 100644 --- a/packages/commons/src/config/index.ts +++ b/packages/commons/src/config/index.ts @@ -15,3 +15,4 @@ export * from "./sessionTokenGenerationConfig.js"; export * from "./redisRateLimiterConfig.js"; export * from "./pecEmailManagerConfig.js"; export * from "./selfcareConfig.js"; +export * from "./authorizationServerTokenGenerationConfig.js"; diff --git a/packages/commons/src/interop-token/interopTokenService.ts b/packages/commons/src/interop-token/interopTokenService.ts index b8088b4767..f4fec60d4f 100644 --- a/packages/commons/src/interop-token/interopTokenService.ts +++ b/packages/commons/src/interop-token/interopTokenService.ts @@ -1,12 +1,28 @@ import crypto from "crypto"; import { KMSClient, SignCommand, SignCommandInput } from "@aws-sdk/client-kms"; +import { + ClientId, + generateId, + PurposeId, + TenantId, + ClientAssertionDigest, +} from "pagopa-interop-models"; import { SessionTokenGenerationConfig } from "../config/sessionTokenGenerationConfig.js"; import { TokenGenerationConfig } from "../config/tokenGenerationConfig.js"; +import { AuthorizationServerTokenGenerationConfig } from "../config/authorizationServerTokenGenerationConfig.js"; +import { dateToSeconds } from "../utils/date.js"; import { CustomClaims, + GENERATED_INTEROP_TOKEN_M2M_ROLE, + InteropApiToken, + InteropConsumerToken, + InteropJwtApiPayload, + InteropJwtConsumerPayload, InteropJwtHeader, InteropJwtPayload, InteropToken, + ORGANIZATION_ID_CLAIM, + ROLE_CLAIM, SessionClaims, SessionJwtPayload, SessionToken, @@ -22,14 +38,26 @@ export class InteropTokenGenerator { private kmsClient: KMSClient; constructor( - private config: TokenGenerationConfig & - Partial + private config: Partial & + Partial & + Partial, + kmsClient?: KMSClient ) { - this.kmsClient = new KMSClient(); + this.kmsClient = kmsClient || new KMSClient(); } public async generateInternalToken(): Promise { - const currentTimestamp = Math.floor(Date.now() / 1000); + const currentTimestamp = dateToSeconds(new Date()); + + if ( + !this.config.kid || + !this.config.issuer || + !this.config.audience || + !this.config.subject || + !this.config.secondsDuration + ) { + throw Error("TokenGenerationConfig not provided or incomplete"); + } const header: InteropJwtHeader = { alg: JWT_HEADER_ALG, @@ -49,11 +77,11 @@ export class InteropTokenGenerator { [JWT_ROLE_CLAIM]: JWT_INTERNAL_ROLE, }; - const serializedToken = await this.createAndSignToken( + const serializedToken = await this.createAndSignToken({ header, payload, - this.config.kid - ); + keyId: this.config.kid, + }); return { header, @@ -75,7 +103,7 @@ export class InteropTokenGenerator { throw Error("SessionTokenGenerationConfig not provided or incomplete"); } - const currentTimestamp = Math.floor(Date.now() / 1000); + const currentTimestamp = dateToSeconds(new Date()); const header: InteropJwtHeader = { alg: JWT_HEADER_ALG, @@ -96,11 +124,122 @@ export class InteropTokenGenerator { ...claims, }; - const serializedToken = await this.createAndSignToken( + const serializedToken = await this.createAndSignToken({ + header, + payload, + keyId: this.config.generatedKid, + }); + + return { + header, + payload, + serialized: serializedToken, + }; + } + + public async generateInteropApiToken({ + sub, + consumerId, + }: { + sub: ClientId; + consumerId: TenantId; + }): Promise { + if ( + !this.config.generatedInteropTokenKid || + !this.config.generatedInteropTokenIssuer || + !this.config.generatedInteropTokenM2MAudience || + !this.config.generatedInteropTokenM2MDurationSeconds + ) { + throw Error( + "AuthorizationServerTokenGenerationConfig not provided or incomplete" + ); + } + + const currentTimestamp = Date.now(); + + const header: InteropJwtHeader = { + alg: "RS256", + use: "sig", + typ: "at+jwt", + kid: this.config.generatedInteropTokenKid, + }; + + const payload: InteropJwtApiPayload = { + jti: generateId(), + iss: this.config.generatedInteropTokenIssuer, + aud: [this.config.generatedInteropTokenM2MAudience], + sub, + iat: currentTimestamp, + nbf: currentTimestamp, + exp: + currentTimestamp + + this.config.generatedInteropTokenM2MDurationSeconds * 1000, + [ORGANIZATION_ID_CLAIM]: consumerId, + [ROLE_CLAIM]: GENERATED_INTEROP_TOKEN_M2M_ROLE, + }; + + const serializedToken = await this.createAndSignToken({ + header, + payload, + keyId: this.config.generatedInteropTokenKid, + }); + + return { + header, + payload, + serialized: serializedToken, + }; + } + + public async generateInteropConsumerToken({ + sub, + audience, + purposeId, + tokenDurationInSeconds, + digest, + }: { + sub: ClientId; + audience: string[]; + purposeId: PurposeId; + tokenDurationInSeconds: number; + digest: ClientAssertionDigest | undefined; + }): Promise { + if ( + !this.config.generatedInteropTokenKid || + !this.config.generatedInteropTokenIssuer || + !this.config.generatedInteropTokenM2MAudience + ) { + throw Error( + "AuthorizationServerTokenGenerationConfig not provided or incomplete" + ); + } + + const currentTimestamp = Date.now(); + + const header: InteropJwtHeader = { + alg: "RS256", + use: "sig", + typ: "at+jwt", + kid: this.config.generatedInteropTokenKid, + }; + + const payload: InteropJwtConsumerPayload = { + jti: generateId(), + iss: this.config.generatedInteropTokenIssuer, + aud: audience, + sub, + iat: currentTimestamp, + nbf: currentTimestamp, + exp: currentTimestamp + tokenDurationInSeconds, + purposeId, + ...(digest ? { digest } : {}), + }; + + const serializedToken = await this.createAndSignToken({ header, payload, - this.config.generatedKid - ); + keyId: this.config.generatedInteropTokenKid, + }); return { header, @@ -109,11 +248,15 @@ export class InteropTokenGenerator { }; } - private async createAndSignToken( - header: InteropJwtHeader, - payload: InteropJwtPayload | SessionJwtPayload, - keyId: string - ): Promise { + private async createAndSignToken({ + header, + payload, + keyId, + }: { + header: InteropJwtHeader; + payload: InteropJwtPayload | SessionJwtPayload | InteropJwtConsumerPayload; + keyId: string; + }): Promise { const serializedToken = `${b64UrlEncode( JSON.stringify(header) )}.${b64UrlEncode(JSON.stringify(payload))}`; diff --git a/packages/commons/src/interop-token/models.ts b/packages/commons/src/interop-token/models.ts index 45c16a577e..9054a5d483 100644 --- a/packages/commons/src/interop-token/models.ts +++ b/packages/commons/src/interop-token/models.ts @@ -1,3 +1,4 @@ +import { ClientAssertionDigest } from "pagopa-interop-models"; import { z } from "zod"; export const ORGANIZATION = "organization"; @@ -11,6 +12,9 @@ export const ORGANIZATION_EXTERNAL_ID_CLAIM = "externalId"; export const ORGANIZATION_EXTERNAL_ID_ORIGIN_CLAIM = "origin"; export const ORGANIZATION_EXTERNAL_ID_VALUE_CLAIM = "value"; export const USER_ROLES = "user-roles"; +const PURPOSE_ID_CLAIM = "purposeId"; +export const GENERATED_INTEROP_TOKEN_M2M_ROLE = "m2m"; +export const ROLE_CLAIM = "role"; export interface InteropJwtHeader { alg: string; @@ -28,6 +32,18 @@ export type InteropJwtCommonPayload = { exp: number; }; +export type InteropJwtConsumerPayload = InteropJwtCommonPayload & { + sub: string; + [PURPOSE_ID_CLAIM]: string; + digest?: ClientAssertionDigest; +}; + +export type InteropJwtApiPayload = InteropJwtCommonPayload & { + sub: string; + [ORGANIZATION_ID_CLAIM]: string; + [ROLE_CLAIM]: string; +}; + export type InteropJwtPayload = InteropJwtCommonPayload & { sub: string; role: string; @@ -39,6 +55,18 @@ export type InteropToken = { serialized: string; }; +export type InteropConsumerToken = { + header: InteropJwtHeader; + payload: InteropJwtConsumerPayload; + serialized: string; +}; + +export type InteropApiToken = { + header: InteropJwtHeader; + payload: InteropJwtApiPayload; + serialized: string; +}; + const Organization = z.object({ id: z.string(), name: z.string(), diff --git a/packages/commons/src/utils/date.ts b/packages/commons/src/utils/date.ts index 7d26258daf..8e4dcace25 100644 --- a/packages/commons/src/utils/date.ts +++ b/packages/commons/src/utils/date.ts @@ -13,6 +13,14 @@ export function formatDateyyyyMMddThhmmss(date: Date): string { return format(date, "yyyy-MM-dd'T'hh:mm:ss"); } +export function formatDateyyyyMMdd(date: Date): string { + return format(date, "yyyyMMdd"); +} + +export function formatTimehhmmss(date: Date): string { + return format(date, "hhmmss"); +} + export function dateAtRomeZone(date: Date): string { return formatInTimeZone(date, "Europe/Rome", "dd/MM/yyyy"); } @@ -20,3 +28,7 @@ export function dateAtRomeZone(date: Date): string { export function timeAtRomeZone(date: Date): string { return formatInTimeZone(date, "Europe/Rome", "HH:mm:ss"); } + +export function dateToSeconds(date: Date): number { + return Math.floor(date.getTime() / 1000); +} diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index 3da409a6e9..201c9c74dd 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -1,4 +1,4 @@ -import { randomUUID } from "crypto"; +import crypto from "crypto"; import { z } from "zod"; export const CorrelationId = z.string().brand("CorrelationId"); @@ -177,7 +177,7 @@ type IDS = // it infers the type of the ID based on how is used the result // the 'as' is used to cast the uuid string to the inferred type export function generateId(): T { - return randomUUID() as T; + return crypto.randomUUID() as T; } // This function is used to get a branded ID from a string diff --git a/packages/models/src/client-assertion/clientAssertionValidation.ts b/packages/models/src/client-assertion/clientAssertionValidation.ts new file mode 100644 index 0000000000..1a12ce3ca8 --- /dev/null +++ b/packages/models/src/client-assertion/clientAssertionValidation.ts @@ -0,0 +1,41 @@ +import { z } from "zod"; +import { ClientId, PurposeId } from "../brandedIds.js"; + +export const ClientAssertionDigest = z + .object({ + alg: z.string(), + value: z.string(), + }) + .strict(); +export type ClientAssertionDigest = z.infer; + +export const ClientAssertionHeader = z + .object({ + kid: z.string(), + alg: z.string(), + typ: z.string().optional(), + }) + .strict(); +export type ClientAssertionHeader = z.infer; + +export const ClientAssertionPayload = z + .object({ + sub: ClientId, + jti: z.string(), + iat: z.number(), + iss: z.string(), + aud: z.array(z.string()).or(z.string()), + exp: z.number(), + digest: ClientAssertionDigest.optional(), + purposeId: PurposeId.optional(), + }) + .strict(); +export type ClientAssertionPayload = z.infer; + +export const ClientAssertion = z + .object({ + header: ClientAssertionHeader, + payload: ClientAssertionPayload, + }) + .strict(); +export type ClientAssertion = z.infer; diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index 8e7aeee6e8..efba64d4d7 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -62,6 +62,8 @@ export * from "./user/user.js"; export * from "./token-generation-readmodel/platform-states-entry.js"; export * from "./token-generation-readmodel/token-generation-states-entry.js"; export * from "./token-generation-readmodel/commons.js"; +export * from "./token-generation-audit/audit.js"; +export * from "./client-assertion/clientAssertionValidation.js"; // Protobuf export * from "./protobuf/protobuf.js"; diff --git a/packages/models/src/token-generation-audit/audit.ts b/packages/models/src/token-generation-audit/audit.ts new file mode 100644 index 0000000000..b3376dc26d --- /dev/null +++ b/packages/models/src/token-generation-audit/audit.ts @@ -0,0 +1,48 @@ +import { z } from "zod"; +import { + AgreementId, + ClientId, + DescriptorId, + EServiceId, + PurposeId, + PurposeVersionId, + TenantId, +} from "../brandedIds.js"; + +export const ClientAssertionAuditDetails = z.object({ + jwtId: z.string(), + issuedAt: z.number(), + algorithm: z.string(), + keyId: z.string(), + issuer: z.string(), + subject: ClientId, + audience: z.string(), + expirationTime: z.number(), +}); +export type ClientAssertionAuditDetails = z.infer< + typeof ClientAssertionAuditDetails +>; + +export const GeneratedTokenAuditDetails = z.object({ + jwtId: z.string(), + correlationId: z.string(), + issuedAt: z.number(), + clientId: ClientId, + organizationId: TenantId, + agreementId: AgreementId, + eserviceId: EServiceId, + descriptorId: DescriptorId, + purposeId: PurposeId, + purposeVersionId: PurposeVersionId, + algorithm: z.string(), + keyId: z.string(), + audience: z.string(), + subject: z.string(), + notBefore: z.number(), + expirationTime: z.number(), + issuer: z.string(), + clientAssertion: ClientAssertionAuditDetails, +}); +export type GeneratedTokenAuditDetails = z.infer< + typeof GeneratedTokenAuditDetails +>; diff --git a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts index c36a56cf9a..1f82f59bf6 100644 --- a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts @@ -46,6 +46,12 @@ export type TokenGenerationStatesClientPurposeEntry = z.infer< typeof TokenGenerationStatesClientPurposeEntry >; +export const FullTokenGenerationStatesClientPurposeEntry = + TokenGenerationStatesClientPurposeEntry.required(); +export type FullTokenGenerationStatesClientPurposeEntry = z.infer< + typeof FullTokenGenerationStatesClientPurposeEntry +>; + export const TokenGenerationStatesClientEntry = TokenGenerationStatesBaseEntry.extend({ PK: TokenGenerationStatesClientKidPK, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1885d59312..81acb9ffd1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -808,6 +808,97 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) + packages/authorization-server: + dependencies: + '@aws-sdk/client-dynamodb': + specifier: 3.637.0 + version: 3.637.0 + '@aws-sdk/client-kms': + specifier: 3.600.0 + version: 3.600.0 + '@aws-sdk/util-dynamodb': + specifier: 3.637.0 + version: 3.637.0(@aws-sdk/client-dynamodb@3.637.0) + '@zodios/core': + specifier: 10.9.6 + version: 10.9.6(axios@1.7.4)(zod@3.23.8) + '@zodios/express': + specifier: 10.6.1 + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) + axios: + specifier: 1.7.4 + version: 1.7.4 + connection-string: + specifier: 4.4.0 + version: 4.4.0 + dotenv-flow: + specifier: 4.1.0 + version: 4.1.0 + express: + specifier: 4.20.0 + version: 4.20.0 + kafka-iam-auth: + specifier: workspace:* + version: link:../kafka-iam-auth + openapi-zod-client: + specifier: 1.18.1 + version: 1.18.1 + pagopa-interop-api-clients: + specifier: workspace:* + version: link:../api-clients + pagopa-interop-client-assertion-validation: + specifier: workspace:* + version: link:../client-assertion-validation + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-models: + specifier: workspace:* + version: link:../models + ts-pattern: + specifier: 5.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + '@types/express': + specifier: 4.17.21 + version: 4.17.21 + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + '@types/uuid': + specifier: 9.0.8 + version: 9.0.8 + jose: + specifier: 5.9.4 + version: 5.9.4 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + uuid: + specifier: 10.0.0 + version: 10.0.0 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + packages/authorization-updater: dependencies: '@protobuf-ts/runtime': @@ -1482,6 +1573,9 @@ importers: dotenv-flow: specifier: 4.1.0 version: 4.1.0 + jose: + specifier: 5.9.4 + version: 5.9.4 jsonwebtoken: specifier: 9.0.2 version: 9.0.2 @@ -4949,6 +5043,9 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/uuid@9.0.8': + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} @@ -7699,6 +7796,10 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true @@ -8093,8 +8194,8 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sso-oidc': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-endpoint-discovery': 3.598.0 @@ -8240,8 +8341,8 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sso-oidc': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-host-header': 3.598.0 @@ -8347,8 +8448,8 @@ snapshots: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sso-oidc': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-bucket-endpoint': 3.598.0 @@ -8455,8 +8556,8 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sso-oidc': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-host-header': 3.598.0 @@ -8499,11 +8600,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0)': + '@aws-sdk/client-sso-oidc@3.600.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-host-header': 3.598.0 @@ -8542,7 +8643,6 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: - - '@aws-sdk/client-sts' - aws-crt '@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)': @@ -9020,11 +9120,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sts@3.600.0': + '@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/client-sso-oidc': 3.600.0 '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-host-header': 3.598.0 @@ -9063,6 +9163,7 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' - aws-crt '@aws-sdk/client-sts@3.609.0': @@ -9393,7 +9494,7 @@ snapshots: '@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)': dependencies: - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/credential-provider-env': 3.598.0 '@aws-sdk/credential-provider-http': 3.598.0 '@aws-sdk/credential-provider-process': 3.598.0 @@ -9710,7 +9811,7 @@ snapshots: '@aws-sdk/credential-provider-web-identity@3.598.0(@aws-sdk/client-sts@3.600.0)': dependencies: - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.7 '@smithy/types': 3.5.0 @@ -10138,7 +10239,7 @@ snapshots: '@aws-sdk/token-providers@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)': dependencies: - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/client-sso-oidc': 3.600.0 '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.7 '@smithy/shared-ini-file-loader': 3.1.8 @@ -11993,6 +12094,8 @@ snapshots: '@types/triple-beam@1.3.5': {} + '@types/uuid@9.0.8': {} + '@types/webidl-conversions@7.0.3': {} '@types/whatwg-url@11.0.5': @@ -15196,6 +15299,8 @@ snapshots: utils-merge@1.0.1: {} + uuid@10.0.0: {} + uuid@9.0.1: {} vary@1.1.2: {} From ec0ad3e876ce7c5ce1c51acee4dbf6700179fdec Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 19 Nov 2024 11:31:14 +0100 Subject: [PATCH 31/34] IMN-798 Fix voucher lifespan in token-generation readmodel (#1177) Co-authored-by: shuyec <76391491+shuyec@users.noreply.github.com> --- .../consumerServiceV1.integration.test.ts | 2 +- .../src/consumerServiceV2.ts | 41 +- .../catalog-platformstate-writer/src/utils.ts | 125 ++++++ .../test/consumerServiceV1.test.ts | 14 +- .../test/consumerServiceV2.test.ts | 355 +++++++++++++++++- .../test/utils.ts | 10 +- 6 files changed, 516 insertions(+), 31 deletions(-) diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts index 339672bc55..67c1696479 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -158,7 +158,7 @@ describe("integration tests V1 events", async () => { ]) ); }); - it("should update the entry if the incoming version is more recent than existing table entry", async () => { + it("should update the entry if the incoming version is more recent than the existing table entry", async () => { const agreement: Agreement = { ...getMockAgreement(), state: agreementState.active, diff --git a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts index ff27b605a9..7043f7889e 100644 --- a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts @@ -20,6 +20,8 @@ import { readCatalogEntry, updateDescriptorStateInPlatformStatesEntry, updateDescriptorStateInTokenGenerationStatesTable, + updateDescriptorVoucherLifespanInPlatformStateEntry, + updateDescriptorVoucherLifespanInTokenGenerationStatesTable, writeCatalogEntry, } from "./utils.js"; @@ -178,6 +180,44 @@ export async function handleMessageV2( dynamoDBClient ); }) + .with({ type: "EServiceDescriptorQuotasUpdated" }, async (msg) => { + const { eservice, descriptor } = parseEServiceAndDescriptor( + msg.data.eservice, + unsafeBrandId(msg.data.descriptorId), + message.type + ); + const primaryKey = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }); + const catalogEntry = await readCatalogEntry(primaryKey, dynamoDBClient); + + if (!catalogEntry || catalogEntry.version > msg.version) { + return Promise.resolve(); + } else { + if ( + descriptor.voucherLifespan !== catalogEntry.descriptorVoucherLifespan + ) { + await updateDescriptorVoucherLifespanInPlatformStateEntry( + dynamoDBClient, + primaryKey, + descriptor.voucherLifespan, + msg.version + ); + + // token-generation-states + const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }); + await updateDescriptorVoucherLifespanInTokenGenerationStatesTable( + eserviceId_descriptorId, + descriptor.voucherLifespan, + dynamoDBClient + ); + } + } + }) .with( { type: "EServiceDeleted" }, { type: "EServiceAdded" }, @@ -186,7 +226,6 @@ export async function handleMessageV2( { type: "EServiceDescriptorAdded" }, { type: "EServiceDraftDescriptorDeleted" }, { type: "EServiceDraftDescriptorUpdated" }, - { type: "EServiceDescriptorQuotasUpdated" }, { type: "EServiceDescriptorInterfaceAdded" }, { type: "EServiceDescriptorDocumentAdded" }, { type: "EServiceDescriptorInterfaceUpdated" }, diff --git a/packages/catalog-platformstate-writer/src/utils.ts b/packages/catalog-platformstate-writer/src/utils.ts index e01e9d572c..924d39fd68 100644 --- a/packages/catalog-platformstate-writer/src/utils.ts +++ b/packages/catalog-platformstate-writer/src/utils.ts @@ -148,6 +148,39 @@ export const updateDescriptorStateInPlatformStatesEntry = async ( await dynamoDBClient.send(command); }; +export const updateDescriptorVoucherLifespanInPlatformStateEntry = async ( + dynamoDBClient: DynamoDBClient, + primaryKey: PlatformStatesEServiceDescriptorPK, + voucherLifespan: number, + version: number +): Promise => { + const input: UpdateItemInput = { + ConditionExpression: "attribute_exists(PK)", + Key: { + PK: { + S: primaryKey, + }, + }, + ExpressionAttributeValues: { + ":newVoucherLifespan": { + N: voucherLifespan.toString(), + }, + ":newVersion": { + N: version.toString(), + }, + ":newUpdateAt": { + S: new Date().toISOString(), + }, + }, + UpdateExpression: + "SET descriptorVoucherLifespan = :newVoucherLifespan, version = :newVersion, updatedAt = :newUpdateAt", + TableName: config.tokenGenerationReadModelTableNamePlatform, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); +}; + export const updateDescriptorStateInTokenGenerationStatesTable = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, descriptorState: ItemState, @@ -217,6 +250,67 @@ export const updateDescriptorStateInTokenGenerationStatesTable = async ( ); }; +export const updateDescriptorVoucherLifespanInTokenGenerationStatesTable = + async ( + eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, + voucherLifespan: number, + dynamoDBClient: DynamoDBClient + ): Promise => { + const runPaginatedQuery = async ( + eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "Descriptor", + KeyConditionExpression: `GSIPK_eserviceId_descriptorId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: eserviceId_descriptorId }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesClientPurposeEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + await updateDescriptorVoucherLifespanInTokenGenerationStatesEntries( + voucherLifespan, + dynamoDBClient, + tokenStateEntries.data + ); + + if (data.LastEvaluatedKey) { + await runPaginatedQuery( + eserviceId_descriptorId, + dynamoDBClient, + data.LastEvaluatedKey + ); + } + } + }; + + await runPaginatedQuery(eserviceId_descriptorId, dynamoDBClient, undefined); + }; + const updateDescriptorStateEntriesInTokenGenerationStatesTable = async ( descriptorState: ItemState, dynamoDBClient: DynamoDBClient, @@ -247,3 +341,34 @@ const updateDescriptorStateEntriesInTokenGenerationStatesTable = async ( await dynamoDBClient.send(command); } }; + +const updateDescriptorVoucherLifespanInTokenGenerationStatesEntries = async ( + voucherLifespan: number, + dynamoDBClient: DynamoDBClient, + entriesToUpdate: TokenGenerationStatesClientPurposeEntry[] +): Promise => { + for (const entry of entriesToUpdate) { + const input: UpdateItemInput = { + ConditionExpression: "attribute_exists(GSIPK_eserviceId_descriptorId)", + Key: { + PK: { + S: entry.PK, + }, + }, + ExpressionAttributeValues: { + ":newVoucherLifespan": { + N: voucherLifespan.toString(), + }, + ":newUpdateAt": { + S: new Date().toISOString(), + }, + }, + UpdateExpression: + "SET descriptorVoucherLifespan = :newVoucherLifespan, updatedAt = :newUpdateAt", + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); + } +}; diff --git a/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts b/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts index 9ba36ba2d5..74cc5351c4 100644 --- a/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts +++ b/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts @@ -38,7 +38,7 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { writeTokenStateEntry } from "pagopa-interop-commons-test"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; import { readCatalogEntry, writeCatalogEntry } from "../src/utils.js"; -import { config, sleep } from "./utils.js"; +import { config } from "./utils.js"; describe("V1 events", async () => { if (!config) { fail(); @@ -124,7 +124,6 @@ describe("V1 events", async () => { await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); - await sleep(1000, mockDate); const primaryKey = makePlatformStatesEServiceDescriptorPK({ eserviceId: eservice.id, @@ -169,7 +168,7 @@ describe("V1 events", async () => { ]) ); }); - it("(suspended -> published) should update the entry if incoming version is more recent than existing table entry", async () => { + it("(suspended -> published) should update the entry if the incoming version is more recent than the existing table entry", async () => { const publishedDescriptor: Descriptor = { ...getMockDescriptor(), audience: ["pagopa.it/test1", "pagopa.it/test2"], @@ -244,7 +243,6 @@ describe("V1 events", async () => { await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); - await sleep(1000, mockDate); const retrievedCatalogEntry = await readCatalogEntry( primaryKey, @@ -358,7 +356,6 @@ describe("V1 events", async () => { await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); - await sleep(1000, mockDate); const retrievedCatalogEntry = await readCatalogEntry( catalogPrimaryKey, @@ -381,7 +378,7 @@ describe("V1 events", async () => { }); describe("(published -> suspended)", () => { - it("should update the entry if msg.version >= existing version", async () => { + it("should update the entry if the incoming version is more recent than the existing table entry", async () => { const suspendedDescriptor: Descriptor = { ...getMockDescriptor(), audience: ["pagopa.it/test1", "pagopa.it/test2"], @@ -458,7 +455,6 @@ describe("V1 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); - await sleep(1000, mockDate); const retrievedEntry = await readCatalogEntry( primaryKey, @@ -497,7 +493,7 @@ describe("V1 events", async () => { ); }); - it("should do no operation if msg.version < existing version", async () => { + it("should do no operation if the existing table entry is more recent", async () => { const suspendedDescriptor: Descriptor = { ...getMockDescriptor(), audience: ["pagopa.it/test1", "pagopa.it/test2"], @@ -574,7 +570,6 @@ describe("V1 events", async () => { await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); - await sleep(1000, mockDate); const retrievedEntry = await readCatalogEntry( primaryKey, @@ -716,7 +711,6 @@ describe("V1 events", async () => { await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); - await sleep(1000, mockDate); const retrievedEntry = await readCatalogEntry(primaryKey, dynamoDBClient); expect(retrievedEntry).toBeUndefined(); diff --git a/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts b/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts index 104c5cf158..5f94de02ee 100644 --- a/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts +++ b/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts @@ -16,6 +16,7 @@ import { EServiceDescriptorActivatedV2, EServiceDescriptorArchivedV2, EServiceDescriptorPublishedV2, + EServiceDescriptorQuotasUpdatedV2, EServiceDescriptorSuspendedV2, EServiceEventEnvelope, PlatformStatesCatalogEntry, @@ -41,7 +42,7 @@ import { } from "pagopa-interop-commons-test"; import { readCatalogEntry, writeCatalogEntry } from "../src/utils.js"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; -import { config, sleep } from "./utils.js"; +import { config } from "./utils.js"; describe("integration tests V2 events", async () => { if (!config) { @@ -138,7 +139,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); // platform-states @@ -237,7 +237,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); // platform-states @@ -346,7 +345,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); expect( handleMessageV2(message, dynamoDBClient) ).resolves.not.toThrowError(); @@ -448,7 +446,6 @@ describe("integration tests V2 events", async () => { GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); @@ -755,7 +752,6 @@ describe("integration tests V2 events", async () => { GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); @@ -898,7 +894,7 @@ describe("integration tests V2 events", async () => { }); describe("EServiceDescriptorSuspended", () => { - it("should do no operation if the entry already exists: incoming has version 1; previous entry has version 2", async () => { + it("should do no operation if the existing table entry is more recent", async () => { const suspendedDescriptor: Descriptor = { ...getMockDescriptor(), audience: ["pagopa.it/test1", "pagopa.it/test2"], @@ -993,7 +989,7 @@ describe("integration tests V2 events", async () => { ]) ); }); - it("should update the entry: incoming has version 3; previous entry has version 2", async () => { + it("should update the entry if the incoming version is more recent than existing table entry", async () => { const suspendedDescriptor: Descriptor = { ...getMockDescriptor(), audience: ["pagopa.it/test1", "pagopa.it/test2"], @@ -1105,7 +1101,7 @@ describe("integration tests V2 events", async () => { ]) ); }); - it("should not throw error if entry doesn't exist", async () => { + it("should do no operation if entry doesn't exist", async () => { const suspendedDescriptor: Descriptor = { ...getMockDescriptor(), audience: ["pagopa.it/test1", "pagopa.it/test2"], @@ -1170,7 +1166,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); expect( handleMessageV2(message, dynamoDBClient) ).resolves.not.toThrowError(); @@ -1198,4 +1193,344 @@ describe("integration tests V2 events", async () => { ); }); }); + + describe("EServiceDescriptorQuotasUpdated", () => { + it("should do no operation if the existing version is more recent", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + audience: ["pagopa.it/test1", "pagopa.it/test2"], + interface: getMockDocument(), + state: descriptorState.suspended, + publishedAt: new Date(), + suspendedAt: new Date(), + voucherLifespan: 60, + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + + const updatedDescriptor: Descriptor = { + ...descriptor, + voucherLifespan: 120, + }; + + const updatedEService: EService = { + ...eservice, + descriptors: [updatedDescriptor], + }; + + const payload: EServiceDescriptorQuotasUpdatedV2 = { + eservice: toEServiceV2(updatedEService), + descriptorId: updatedDescriptor.id, + }; + const message: EServiceEventEnvelope = { + sequence_num: 1, + stream_id: eservice.id, + version: 1, + type: "EServiceDescriptorQuotasUpdated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const primaryKey = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: updatedDescriptor.id, + }); + const previousStateEntry: PlatformStatesCatalogEntry = { + PK: primaryKey, + state: itemState.active, + descriptorAudience: updatedDescriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readCatalogEntry(primaryKey, dynamoDBClient); + + expect(retrievedEntry).toEqual(previousStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByEserviceIdAndDescriptorId( + eserviceId_descriptorId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than the existing table entry", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + audience: ["pagopa.it/test1", "pagopa.it/test2"], + interface: getMockDocument(), + state: descriptorState.suspended, + publishedAt: new Date(), + suspendedAt: new Date(), + voucherLifespan: 60, + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + + const updatedDescriptor: Descriptor = { + ...descriptor, + voucherLifespan: 120, + }; + + const updatedEService: EService = { + ...eservice, + descriptors: [updatedDescriptor], + }; + + const payload: EServiceDescriptorQuotasUpdatedV2 = { + eservice: toEServiceV2(updatedEService), + descriptorId: updatedDescriptor.id, + }; + const message: EServiceEventEnvelope = { + sequence_num: 1, + stream_id: eservice.id, + version: 3, + type: "EServiceDescriptorQuotasUpdated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const primaryKey = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: updatedDescriptor.id, + }); + const previousStateEntry: PlatformStatesCatalogEntry = { + PK: primaryKey, + state: itemState.active, + descriptorAudience: updatedDescriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readCatalogEntry(primaryKey, dynamoDBClient); + const expectedEntry: PlatformStatesCatalogEntry = { + ...previousStateEntry, + version: 3, + descriptorVoucherLifespan: updatedDescriptor.voucherLifespan, + updatedAt: new Date().toISOString(), + }; + expect(retrievedEntry).toEqual(expectedEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByEserviceIdAndDescriptorId( + eserviceId_descriptorId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + descriptorVoucherLifespan: updatedDescriptor.voucherLifespan, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + descriptorVoucherLifespan: updatedDescriptor.voucherLifespan, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + it("should do no operation if entry doesn't exist", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + audience: ["pagopa.it/test1", "pagopa.it/test2"], + interface: getMockDocument(), + state: descriptorState.suspended, + publishedAt: new Date(), + suspendedAt: new Date(), + voucherLifespan: 60, + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + + const updatedDescriptor: Descriptor = { + ...descriptor, + voucherLifespan: 120, + }; + + const updatedEService: EService = { + ...eservice, + descriptors: [updatedDescriptor], + }; + + const payload: EServiceDescriptorQuotasUpdatedV2 = { + eservice: toEServiceV2(updatedEService), + descriptorId: updatedDescriptor.id, + }; + const message: EServiceEventEnvelope = { + sequence_num: 1, + stream_id: eservice.id, + version: 3, + type: "EServiceDescriptorQuotasUpdated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const primaryKey = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: updatedDescriptor.id, + }); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + expect( + handleMessageV2(message, dynamoDBClient) + ).resolves.not.toThrowError(); + + // platform-states + const retrievedCatalogEntry = await readCatalogEntry( + primaryKey, + dynamoDBClient + ); + expect(retrievedCatalogEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByEserviceIdAndDescriptorId( + eserviceId_descriptorId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + }); }); diff --git a/packages/catalog-platformstate-writer/test/utils.ts b/packages/catalog-platformstate-writer/test/utils.ts index fdc2e3c2c5..aca07c9cc6 100644 --- a/packages/catalog-platformstate-writer/test/utils.ts +++ b/packages/catalog-platformstate-writer/test/utils.ts @@ -1,11 +1,3 @@ -import { inject, vi } from "vitest"; +import { inject } from "vitest"; export const config = inject("tokenGenerationReadModelConfig"); - -export const sleep = (ms: number, mockDate = new Date()): Promise => - new Promise((resolve) => { - vi.useRealTimers(); - setTimeout(resolve, ms); - vi.useFakeTimers(); - vi.setSystemTime(mockDate); - }); From d2ed805c0bc1a6985e06d2577bd4faebd79751a5 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Wed, 20 Nov 2024 10:38:07 +0100 Subject: [PATCH 32/34] Delegation process improvements in errors, tests, API spec (#1209) --- collections/collection.bru | 1 + ...r delegations.bru => List delegations.bru} | 6 +- .../producer => }/Retrieves a delegation.bru | 2 +- .../health/Health status endpoint.bru | 0 .../producer/Approves a delegation.bru | 6 +- .../producer/Delegation Creation.bru | 10 +- .../producer/Rejects a delegation.bru | 8 +- .../producer/Revoke a delegation.bru | 6 +- collections/environments/PagoPA local.bru | 1 + .../api-clients/open-api/delegationApi.yml | 83 +++--- packages/commons-test/src/testUtils.ts | 10 +- .../src/model/domain/errors.ts | 25 +- .../src/services/delegationProducerService.ts | 17 +- .../src/services/validators.ts | 58 ++-- .../src/utilities/errorMappers.ts | 10 +- .../test/approveProducerDelegation.test.ts | 17 +- .../test/createProducerDelegation.test.ts | 260 +++++++++++------- .../test/getDelegationById.test.ts | 16 +- .../test/getDelegations.test.ts | 12 +- .../test/rejectProducerDelegation.test.ts | 14 +- .../test/revokeProducerDelegation.test.ts | 20 +- .../test/consumerServiceV2.test.ts | 10 +- 22 files changed, 365 insertions(+), 227 deletions(-) rename collections/delegation/{Delegation Process Micro Service/producer/List producer delegations.bru => List delegations.bru} (79%) rename collections/delegation/{Delegation Process Micro Service/producer => }/Retrieves a delegation.bru (82%) rename collections/delegation/{Delegation Process Micro Service => }/health/Health status endpoint.bru (100%) rename collections/delegation/{Delegation Process Micro Service => }/producer/Approves a delegation.bru (76%) rename collections/delegation/{Delegation Process Micro Service => }/producer/Delegation Creation.bru (64%) rename collections/delegation/{Delegation Process Micro Service => }/producer/Rejects a delegation.bru (68%) rename collections/delegation/{Delegation Process Micro Service => }/producer/Revoke a delegation.bru (79%) diff --git a/collections/collection.bru b/collections/collection.bru index 2597da5bd6..5c993b3ebd 100644 --- a/collections/collection.bru +++ b/collections/collection.bru @@ -1,5 +1,6 @@ vars:pre-request { tenantId: 69e2865e-65ab-4e48-a638-2037a9ee2ee7 + tenantId2: 0cf1db41-3085-43a6-9e4c-57e0fb81a916 userId1: f07ddb8f-17f9-47d4-b31e-35d1ac10e521 userId2: 2a1614d7-c1aa-4148-895f-dcadb75b6660 keyEncodedPem: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FETUVrdEZVVyszY2VWN01FT2RBVG5ZYWc3eQpJc3JtRDZ6eVBTZHhJTDlJczNmdnlGREMvbnVCWVFpa3Izampjc21aREdTN0RGKzNWRUJ3UXFYUldGM3NObElRCnFIc21Td2x2NjZ2ZDQ0OHEzSXpSb1JBWktGMGc3c3BGcUJ5bi9DTXZaM0RET2xVK2V0c2xDYWRNa084UktyM1YKd2xqQjFJdk90TWtCd2lLTU53SURBUUFCCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo= diff --git a/collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru b/collections/delegation/List delegations.bru similarity index 79% rename from collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru rename to collections/delegation/List delegations.bru index 733ec7c274..a7503108d1 100644 --- a/collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru +++ b/collections/delegation/List delegations.bru @@ -1,5 +1,5 @@ meta { - name: List producer delegations + name: List delegations type: http seq: 1 } @@ -24,3 +24,7 @@ headers { Authorization: {{JWT}} X-Correlation-Id: {{correlation-id}} } + +vars:post-response { + delegationId: res.body.results.at(-1).id +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru b/collections/delegation/Retrieves a delegation.bru similarity index 82% rename from collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru rename to collections/delegation/Retrieves a delegation.bru index 4e9425bd6d..398d20875c 100644 --- a/collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru +++ b/collections/delegation/Retrieves a delegation.bru @@ -11,7 +11,7 @@ get { } params:path { - delegationId: 123e4567-e89b-12d3-a456-426614174000 + delegationId: {{delegationId}} } headers { diff --git a/collections/delegation/Delegation Process Micro Service/health/Health status endpoint.bru b/collections/delegation/health/Health status endpoint.bru similarity index 100% rename from collections/delegation/Delegation Process Micro Service/health/Health status endpoint.bru rename to collections/delegation/health/Health status endpoint.bru diff --git a/collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru b/collections/delegation/producer/Approves a delegation.bru similarity index 76% rename from collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru rename to collections/delegation/producer/Approves a delegation.bru index 23bd4e2e56..3f94f781fd 100644 --- a/collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru +++ b/collections/delegation/producer/Approves a delegation.bru @@ -1,7 +1,7 @@ meta { name: Approves a delegation type: http - seq: 4 + seq: 2 } post { @@ -11,10 +11,10 @@ post { } params:path { - delegationId: + delegationId: {{delegationId}} } headers { - Authorization: {{JWT}} + Authorization: {{JWT2}} X-Correlation-Id: {{correlation-id}} } diff --git a/collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru b/collections/delegation/producer/Delegation Creation.bru similarity index 64% rename from collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru rename to collections/delegation/producer/Delegation Creation.bru index fa36a2e7e5..4e1656fadf 100644 --- a/collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru +++ b/collections/delegation/producer/Delegation Creation.bru @@ -1,7 +1,7 @@ meta { name: Delegation Creation type: http - seq: 3 + seq: 1 } post { @@ -17,7 +17,11 @@ headers { body:json { { - "eserviceId": "", - "delegateId": "" + "eserviceId": "{{eserviceId}}", + "delegateId": "{{tenantId2}}" } } + +vars:post-response { + delegationId: res.body.id +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru b/collections/delegation/producer/Rejects a delegation.bru similarity index 68% rename from collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru rename to collections/delegation/producer/Rejects a delegation.bru index b2b1477d62..57f0709361 100644 --- a/collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru +++ b/collections/delegation/producer/Rejects a delegation.bru @@ -1,7 +1,7 @@ meta { name: Rejects a delegation type: http - seq: 5 + seq: 3 } post { @@ -11,16 +11,16 @@ post { } params:path { - delegationId: + delegationId: {{delegationId}} } headers { - Authorization: {{JWT}} + Authorization: {{JWT2}} X-Correlation-Id: {{correlation-id}} } body:json { { - "rejectionReason": "" + "rejectionReason": "test rejection reason" } } diff --git a/collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru b/collections/delegation/producer/Revoke a delegation.bru similarity index 79% rename from collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru rename to collections/delegation/producer/Revoke a delegation.bru index 69741abb1d..392a524b0d 100644 --- a/collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru +++ b/collections/delegation/producer/Revoke a delegation.bru @@ -1,7 +1,7 @@ meta { name: Revoke a delegation type: http - seq: 6 + seq: 4 } delete { @@ -10,6 +10,10 @@ delete { auth: none } +params:path { + delegationId: {{delegationId}} +} + headers { Authorization: {{JWT}} X-Correlation-Id: {{correlation-id}} diff --git a/collections/environments/PagoPA local.bru b/collections/environments/PagoPA local.bru index f0051f88bb..30c756afeb 100644 --- a/collections/environments/PagoPA local.bru +++ b/collections/environments/PagoPA local.bru @@ -12,4 +12,5 @@ vars { host-delegation: http://localhost:3800 host-api-gw: http://localhost:3700/api-gateway/0.0 JWT-M2M: Bearer {{process.env.JWT-M2M}} + JWT2: Bearer {{process.env.JWT2}} } diff --git a/packages/api-clients/open-api/delegationApi.yml b/packages/api-clients/open-api/delegationApi.yml index c87eeff3b2..d912ab74c0 100644 --- a/packages/api-clients/open-api/delegationApi.yml +++ b/packages/api-clients/open-api/delegationApi.yml @@ -33,6 +33,8 @@ tags: url: http://swagger.io paths: /delegations: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" get: description: List delegations summary: List delegations @@ -118,19 +120,21 @@ paths: schema: $ref: "#/components/schemas/Problem" /delegations/{delegationId}: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + format: uuid get: description: Retrieves a delegation summary: Retrieves a delegation tags: - delegation operationId: getDelegation - parameters: - - name: delegationId - in: path - description: The delegation id - required: true - schema: - type: string responses: "200": description: Delegation retrieved @@ -157,9 +161,11 @@ paths: schema: $ref: "#/components/schemas/Problem" /producer/delegations: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" post: - description: creates the delegation - summary: Delegation Creation + description: Creates a producer delegation + summary: Producer delegation creation tags: - producer operationId: createProducerDelegation @@ -168,7 +174,7 @@ paths: application/json: schema: $ref: "#/components/schemas/DelegationSeed" - description: payload for delegation creation + description: Payload for delegation creation required: true responses: "200": @@ -196,20 +202,21 @@ paths: schema: $ref: "#/components/schemas/Problem" /producer/delegations/{delegationId}/approve: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + format: uuid post: - description: Approves a delegation - summary: Approves a delegation + description: Approves a producer delegation + summary: Producer delegation approval tags: - producer operationId: approveProducerDelegation - parameters: - - name: delegationId - in: path - description: The delegation id - required: true - schema: - type: string - format: uuid responses: "204": description: Delegation approved @@ -238,9 +245,18 @@ paths: schema: $ref: "#/components/schemas/Problem" /producer/delegations/{delegationId}/reject: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + format: uuid post: - description: Rejects a delegation - summary: Rejects a delegation + description: Rejects a producer delegation + summary: Producer delegation rejection tags: - producer operationId: rejectProducerDelegation @@ -250,15 +266,7 @@ paths: schema: $ref: "#/components/schemas/RejectDelegationPayload" required: true - description: payload for delegation rejection - parameters: - - name: delegationId - in: path - description: The delegation id - required: true - schema: - type: string - format: uuid + description: Payload for delegation rejection responses: "204": description: Delegation rejected @@ -288,15 +296,17 @@ paths: $ref: "#/components/schemas/Problem" /producer/delegations/{delegationId}: parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" - name: delegationId in: path description: The delegation id required: true schema: type: string + format: uuid delete: - description: Revokes a delegation - summary: Revokes a delegation + description: Revokes a producer delegation + summary: Producer delegation revocation tags: - producer operationId: revokeProducerDelegation @@ -337,6 +347,13 @@ paths: schema: $ref: "#/components/schemas/Problem" components: + parameters: + CorrelationIdHeader: + in: header + name: X-Correlation-Id + required: true + schema: + type: string schemas: Delegations: type: object diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 2d8a8d7692..72fbb57eff 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -50,7 +50,6 @@ import { PurposeVersionId, ProducerKeychain, Delegation, - delegationKind, DelegationId, DelegationContractDocument, DelegationContractId, @@ -68,6 +67,7 @@ import { PlatformStatesClientPK, PlatformStatesClientEntry, makePlatformStatesClientPK, + DelegationKind, unsafeBrandId, } from "pagopa-interop-models"; import { AuthData, dateToSeconds } from "pagopa-interop-commons"; @@ -369,7 +369,8 @@ export const getMockAuthData = (organizationId?: TenantId): AuthData => ({ selfcareId: generateId(), }); -export const getMockDelegationProducer = ({ +export const getMockDelegation = ({ + kind, id = generateId(), delegatorId = generateId(), delegateId = generateId(), @@ -378,6 +379,7 @@ export const getMockDelegationProducer = ({ activationContract = undefined, revocationContract = undefined, }: { + kind: DelegationKind; id?: DelegationId; delegatorId?: TenantId; delegateId?: TenantId; @@ -385,7 +387,7 @@ export const getMockDelegationProducer = ({ state?: DelegationState; activationContract?: DelegationContractDocument; revocationContract?: DelegationContractDocument; -} = {}): Delegation => { +}): Delegation => { const creationTime = new Date(); return { @@ -398,7 +400,7 @@ export const getMockDelegationProducer = ({ state, activationContract, revocationContract, - kind: delegationKind.delegatedProducer, + kind, stamps: { submission: { who: delegatorId, diff --git a/packages/delegation-process/src/model/domain/errors.ts b/packages/delegation-process/src/model/domain/errors.ts index b5b0064bb6..7822e1e03c 100644 --- a/packages/delegation-process/src/model/domain/errors.ts +++ b/packages/delegation-process/src/model/domain/errors.ts @@ -5,7 +5,10 @@ import { makeApiProblemBuilder, TenantId, DelegationState, + DelegationKind, + Tenant, } from "pagopa-interop-models"; +import { match } from "ts-pattern"; export const errorCodes = { delegationNotFound: "0001", @@ -13,7 +16,7 @@ export const errorCodes = { delegationAlreadyExists: "0003", tenantNotFound: "0004", invalidDelegatorAndDelegateIds: "0005", - invalidExternalOriginId: "0006", + tenantIsNotIPAError: "0006", tenantNotAllowedToDelegation: "0007", delegationNotRevokable: "0008", operationNotAllowOnDelegation: "0009", @@ -70,21 +73,27 @@ export function delegatorAndDelegateSameIdError(): ApiError { }); } -export function invalidExternalOriginError( - externalOrigin?: string +export function tenantIsNotIPAError( + tenant: Tenant, + delegatorOrDelegate: "Delegator" | "Delegate" ): ApiError { + const delegatorOrDelegateString = match(delegatorOrDelegate) + .with("Delegator", () => "Delegator") + .with("Delegate", () => "Delegate") + .exhaustive(); return new ApiError({ - detail: `Delegator is not an IPA`, - code: "invalidExternalOriginId", - title: `Invalid External origin ${externalOrigin}`, + detail: `${delegatorOrDelegateString} ${tenant.id} with external origin ${tenant.externalId.origin} is not an IPA`, + code: "tenantIsNotIPAError", + title: `Invalid external origin`, }); } export function tenantNotAllowedToDelegation( - tenantId: string + tenantId: string, + kind: DelegationKind ): ApiError { return new ApiError({ - detail: `Tenant ${tenantId} not allowed to delegation`, + detail: `Tenant ${tenantId} not allowed to receive delegations of kind: ${kind}`, code: "tenantNotAllowedToDelegation", title: "Tenant not allowed to delegation", }); diff --git a/packages/delegation-process/src/services/delegationProducerService.ts b/packages/delegation-process/src/services/delegationProducerService.ts index f323a8de45..81c844f3f3 100644 --- a/packages/delegation-process/src/services/delegationProducerService.ts +++ b/packages/delegation-process/src/services/delegationProducerService.ts @@ -32,12 +32,12 @@ import { ReadModelService } from "./readModelService.js"; import { assertDelegationIsRevokable, assertDelegationNotExists, - assertDelegatorIsIPA, assertDelegatorIsNotDelegate, - assertEserviceExists, - assertTenantAllowedToReceiveProducerDelegation, + assertDelegatorIsProducer, + assertTenantAllowedToReceiveDelegation, assertIsDelegate, assertIsState, + assertDelegatorAndDelegateIPA, } from "./validators.js"; import { contractBuilder } from "./delegationContractBuilder.js"; import { retrieveDelegationById } from "./delegationService.js"; @@ -84,9 +84,14 @@ export function delegationProducerServiceBuilder( const delegator = await retrieveTenantById(delegatorId); const delegate = await retrieveTenantById(delegateId); - assertTenantAllowedToReceiveProducerDelegation(delegate); - await assertDelegatorIsIPA(delegator); - await assertEserviceExists(delegatorId, eserviceId, readModelService); + assertTenantAllowedToReceiveDelegation( + delegate, + delegationKind.delegatedProducer + ); + await assertDelegatorAndDelegateIPA(delegator, delegate); + + const eservice = await retrieveEserviceById(eserviceId); + assertDelegatorIsProducer(delegatorId, eservice); await assertDelegationNotExists( delegator, eserviceId, diff --git a/packages/delegation-process/src/services/validators.ts b/packages/delegation-process/src/services/validators.ts index c5a6437d4a..e4060bff3f 100644 --- a/packages/delegation-process/src/services/validators.ts +++ b/packages/delegation-process/src/services/validators.ts @@ -1,30 +1,31 @@ import { Delegation, + delegationKind, DelegationKind, DelegationState, delegationState, + EService, EServiceId, PUBLIC_ADMINISTRATIONS_IDENTIFIER, Tenant, TenantId, } from "pagopa-interop-models"; +import { match } from "ts-pattern"; import { delegationAlreadyExists, delegationNotRevokable, delegatorAndDelegateSameIdError, delegatorNotAllowToRevoke, differentEServiceProducer, - eserviceNotFound, incorrectState, - invalidExternalOriginError, operationRestrictedToDelegate, + tenantIsNotIPAError, tenantNotAllowedToDelegation, - tenantNotFound, } from "../model/domain/errors.js"; import { ReadModelService } from "./readModelService.js"; /* ========= STATES ========= */ -export const delegationNotActivableStates: DelegationState[] = [ +export const inactiveDelegationStates: DelegationState[] = [ delegationState.rejected, delegationState.revoked, ]; @@ -34,17 +35,11 @@ export const activeDelegationStates: DelegationState[] = [ delegationState.active, ]; -export const assertEserviceExists = async ( +export const assertDelegatorIsProducer = ( delegatorId: TenantId, - eserviceId: EServiceId, - readModelService: ReadModelService -): Promise => { - const eservice = await readModelService.getEServiceById(eserviceId); - if (!eservice) { - throw eserviceNotFound(eserviceId); - } - - if (eservice.data.producerId !== delegatorId) { + eservice: EService +): void => { + if (eservice.producerId !== delegatorId) { throw differentEServiceProducer(delegatorId); } }; @@ -58,33 +53,34 @@ export const assertDelegatorIsNotDelegate = ( } }; -export const assertDelegatorIsIPA = async ( - delegator?: Tenant +export const assertDelegatorAndDelegateIPA = async ( + delegator: Tenant, + delegate: Tenant ): Promise => { if (delegator?.externalId?.origin !== PUBLIC_ADMINISTRATIONS_IDENTIFIER) { - throw invalidExternalOriginError(delegator?.externalId?.origin); + throw tenantIsNotIPAError(delegator, "Delegator"); + } + + if (delegate?.externalId?.origin !== PUBLIC_ADMINISTRATIONS_IDENTIFIER) { + throw tenantIsNotIPAError(delegate, "Delegate"); } }; -export const assertTenantAllowedToReceiveProducerDelegation = ( - tenant: Tenant +export const assertTenantAllowedToReceiveDelegation = ( + tenant: Tenant, + kind: DelegationKind ): void => { const delegationFeature = tenant.features.find( - (f) => f.type === "DelegatedProducer" + (f) => + f.type === + match(kind) + .with(delegationKind.delegatedProducer, () => "DelegatedProducer") + .with(delegationKind.delegatedConsumer, () => "DelegatedConsumer") + .exhaustive() ); if (!delegationFeature) { - throw tenantNotAllowedToDelegation(tenant.id); - } -}; - -export const assertTenantExists = async ( - tenantId: TenantId, - readModelService: ReadModelService -): Promise => { - const tenant = await readModelService.getTenantById(tenantId); - if (!tenant) { - throw tenantNotFound(tenantId); + throw tenantNotAllowedToDelegation(tenant.id, kind); } }; diff --git a/packages/delegation-process/src/utilities/errorMappers.ts b/packages/delegation-process/src/utilities/errorMappers.ts index 98e2a40d3a..00268107f2 100644 --- a/packages/delegation-process/src/utilities/errorMappers.ts +++ b/packages/delegation-process/src/utilities/errorMappers.ts @@ -12,6 +12,7 @@ const { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_FORBIDDEN, HTTP_STATUS_UNAUTHORIZED, + HTTP_STATUS_CONFLICT, } = constants; export const getDelegationsErrorMapper = ( @@ -32,13 +33,16 @@ export const createProducerDelegationErrorMapper = ( match(error.code) .with( "eserviceNotFound", - "delegationAlreadyExists", "tenantNotFound", "invalidDelegatorAndDelegateIds", - "invalidExternalOriginId", - "tenantNotAllowedToDelegation", () => HTTP_STATUS_BAD_REQUEST ) + .with( + "tenantIsNotIPAError", + "tenantNotAllowedToDelegation", + () => HTTP_STATUS_FORBIDDEN + ) + .with("delegationAlreadyExists", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); export const revokeDelegationErrorMapper = ( diff --git a/packages/delegation-process/test/approveProducerDelegation.test.ts b/packages/delegation-process/test/approveProducerDelegation.test.ts index dc1841a5ca..69199af69a 100644 --- a/packages/delegation-process/test/approveProducerDelegation.test.ts +++ b/packages/delegation-process/test/approveProducerDelegation.test.ts @@ -1,7 +1,7 @@ /* eslint-disable functional/no-let */ import { decodeProtobufPayload, - getMockDelegationProducer, + getMockDelegation, getMockTenant, getMockEService, getMockAuthData, @@ -16,6 +16,7 @@ import { Tenant, toDelegationV2, unsafeBrandId, + delegationKind, } from "pagopa-interop-models"; import { delegationState } from "pagopa-interop-models"; import { @@ -40,7 +41,7 @@ import { flushPDFMetadata, } from "./utils.js"; -describe("approve delegation", () => { +describe("approve producer delegation", () => { const currentExecutionTime = new Date(); beforeAll(async () => { vi.useFakeTimers(); @@ -63,7 +64,8 @@ describe("approve delegation", () => { it("should approve delegation if validations succeed", async () => { const delegationId = generateId(); - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, id: delegationId, state: "WaitingForApproval", delegateId: delegate.id, @@ -173,7 +175,8 @@ describe("approve delegation", () => { it("should throw operationRestrictedToDelegate when approver is not the delegate", async () => { const wrongDelegate = getMockTenant(); await addOneTenant(wrongDelegate); - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, state: "WaitingForApproval", delegateId: delegate.id, delegatorId: delegator.id, @@ -194,7 +197,8 @@ describe("approve delegation", () => { }); it("should throw incorrectState when delegation is not in WaitingForApproval state", async () => { - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, state: "Active", delegateId: delegate.id, delegatorId: delegator.id, @@ -219,7 +223,8 @@ describe("approve delegation", () => { }); it("should generete a pdf document for a delegation", async () => { - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, state: "WaitingForApproval", delegateId: delegate.id, delegatorId: delegator.id, diff --git a/packages/delegation-process/test/createProducerDelegation.test.ts b/packages/delegation-process/test/createProducerDelegation.test.ts index 97b0cec3c2..1463fba985 100644 --- a/packages/delegation-process/test/createProducerDelegation.test.ts +++ b/packages/delegation-process/test/createProducerDelegation.test.ts @@ -2,7 +2,7 @@ import { fail } from "assert"; import { genericLogger } from "pagopa-interop-commons"; import { decodeProtobufPayload, - getMockDelegationProducer, + getMockDelegation, getMockEService, getMockTenant, getRandomAuthData, @@ -25,14 +25,14 @@ import { delegatorAndDelegateSameIdError, differentEServiceProducer, eserviceNotFound, - invalidExternalOriginError, + tenantIsNotIPAError, tenantNotAllowedToDelegation, tenantNotFound, } from "../src/model/domain/errors.js"; import { activeDelegationStates, - delegationNotActivableStates, + inactiveDelegationStates, } from "../src/services/validators.js"; import { addOneDelegation, @@ -81,8 +81,8 @@ const expectedDelegationCreation = async ( ); }; -describe("create delegation", () => { - it("should create a delegation if not exists", async () => { +describe("create producer delegation", () => { + it("should create a delegation if it does not exist", async () => { const currentExecutionTime = new Date(); vi.useFakeTimers(); vi.setSystemTime(currentExecutionTime); @@ -93,7 +93,7 @@ describe("create delegation", () => { ...getMockTenant(delegatorId), externalId: { origin: "IPA", - value: "anythings", + value: "test", }, }; @@ -147,83 +147,87 @@ describe("create delegation", () => { vi.useRealTimers(); }); - it("should create a delegation if already exists the same delegation in status Rejected or Revoked", async () => { - const currentExecutionTime = new Date(); - vi.useFakeTimers(); - vi.setSystemTime(currentExecutionTime); + it.each(inactiveDelegationStates)( + "should create a delegation the same delegation exists and is in state %s", + async (inactiveDelegationState) => { + const currentExecutionTime = new Date(); + vi.useFakeTimers(); + vi.setSystemTime(currentExecutionTime); - const delegatorId = generateId(); - const authData = getRandomAuthData(delegatorId); - const delegator = { - ...getMockTenant(delegatorId), - externalId: { - origin: "IPA", - value: "anythings", - }, - }; - - const delegate = { - ...getMockTenant(), - features: [ - { - type: "DelegatedProducer" as const, - availabilityTimestamp: currentExecutionTime, + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "test", }, - ], - }; - const eservice = getMockEService(generateId(), delegatorId); - - const existentDelegation = { - ...getMockDelegationProducer({ - id: generateId(), - delegatorId, - delegateId: delegate.id, - eserviceId: eservice.id, - }), - state: randomArrayItem(delegationNotActivableStates), - }; + }; - await addOneTenant(delegator); - await addOneTenant(delegate); - await addOneEservice(eservice); - await addOneDelegation(existentDelegation); + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: currentExecutionTime, + }, + ], + }; + const eservice = getMockEService(generateId(), delegatorId); - const actualDelegation = - await delegationProducerService.createProducerDelegation( - { + const existentDelegation = { + ...getMockDelegation({ + kind: delegationKind.delegatedProducer, + id: generateId(), + delegatorId, delegateId: delegate.id, eserviceId: eservice.id, - }, - { - authData, - logger: genericLogger, - correlationId: generateId(), - serviceName: "DelegationServiceTest", - } - ); + }), + state: inactiveDelegationState, + }; - const expectedDelegation: Delegation = { - id: actualDelegation.id, - delegatorId, - delegateId: delegate.id, - eserviceId: eservice.id, - kind: delegationKind.delegatedProducer, - state: delegationState.waitingForApproval, - createdAt: currentExecutionTime, - submittedAt: currentExecutionTime, - stamps: { - submission: { - who: delegatorId, - when: currentExecutionTime, + await addOneTenant(delegator); + await addOneTenant(delegate); + await addOneEservice(eservice); + await addOneDelegation(existentDelegation); + + const actualDelegation = + await delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: eservice.id, + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ); + + const expectedDelegation: Delegation = { + id: actualDelegation.id, + delegatorId, + delegateId: delegate.id, + eserviceId: eservice.id, + kind: delegationKind.delegatedProducer, + state: delegationState.waitingForApproval, + createdAt: currentExecutionTime, + submittedAt: currentExecutionTime, + stamps: { + submission: { + who: delegatorId, + when: currentExecutionTime, + }, }, - }, - }; + }; - await expectedDelegationCreation(actualDelegation, expectedDelegation); - vi.useRealTimers(); - }); + await expectedDelegationCreation(actualDelegation, expectedDelegation); + vi.useRealTimers(); + } + ); - it("should throw an differentEServiceProducer error if requester is not Eservice producer", async () => { + it("should throw a differentEServiceProducer error if requester is not Eservice producer", async () => { const currentExecutionTime = new Date(); vi.useFakeTimers(); vi.setSystemTime(currentExecutionTime); @@ -234,7 +238,7 @@ describe("create delegation", () => { ...getMockTenant(delegatorId), externalId: { origin: "IPA", - value: "anythings", + value: "test", }, }; @@ -272,7 +276,7 @@ describe("create delegation", () => { }); it.each(activeDelegationStates)( - "should throw an delegationAlreadyExists error when Delegation for eservice producer already exists with for same delegator, delegate and eserivce ", + "should throw a delegationAlreadyExists error when a producer Delegation in state %s already exists with for same delegator, delegate and eservice", async (validDelegationState) => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); @@ -280,7 +284,7 @@ describe("create delegation", () => { ...getMockTenant(delegatorId), externalId: { origin: "IPA", - value: "anythings", + value: "test", }, }; @@ -294,8 +298,9 @@ describe("create delegation", () => { ], }; const eservice = getMockEService(generateId(), delegatorId); - const existentValidDelegation = { - ...getMockDelegationProducer({ + const existentActiveDelegation = { + ...getMockDelegation({ + kind: delegationKind.delegatedProducer, id: generateId(), delegatorId, delegateId: delegate.id, @@ -307,35 +312,40 @@ describe("create delegation", () => { await addOneTenant(delegate); await addOneTenant(delegator); await addOneEservice(eservice); - // Add existent valid delegation for the same delegator, delegate and eservice - await addOneDelegation(existentValidDelegation); - // Add existent invalid delegation for the same delegator, delegate and eservice + // Add existent active delegation for the same delegator, delegate and eservice + await addOneDelegation(existentActiveDelegation); + // Add existent inactive delegation for the same delegator, delegate and eservice await addOneDelegation({ - ...existentValidDelegation, + ...existentActiveDelegation, id: generateId(), - state: validDelegationState, + state: randomArrayItem(inactiveDelegationStates), }); // Add another generic delegation - await addOneDelegation(getMockDelegationProducer()); + await addOneDelegation( + getMockDelegation({ kind: delegationKind.delegatedProducer }) + ); // Add another delegation with same delegator await addOneDelegation( - getMockDelegationProducer({ + getMockDelegation({ + kind: delegationKind.delegatedProducer, delegatorId, }) ); // Add another delegation with same delegate await addOneDelegation( - getMockDelegationProducer({ + getMockDelegation({ + kind: delegationKind.delegatedProducer, delegateId: delegate.id, }) ); // Add another delegation for the same eservice await addOneDelegation( - getMockDelegationProducer({ + getMockDelegation({ + kind: delegationKind.delegatedProducer, eserviceId: eservice.id, }) ); @@ -356,14 +366,14 @@ describe("create delegation", () => { ).rejects.toThrowError( delegationAlreadyExists( delegatorId, - existentValidDelegation.eserviceId, + existentActiveDelegation.eserviceId, delegationKind.delegatedProducer ) ); } ); - it("should throw an tenantNotFound error if delegated tenant not exists", async () => { + it("should throw a tenantNotFound error if delegated tenant does not exist", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); const delegator = getMockTenant(delegatorId); @@ -388,7 +398,7 @@ describe("create delegation", () => { ).rejects.toThrowError(tenantNotFound(delegateId)); }); - it("should throw an tenantNotFound error if delegator tenant not exists", async () => { + it("should throw a tenantNotFound error if delegator tenant does not exist", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); @@ -441,14 +451,14 @@ describe("create delegation", () => { ).rejects.toThrowError(delegatorAndDelegateSameIdError()); }); - it("should throw an invalidExternalOriginError error if delegator has externalId origin different from IPA", async () => { + it("should throw an tenantIsNotIPAError error if delegator has externalId origin different from IPA", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); const delegator = { ...getMockTenant(delegatorId), externalId: { origin: "NOT_IPA", - value: "anythings", + value: "test", }, }; @@ -478,19 +488,61 @@ describe("create delegation", () => { serviceName: "DelegationServiceTest", } ) - ).rejects.toThrowError( - invalidExternalOriginError(delegator.externalId.origin) - ); + ).rejects.toThrowError(tenantIsNotIPAError(delegator, "Delegator")); }); - it("should throw an eserviceNotFound error if Eservice not exists", async () => { + it("should throw an tenantIsNotIPAError error if delegate has externalId origin different from IPA", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); const delegator = { ...getMockTenant(delegatorId), externalId: { origin: "IPA", - value: "anythings", + value: "test", + }, + }; + + const delegate = { + ...getMockTenant(), + externalId: { + origin: "NOT_IPA", + value: "test", + }, + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: new Date(), + }, + ], + }; + + await addOneTenant(delegate); + await addOneTenant(delegator); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: generateId(), + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(tenantIsNotIPAError(delegate, "Delegate")); + }); + + it("should throw an eserviceNotFound error if Eservice does not exist", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "test", }, }; @@ -504,7 +556,8 @@ describe("create delegation", () => { ], }; const eserviceId = generateId(); - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, id: generateId(), delegatorId, delegateId: delegate.id, @@ -530,14 +583,14 @@ describe("create delegation", () => { ).rejects.toThrowError(eserviceNotFound(eserviceId)); }); - it("should throw an invalidExternalOriginError error if delegator has externalId origin different from IPA", async () => { + it("should throw a tenantNotAllowedToDelegation error if delegate tenant has no DelegatedProducer feature", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); const delegator = { ...getMockTenant(delegatorId), externalId: { origin: "IPA", - value: "anythings", + value: "test", }, }; @@ -559,6 +612,11 @@ describe("create delegation", () => { serviceName: "DelegationServiceTest", } ) - ).rejects.toThrowError(tenantNotAllowedToDelegation(delegate.id)); + ).rejects.toThrowError( + tenantNotAllowedToDelegation( + delegate.id, + delegationKind.delegatedProducer + ) + ); }); }); diff --git a/packages/delegation-process/test/getDelegationById.test.ts b/packages/delegation-process/test/getDelegationById.test.ts index ddaf026c79..9a323bbaec 100644 --- a/packages/delegation-process/test/getDelegationById.test.ts +++ b/packages/delegation-process/test/getDelegationById.test.ts @@ -1,6 +1,10 @@ /* eslint-disable functional/no-let */ -import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; -import { DelegationId, generateId } from "pagopa-interop-models"; +import { getMockDelegation } from "pagopa-interop-commons-test/index.js"; +import { + DelegationId, + delegationKind, + generateId, +} from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; import { delegationNotFound } from "../src/model/domain/errors.js"; @@ -8,7 +12,9 @@ import { addOneDelegation, delegationService } from "./utils.js"; describe("get delegation by id", () => { it("should get the delegation if it exists", async () => { - const delegation = getMockDelegationProducer(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + }); await addOneDelegation(delegation); @@ -21,7 +27,9 @@ describe("get delegation by id", () => { }); it("should fail with delegationNotFound", async () => { - const delegation = getMockDelegationProducer(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + }); await addOneDelegation(delegation); diff --git a/packages/delegation-process/test/getDelegations.test.ts b/packages/delegation-process/test/getDelegations.test.ts index e7ba0c8381..0a11027266 100644 --- a/packages/delegation-process/test/getDelegations.test.ts +++ b/packages/delegation-process/test/getDelegations.test.ts @@ -1,13 +1,19 @@ /* eslint-disable functional/no-let */ -import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; +import { getMockDelegation } from "pagopa-interop-commons-test/index.js"; import { describe, expect, it } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; +import { delegationKind } from "pagopa-interop-models"; import { addOneDelegation, delegationService } from "./utils.js"; describe("get delegations", () => { it("should get delegations", async () => { - const delegation1 = getMockDelegationProducer({ state: "Active" }); - const delegation2 = getMockDelegationProducer(); + const delegation1 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + state: "Active", + }); + const delegation2 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + }); await addOneDelegation(delegation1); await addOneDelegation(delegation2); diff --git a/packages/delegation-process/test/rejectProducerDelegation.test.ts b/packages/delegation-process/test/rejectProducerDelegation.test.ts index f24401386d..cf5afe2063 100644 --- a/packages/delegation-process/test/rejectProducerDelegation.test.ts +++ b/packages/delegation-process/test/rejectProducerDelegation.test.ts @@ -2,13 +2,14 @@ import { decodeProtobufPayload, getMockAuthData, - getMockDelegationProducer, + getMockDelegation, getMockTenant, } from "pagopa-interop-commons-test/index.js"; import { describe, expect, it, vi } from "vitest"; import { DelegationId, ProducerDelegationRejectedV2, + delegationKind, generateId, toDelegationV2, unsafeBrandId, @@ -26,14 +27,15 @@ import { readLastDelegationEvent, } from "./utils.js"; -describe("reject delegation", () => { +describe("reject producer delegation", () => { it("should reject delegation if all validations succed", async () => { const currentExecutionTime = new Date(); vi.useFakeTimers(); vi.setSystemTime(currentExecutionTime); const delegate = getMockTenant(); - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, state: "WaitingForApproval", delegateId: delegate.id, }); @@ -93,7 +95,8 @@ describe("reject delegation", () => { it("should throw operationRestrictedToDelegate when rejecter is not the delegate", async () => { const delegate = getMockTenant(); const wrongDelegate = getMockTenant(); - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, state: "WaitingForApproval", delegateId: delegate.id, }); @@ -113,7 +116,8 @@ describe("reject delegation", () => { it("should throw incorrectState when delegation is not in WaitingForApproval state", async () => { const delegate = getMockTenant(); - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, state: "Active", delegateId: delegate.id, }); diff --git a/packages/delegation-process/test/revokeProducerDelegation.test.ts b/packages/delegation-process/test/revokeProducerDelegation.test.ts index ebff03f0cb..ffc5c3c287 100644 --- a/packages/delegation-process/test/revokeProducerDelegation.test.ts +++ b/packages/delegation-process/test/revokeProducerDelegation.test.ts @@ -1,7 +1,7 @@ import { fail } from "assert"; import { decodeProtobufPayload, - getMockDelegationProducer, + getMockDelegation, getMockEService, getMockTenant, getRandomAuthData, @@ -17,6 +17,7 @@ import { EServiceId, unsafeBrandId, DelegationContractId, + delegationKind, } from "pagopa-interop-models"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { @@ -102,7 +103,7 @@ const getNotRevocableStateSeeds = (): DelegationStateSeed[] => { ]; }; -describe("revoke delegation", () => { +describe("revoke producer delegation", () => { const TEST_EXECUTION_DATE = new Date(); beforeAll(() => { @@ -138,7 +139,8 @@ describe("revoke delegation", () => { await addOneEservice(eservice); const existentDelegation: Delegation = { - ...getMockDelegationProducer({ + ...getMockDelegation({ + kind: delegationKind.delegatedProducer, delegatorId, delegateId, }), @@ -257,7 +259,7 @@ describe("revoke delegation", () => { expect(delegationFromLastEvent).toMatchObject(expectedDelegation); }); - it("should throw an delegationNotFound if Delegation not exists", async () => { + it("should throw a delegationNotFound if Delegation does not exist", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); const delegationId = generateId(); @@ -271,7 +273,7 @@ describe("revoke delegation", () => { ).rejects.toThrow(delegationNotFound(delegationId)); }); - it("should throw an delegatorNotAllowToRevoke if Requester Id and DelegatorId are differents", async () => { + it("should throw a delegatorNotAllowToRevoke if Requester Id and DelegatorId are differents", async () => { const currentExecutionTime = new Date(); const delegatorId = generateId(); @@ -286,7 +288,8 @@ describe("revoke delegation", () => { delegationApprovalDate.setMonth(currentExecutionTime.getMonth() - 1); const existentDelegation = { - ...getMockDelegationProducer({ + ...getMockDelegation({ + kind: delegationKind.delegatedProducer, id: delegationId, delegateId, }), @@ -318,7 +321,7 @@ describe("revoke delegation", () => { }); it.each(notRevocableDelegationState)( - "should throw an delegatorNotAllowToRevoke if delegation doesn't have revocable one of revocable states [Rejected,Revoked]", + "should throw a delegatorNotAllowToRevoke if delegation doesn't have revocable one of revocable states [Rejected,Revoked]", async (notRevocableDelegationState: DelegationStateSeed) => { const currentExecutionTime = new Date(); @@ -333,7 +336,8 @@ describe("revoke delegation", () => { delegationActivationDate.setMonth(currentExecutionTime.getMonth() - 1); const existentDelegation: Delegation = { - ...getMockDelegationProducer({ + ...getMockDelegation({ + kind: delegationKind.delegatedProducer, delegatorId, delegateId, }), diff --git a/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts b/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts index 48348a453f..e9aa448762 100644 --- a/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts +++ b/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts @@ -1,4 +1,7 @@ -import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; +import { + getMockDelegation, + randomArrayItem, +} from "pagopa-interop-commons-test/index.js"; import { DelegationEventEnvelopeV2, toDelegationV2, @@ -6,13 +9,16 @@ import { ProducerDelegationRejectedV2, ProducerDelegationRevokedV2, ProducerDelegationSubmittedV2, + delegationKind, } from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; import { handleMessageV2 } from "../src/delegationConsumerServiceV2.js"; import { delegations } from "./utils.js"; describe("Events V2", async () => { - const mockDelegation = getMockDelegationProducer(); + const mockDelegation = getMockDelegation({ + kind: randomArrayItem(Object.values(delegationKind)), + }); const mockMessage: DelegationEventEnvelopeV2 = { event_version: 2, stream_id: mockDelegation.id, From f439ec831105fa3d063f57d116cd96b312dca44a Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Fri, 22 Nov 2024 12:11:07 +0100 Subject: [PATCH 33/34] Fixing type of `who` in delegation process (#1216) --- packages/commons-test/src/testUtils.ts | 5 +++- .../src/services/delegationProducerService.ts | 13 ++++----- .../delegation-process/test/.eslintrc.json | 3 ++- .../test/approveProducerDelegation.test.ts | 15 ++++++----- .../test/createProducerDelegation.test.ts | 4 +-- .../test/rejectProducerDelegation.test.ts | 13 ++++----- .../test/revokeProducerDelegation.test.ts | 27 ++++++++++--------- packages/models/src/delegation/delegation.ts | 3 ++- 8 files changed, 46 insertions(+), 37 deletions(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 72fbb57eff..6b72aa572a 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -69,6 +69,7 @@ import { makePlatformStatesClientPK, DelegationKind, unsafeBrandId, + UserId, } from "pagopa-interop-models"; import { AuthData, dateToSeconds } from "pagopa-interop-commons"; import { z } from "zod"; @@ -376,6 +377,7 @@ export const getMockDelegation = ({ delegateId = generateId(), eserviceId = generateId(), state = "WaitingForApproval", + submitterId = generateId(), activationContract = undefined, revocationContract = undefined, }: { @@ -385,6 +387,7 @@ export const getMockDelegation = ({ delegateId?: TenantId; eserviceId?: EServiceId; state?: DelegationState; + submitterId?: UserId; activationContract?: DelegationContractDocument; revocationContract?: DelegationContractDocument; }): Delegation => { @@ -403,7 +406,7 @@ export const getMockDelegation = ({ kind, stamps: { submission: { - who: delegatorId, + who: submitterId, when: creationTime, }, }, diff --git a/packages/delegation-process/src/services/delegationProducerService.ts b/packages/delegation-process/src/services/delegationProducerService.ts index 81c844f3f3..72963b4127 100644 --- a/packages/delegation-process/src/services/delegationProducerService.ts +++ b/packages/delegation-process/src/services/delegationProducerService.ts @@ -111,7 +111,7 @@ export function delegationProducerServiceBuilder( kind: delegationKind.delegatedProducer, stamps: { submission: { - who: delegatorId, + who: authData.userId, when: creationDate, }, }, @@ -166,7 +166,7 @@ export function delegationProducerServiceBuilder( stamps: { ...currentDelegation.data.stamps, revocation: { - who: delegatorId, + who: authData.userId, when: now, }, }, @@ -232,7 +232,7 @@ export function delegationProducerServiceBuilder( stamps: { ...delegation.stamps, activation: { - who: delegateId, + who: authData.userId, when: now, }, }, @@ -262,19 +262,20 @@ export function delegationProducerServiceBuilder( assertIsDelegate(delegation, delegateId); assertIsState(delegationState.waitingForApproval, delegation); + const now = new Date(); await repository.createEvent( toCreateEventProducerDelegationRejected( { data: { ...delegation, state: delegationState.rejected, - rejectedAt: new Date(), + rejectedAt: now, rejectionReason, stamps: { ...delegation.stamps, rejection: { - who: delegateId, - when: new Date(), + who: authData.userId, + when: now, }, }, }, diff --git a/packages/delegation-process/test/.eslintrc.json b/packages/delegation-process/test/.eslintrc.json index 6135a5ce08..9346af31d5 100644 --- a/packages/delegation-process/test/.eslintrc.json +++ b/packages/delegation-process/test/.eslintrc.json @@ -2,6 +2,7 @@ "extends": ["../../../.eslintrc.cjs"], "rules": { "functional/immutable-data": "off", - "sonarjs/no-identical-functions": "off" + "sonarjs/no-identical-functions": "off", + "@typescript-eslint/no-non-null-assertion": "off" } } diff --git a/packages/delegation-process/test/approveProducerDelegation.test.ts b/packages/delegation-process/test/approveProducerDelegation.test.ts index 69199af69a..1605edf592 100644 --- a/packages/delegation-process/test/approveProducerDelegation.test.ts +++ b/packages/delegation-process/test/approveProducerDelegation.test.ts @@ -4,7 +4,7 @@ import { getMockDelegation, getMockTenant, getMockEService, - getMockAuthData, + getRandomAuthData, } from "pagopa-interop-commons-test/index.js"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { @@ -63,6 +63,7 @@ describe("approve producer delegation", () => { it("should approve delegation if validations succeed", async () => { const delegationId = generateId(); + const authData = getRandomAuthData(delegate.id); const delegation = getMockDelegation({ kind: delegationKind.delegatedProducer, @@ -77,7 +78,7 @@ describe("approve producer delegation", () => { expect(version).toBe("0"); await delegationProducerService.approveProducerDelegation(delegation.id, { - authData: getMockAuthData(delegate.id), + authData, serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -107,7 +108,7 @@ describe("approve producer delegation", () => { stamps: { ...delegation.stamps, activation: { - who: delegate.id, + who: authData.userId, when: currentExecutionTime, }, }, @@ -163,7 +164,7 @@ describe("approve producer delegation", () => { delegationProducerService.approveProducerDelegation( nonExistentDelegationId, { - authData: getMockAuthData(delegateId), + authData: getRandomAuthData(delegateId), serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -186,7 +187,7 @@ describe("approve producer delegation", () => { await expect( delegationProducerService.approveProducerDelegation(delegation.id, { - authData: getMockAuthData(wrongDelegate.id), + authData: getRandomAuthData(wrongDelegate.id), serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -208,7 +209,7 @@ describe("approve producer delegation", () => { await expect( delegationProducerService.approveProducerDelegation(delegation.id, { - authData: getMockAuthData(delegate.id), + authData: getRandomAuthData(delegate.id), serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -235,7 +236,7 @@ describe("approve producer delegation", () => { expect(version).toBe("0"); await delegationProducerService.approveProducerDelegation(delegation.id, { - authData: getMockAuthData(delegate.id), + authData: getRandomAuthData(delegate.id), serviceName: "", correlationId: generateId(), logger: genericLogger, diff --git a/packages/delegation-process/test/createProducerDelegation.test.ts b/packages/delegation-process/test/createProducerDelegation.test.ts index 1463fba985..f3e42b73c5 100644 --- a/packages/delegation-process/test/createProducerDelegation.test.ts +++ b/packages/delegation-process/test/createProducerDelegation.test.ts @@ -137,7 +137,7 @@ describe("create producer delegation", () => { submittedAt: currentExecutionTime, stamps: { submission: { - who: delegatorId, + who: authData.userId, when: currentExecutionTime, }, }, @@ -216,7 +216,7 @@ describe("create producer delegation", () => { submittedAt: currentExecutionTime, stamps: { submission: { - who: delegatorId, + who: authData.userId, when: currentExecutionTime, }, }, diff --git a/packages/delegation-process/test/rejectProducerDelegation.test.ts b/packages/delegation-process/test/rejectProducerDelegation.test.ts index cf5afe2063..9fd50cbd59 100644 --- a/packages/delegation-process/test/rejectProducerDelegation.test.ts +++ b/packages/delegation-process/test/rejectProducerDelegation.test.ts @@ -1,9 +1,9 @@ /* eslint-disable functional/no-let */ import { decodeProtobufPayload, - getMockAuthData, getMockDelegation, getMockTenant, + getRandomAuthData, } from "pagopa-interop-commons-test/index.js"; import { describe, expect, it, vi } from "vitest"; import { @@ -34,6 +34,7 @@ describe("reject producer delegation", () => { vi.setSystemTime(currentExecutionTime); const delegate = getMockTenant(); + const authData = getRandomAuthData(delegate.id); const delegation = getMockDelegation({ kind: delegationKind.delegatedProducer, state: "WaitingForApproval", @@ -47,7 +48,7 @@ describe("reject producer delegation", () => { delegation.id, rejectionReason, { - authData: getMockAuthData(delegate.id), + authData, serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -67,7 +68,7 @@ describe("reject producer delegation", () => { rejectionReason, stamps: { ...delegation.stamps, - rejection: { who: delegate.id, when: currentExecutionTime }, + rejection: { who: authData.userId, when: currentExecutionTime }, }, }); expect(actualDelegation).toEqual(expectedDelegation); @@ -83,7 +84,7 @@ describe("reject producer delegation", () => { nonExistentDelegationId, "", { - authData: getMockAuthData(delegateId), + authData: getRandomAuthData(delegateId), serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -104,7 +105,7 @@ describe("reject producer delegation", () => { await expect( delegationProducerService.rejectProducerDelegation(delegation.id, "", { - authData: getMockAuthData(wrongDelegate.id), + authData: getRandomAuthData(wrongDelegate.id), serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -125,7 +126,7 @@ describe("reject producer delegation", () => { await expect( delegationProducerService.rejectProducerDelegation(delegation.id, "", { - authData: getMockAuthData(delegate.id), + authData: getRandomAuthData(delegate.id), serviceName: "", correlationId: generateId(), logger: genericLogger, diff --git a/packages/delegation-process/test/revokeProducerDelegation.test.ts b/packages/delegation-process/test/revokeProducerDelegation.test.ts index ffc5c3c287..1130cc07c3 100644 --- a/packages/delegation-process/test/revokeProducerDelegation.test.ts +++ b/packages/delegation-process/test/revokeProducerDelegation.test.ts @@ -18,6 +18,7 @@ import { unsafeBrandId, DelegationContractId, delegationKind, + UserId, } from "pagopa-interop-models"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { @@ -51,7 +52,7 @@ type DelegationStateSeed = }; stamps: { rejection: { - who: TenantId; + who: UserId; when: Date; }; }; @@ -63,7 +64,7 @@ type DelegationStateSeed = }; stamps: { revocation: { - who: TenantId; + who: UserId; when: Date; }; }; @@ -83,7 +84,7 @@ const getNotRevocableStateSeeds = (): DelegationStateSeed[] => { }, stamps: { rejection: { - who: generateId(), + who: generateId(), when: rejectionOrRevokeDate, }, }, @@ -95,7 +96,7 @@ const getNotRevocableStateSeeds = (): DelegationStateSeed[] => { }, stamps: { revocation: { - who: generateId(), + who: generateId(), when: rejectionOrRevokeDate, }, }, @@ -149,11 +150,11 @@ describe("revoke producer delegation", () => { submittedAt: delegationCreationDate, stamps: { submission: { - who: delegatorId, + who: generateId(), when: delegationCreationDate, }, activation: { - who: delegateId, + who: generateId(), when: delegationActivationDate, }, }, @@ -186,15 +187,15 @@ describe("revoke producer delegation", () => { revokedAt: currentExecutionTime, stamps: { submission: { - who: delegatorId, + who: existentDelegation.stamps.submission.who, when: delegationCreationDate, }, activation: { - who: delegateId, + who: existentDelegation.stamps.activation!.who, when: delegationActivationDate, }, revocation: { - who: delegatorId, + who: authData.userId, when: currentExecutionTime, }, }, @@ -297,11 +298,11 @@ describe("revoke producer delegation", () => { submittedAt: delegationCreationDate, stamps: { submission: { - who: delegatorId, + who: generateId(), when: delegationCreationDate, }, approval: { - who: delegateId, + who: generateId(), when: delegationApprovalDate, }, }, @@ -345,11 +346,11 @@ describe("revoke producer delegation", () => { submittedAt: delegationCreationDate, stamps: { submission: { - who: delegatorId, + who: generateId(), when: delegationCreationDate, }, activation: { - who: delegateId, + who: generateId(), when: delegationActivationDate, }, ...notRevocableDelegationState.stamps, diff --git a/packages/models/src/delegation/delegation.ts b/packages/models/src/delegation/delegation.ts index f9cc0dd3d6..5ad09fc983 100644 --- a/packages/models/src/delegation/delegation.ts +++ b/packages/models/src/delegation/delegation.ts @@ -4,6 +4,7 @@ import { DelegationId, EServiceId, TenantId, + UserId, } from "../brandedIds.js"; export const delegationKind = { @@ -42,7 +43,7 @@ export type DelegationContractDocument = z.infer< >; export const DelegationStamp = z.object({ - who: TenantId, + who: UserId, when: z.coerce.date(), }); export type DelegationStamp = z.infer; From cd3422bd0f85b4f90eeede138ee2545fae3ddd16 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Fri, 22 Nov 2024 12:37:07 +0100 Subject: [PATCH 34/34] PIN-5674 - Include descriptors in `Delegation` model in bff (#1199) --- packages/api-clients/open-api/bffApi.yml | 5 +++++ .../backend-for-frontend/src/api/delegationApiConverter.ts | 2 ++ 2 files changed, 7 insertions(+) diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index 9542323821..0cd2c52c31 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -15287,11 +15287,16 @@ components: format: uuid producerName: type: string + descriptors: + type: array + items: + $ref: "#/components/schemas/CompactDescriptor" required: - id - name - producerId - producerName + - descriptors Delegation: type: object additionalProperties: false diff --git a/packages/backend-for-frontend/src/api/delegationApiConverter.ts b/packages/backend-for-frontend/src/api/delegationApiConverter.ts index 3fa376b4b4..d6c9144c05 100644 --- a/packages/backend-for-frontend/src/api/delegationApiConverter.ts +++ b/packages/backend-for-frontend/src/api/delegationApiConverter.ts @@ -11,6 +11,7 @@ import { DelegationState, } from "pagopa-interop-models"; import { match } from "ts-pattern"; +import { toCompactDescriptor } from "./catalogApiConverter.js"; export type DelegationsQueryParams = { delegatorIds?: string[]; @@ -67,6 +68,7 @@ export function toBffDelegationApiDelegation( description: eservice.description, producerId: eservice.producerId, producerName: producer.name, + descriptors: eservice.descriptors.map(toCompactDescriptor), }, delegate: { id: delegate.id,

)l4iJW(o?;Sbk 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 OVB-vw9F{DiM*j(2qHrt# literal 0 HcmV?d00001 diff --git a/packages/delegation-process/src/resources/assets/font/Montserrat-SemiBold.ttf b/packages/delegation-process/src/resources/assets/font/Montserrat-SemiBold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ccaba1a86ca13fa394bfc3fa511bc5be0a20bb18 GIT binary patch literal 198720 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