diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ed497e40..b75aace9 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -17,6 +17,7 @@ "source=${env:HOME}${env:USERPROFILE}/.gitconfig,target=/home/vscode/.gitconfig,type=bind" ], "features": { + "ghcr.io/devcontainers/features/github-cli:1": {}, "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { "version": "latest", "moby": "true", diff --git a/.github/scripts/check-sbom-issues-against-ignores.sh b/.github/scripts/check-sbom-issues-against-ignores.sh deleted file mode 100755 index 8db75ab2..00000000 --- a/.github/scripts/check-sbom-issues-against-ignores.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# Usage: ./check-sbom-issues-against-ignores.sh -IGNORED_ISSUES_FILE="$1" -SCAN_RESULTS_FILE="$2" - -# Check if files exist -if [[ ! -f "$IGNORED_ISSUES_FILE" || ! -f "$SCAN_RESULTS_FILE" ]]; then - echo "Error: One or both of the required files do not exist." - exit 1 -fi - -# Read ignored issues into an array -mapfile -t IGNORED_ISSUES < <(jq -r '.[]' "$IGNORED_ISSUES_FILE") - -# Read scan results and check for critical vulnerabilities -CRITICAL_FOUND=false - -# Loop through vulnerabilities in the scan results -while IFS= read -r MATCH; do - VULN_ID=$(echo "$MATCH" | jq -r '.vulnerability.id') - - # Check if the vulnerability ID is in the ignored list - FOUND=false - for IGNORED in "${IGNORED_ISSUES[@]}"; do - if [[ "$IGNORED" == "$VULN_ID" ]]; then - FOUND=true - echo "Warning: Ignored vulnerability found: $VULN_ID" - break - fi - done - - # If the vulnerability is not found in the ignored list, mark critical as found - if [[ "$FOUND" == false ]]; then - echo "Error: Critical vulnerability found that is not in the ignore list: $VULN_ID" - CRITICAL_FOUND=true - fi -done < <(jq -c '.matches[] | select(.vulnerability.severity == "Critical")' "$SCAN_RESULTS_FILE") - -# Exit with error if critical vulnerability is found -if [[ "$CRITICAL_FOUND" == true ]]; then - exit 1 -fi - -echo "No unignored critical vulnerabilities found." -exit 0 diff --git a/.github/scripts/fix_cdk_json.sh b/.github/scripts/fix_cdk_json.sh deleted file mode 100755 index 443a41c8..00000000 --- a/.github/scripts/fix_cdk_json.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env bash -set -e - -# script used to set context key values in cdk.json pre deployment from environment variables - -# helper function to set string values -fix_string_key() { - KEY_NAME=$1 - KEY_VALUE=$2 - if [ -z "${KEY_VALUE}" ]; then - echo "${KEY_NAME} value is unset or set to the empty string" - exit 1 - fi - echo "Setting ${KEY_NAME}" - jq \ - --arg key_value "${KEY_VALUE}" \ - --arg key_name "${KEY_NAME}" \ - '. += {($key_name): $key_value}' "$OUTPUT_FILE_NAME" > "${TEMP_FILE}" - mv "${TEMP_FILE}" "$OUTPUT_FILE_NAME" -} - -# helper function to set boolean and number values (without quotes) -fix_boolean_number_key() { - KEY_NAME=$1 - KEY_VALUE=$2 - if [ -z "${KEY_VALUE}" ]; then - echo "${KEY_NAME} value is unset or set to the empty string" - exit 1 - fi - echo "Setting ${KEY_NAME}" - jq \ - --argjson key_value "${KEY_VALUE}" \ - --arg key_name "${KEY_NAME}" \ - '. += {($key_name): $key_value}' "$OUTPUT_FILE_NAME" > "${TEMP_FILE}" - mv "${TEMP_FILE}" "$OUTPUT_FILE_NAME" -} - - -OUTPUT_FILE_NAME=$1 -if [ -z "${OUTPUT_FILE_NAME}" ]; then - echo "OUTPUT_FILE_NAME value is unset or set to the empty string" - exit 1 -fi -echo "{}" > "$OUTPUT_FILE_NAME" -TEMP_FILE=$(mktemp) - -CFN_DRIFT_DETECTION_GROUP="epsam" -IS_PULL_REQUEST="false" -if [[ "$STACK_NAME" =~ -pr-[0-9]+$ ]]; then - CFN_DRIFT_DETECTION_GROUP="epsam-pull-request" - IS_PULL_REQUEST="true" -fi - -# go through all the key values we need to set -fix_string_key stackName "${STACK_NAME}" -fix_string_key versionNumber "${VERSION_NUMBER}" -fix_string_key commitId "${COMMIT_ID}" -fix_string_key logRetentionInDays "${LOG_RETENTION_IN_DAYS}" -fix_string_key logLevel "${LOG_LEVEL}" -fix_string_key enableBedrockLogging "${ENABLE_BEDROCK_LOGGING:-false}" -fix_string_key slackBotToken "${SLACK_BOT_TOKEN}" -fix_string_key slackSigningSecret "${SLACK_SIGNING_SECRET}" -fix_string_key cfnDriftDetectionGroup "${CFN_DRIFT_DETECTION_GROUP}" -fix_string_key csocApiGatewayDestination "arn:aws:logs:eu-west-2:693466633220:destination:api_gateway_log_destination" # CSOC API GW log destination - do not change -fix_boolean_number_key forwardCsocLogs "${FORWARD_CSOC_LOGS}" -fix_boolean_number_key isPullRequest "${IS_PULL_REQUEST}" -fix_boolean_number_key runRegressionTests "${RUN_REGRESSION_TESTS}" diff --git a/.github/workflows/cdk_package_code.yml b/.github/workflows/cdk_package_code.yml index 5b68c9a3..66c8ef6d 100644 --- a/.github/workflows/cdk_package_code.yml +++ b/.github/workflows/cdk_package_code.yml @@ -116,7 +116,6 @@ jobs: package-lock.json \ tsconfig.defaults.json \ Makefile \ - cdk.json \ .dependencies - uses: actions/upload-artifact@v6 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5946d710..56e485a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,10 @@ jobs: needs: [tag_release, package_code] uses: ./.github/workflows/release_all_stacks.yml with: - STACK_NAME: epsam + STATEFUL_STACK_NAME: epsam-stateful + STATELESS_STACK_NAME: epsam-stateless + BASE_PATH_MAPPING_STACK_NAME: epsam-bpm + API_GATEWAY_DOMAIN_NAME: epsam TARGET_ENVIRONMENT: dev VERSION_NUMBER: ${{ needs.tag_release.outputs.version_tag }} COMMIT_ID: ${{ github.sha }} @@ -87,7 +90,10 @@ jobs: needs: [tag_release, package_code, release_dev] uses: ./.github/workflows/release_all_stacks.yml with: - STACK_NAME: epsam + STATEFUL_STACK_NAME: epsam-stateful + STATELESS_STACK_NAME: epsam-stateless + BASE_PATH_MAPPING_STACK_NAME: epsam-bpm + API_GATEWAY_DOMAIN_NAME: epsam TARGET_ENVIRONMENT: qa VERSION_NUMBER: ${{ needs.tag_release.outputs.version_tag }} COMMIT_ID: ${{ github.sha }} diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 66f73dab..3b6675cc 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -169,7 +169,10 @@ jobs: ! contains(needs.*.result, 'cancelled') uses: ./.github/workflows/release_all_stacks.yml with: - STACK_NAME: epsam-pr-${{needs.get_issue_number.outputs.issue_number}} + STATEFUL_STACK_NAME: epsam-pr-${{needs.get_issue_number.outputs.issue_number}}-stateful + STATELESS_STACK_NAME: epsam-pr-${{needs.get_issue_number.outputs.issue_number}}-stateless-old + BASE_PATH_MAPPING_STACK_NAME: epsam-pr-${{needs.get_issue_number.outputs.issue_number}} + API_GATEWAY_DOMAIN_NAME: epsam-pr-${{needs.get_issue_number.outputs.issue_number}} TARGET_ENVIRONMENT: dev-pr VERSION_NUMBER: PR-${{ needs.get_issue_number.outputs.issue_number }} COMMIT_ID: ${{ github.sha }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 96d0af65..ba4049d3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,7 +56,10 @@ jobs: needs: [tag_release, package_code] uses: ./.github/workflows/release_all_stacks.yml with: - STACK_NAME: epsam + STATEFUL_STACK_NAME: epsam-stateful + STATELESS_STACK_NAME: epsam-stateless + BASE_PATH_MAPPING_STACK_NAME: epsam-bpm + API_GATEWAY_DOMAIN_NAME: epsam TARGET_ENVIRONMENT: dev VERSION_NUMBER: ${{ needs.tag_release.outputs.version_tag }} COMMIT_ID: ${{ github.sha }} @@ -86,7 +89,10 @@ jobs: needs: [tag_release, package_code, release_dev] uses: ./.github/workflows/release_all_stacks.yml with: - STACK_NAME: epsam + STATEFUL_STACK_NAME: epsam-stateful + STATELESS_STACK_NAME: epsam-stateless + BASE_PATH_MAPPING_STACK_NAME: epsam-bpm + API_GATEWAY_DOMAIN_NAME: epsam TARGET_ENVIRONMENT: qa VERSION_NUMBER: ${{ needs.tag_release.outputs.version_tag }} COMMIT_ID: ${{ github.sha }} @@ -116,7 +122,10 @@ jobs: needs: [tag_release, package_code, release_dev] uses: ./.github/workflows/release_all_stacks.yml with: - STACK_NAME: epsam + STATEFUL_STACK_NAME: epsam-stateful + STATELESS_STACK_NAME: epsam-stateless + BASE_PATH_MAPPING_STACK_NAME: epsam-bpm + API_GATEWAY_DOMAIN_NAME: epsam TARGET_ENVIRONMENT: ref VERSION_NUMBER: ${{ needs.tag_release.outputs.version_tag }} COMMIT_ID: ${{ github.sha }} @@ -146,7 +155,10 @@ jobs: needs: [tag_release, package_code, release_qa] uses: ./.github/workflows/release_all_stacks.yml with: - STACK_NAME: epsam + STATEFUL_STACK_NAME: epsam-stateful + STATELESS_STACK_NAME: epsam-stateless + BASE_PATH_MAPPING_STACK_NAME: epsam-bpm + API_GATEWAY_DOMAIN_NAME: epsam TARGET_ENVIRONMENT: int VERSION_NUMBER: ${{ needs.tag_release.outputs.version_tag }} COMMIT_ID: ${{ github.sha }} @@ -176,7 +188,10 @@ jobs: needs: [tag_release, package_code, release_int] uses: ./.github/workflows/release_all_stacks.yml with: - STACK_NAME: epsam + STATEFUL_STACK_NAME: epsam-stateful + STATELESS_STACK_NAME: epsam-stateless + BASE_PATH_MAPPING_STACK_NAME: epsam-bpm + API_GATEWAY_DOMAIN_NAME: epsam TARGET_ENVIRONMENT: prod VERSION_NUMBER: ${{ needs.tag_release.outputs.version_tag }} COMMIT_ID: ${{ github.sha }} diff --git a/.github/workflows/release_all_stacks.yml b/.github/workflows/release_all_stacks.yml index 8164f64e..3de09138 100644 --- a/.github/workflows/release_all_stacks.yml +++ b/.github/workflows/release_all_stacks.yml @@ -3,7 +3,16 @@ name: release all stacks on: workflow_call: inputs: - STACK_NAME: + STATEFUL_STACK_NAME: + required: true + type: string + STATELESS_STACK_NAME: + required: true + type: string + BASE_PATH_MAPPING_STACK_NAME: + required: true + type: string + API_GATEWAY_DOMAIN_NAME: required: true type: string TARGET_ENVIRONMENT: @@ -101,14 +110,6 @@ jobs: TARGET_CLOUD_FORMATION_CHECK_VERSION_ROLE: ${{ secrets.INT_CLOUD_FORMATION_CHECK_VERSION_ROLE }} DEV_CLOUD_FORMATION_EXECUTE_LAMBDA_ROLE: ${{ secrets.DEV_CLOUD_FORMATION_EXECUTE_LAMBDA_ROLE }} - - name: Configure AWS Credentials - id: connect_aws_pull_image - uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 - with: - aws-region: eu-west-2 - role-to-assume: ${{ secrets.CDK_PULL_IMAGE_ROLE }} - role-session-name: eps-assist-me-pull-image - - name: build_artifact download uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 with: @@ -117,21 +118,7 @@ jobs: - name: extract build_artifact run: | mkdir -p .build - tar -xf artifact.tar -C .build - - - name: Retrieve AWS Account ID - id: retrieve_aws_account_id - run: echo "ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)" >> "$GITHUB_ENV" - - - name: Login to Amazon ECR - id: login_ecr - run: | - aws ecr get-login-password --region eu-west-2 | docker login --username AWS --password-stdin ${{ env.ACCOUNT_ID }}.dkr.ecr.eu-west-2.amazonaws.com - - - name: Pull cdk-utils-build from Amazon ECR - run: | - docker pull "${{ env.ACCOUNT_ID }}.dkr.ecr.eu-west-2.amazonaws.com/cdk-utils-build-repo:latest" - docker tag "${{ env.ACCOUNT_ID }}.dkr.ecr.eu-west-2.amazonaws.com/cdk-utils-build-repo:latest" cdk-utils-build-repo:latest + tar -xf artifact.tar - name: Configure AWS Credentials id: connect_aws_for_deployment @@ -140,55 +127,78 @@ jobs: aws-region: eu-west-2 role-to-assume: ${{ secrets.CLOUD_FORMATION_DEPLOY_ROLE }} role-session-name: eps-assist-me-deployment - output-credentials: true - - - name: fix cdk.json for deployment - run: | - ./.github/scripts/fix_cdk_json.sh .build/epsam_app.json + output-credentials: false + + - name: Deploy stateful app + run: npm run cdk-deploy --workspace packages/cdk + shell: bash env: - ACCOUNT_ID: "${{ env.ACCOUNT_ID }}" - STACK_NAME: "${{ inputs.STACK_NAME }}" - VERSION_NUMBER: "${{ inputs.VERSION_NUMBER }}" - COMMIT_ID: "${{ inputs.COMMIT_ID }}" - LOG_RETENTION_IN_DAYS: "${{ inputs.LOG_RETENTION_IN_DAYS }}" - LOG_LEVEL: "${{ inputs.LOG_LEVEL }}" - SLACK_BOT_TOKEN: "${{ secrets.SLACK_BOT_TOKEN }}" - SLACK_SIGNING_SECRET: "${{ secrets.SLACK_SIGNING_SECRET }}" - CDK_APP_NAME: ${{ inputs.CDK_APP_NAME }} - FORWARD_CSOC_LOGS: ${{ inputs.FORWARD_CSOC_LOGS }} - IS_PULL_REQUEST: ${{ inputs.IS_PULL_REQUEST }} - RUN_REGRESSION_TESTS: ${{ inputs.RUN_REGRESSION_TESTS }} - - - name: Show diff for stack - run: | - docker run \ - -v "$(pwd)/.build":/home/cdkuser/workspace/ \ - -e AWS_ACCESS_KEY_ID=${{ steps.connect_aws_for_deployment.outputs.aws-access-key-id }} \ - -e AWS_SECRET_ACCESS_KEY=${{ steps.connect_aws_for_deployment.outputs.aws-secret-access-key }} \ - -e AWS_SESSION_TOKEN=${{ steps.connect_aws_for_deployment.outputs.aws-session-token }} \ - -e AWS_REGION="eu-west-2" \ - -e SHOW_DIFF="true" \ - -e DEPLOY_CODE="false" \ - -e CONFIG_FILE_NAME="epsam_app.json" \ - -e CDK_APP_PATH="packages/cdk/bin/EpsAssistMeApp.ts" \ - cdk-utils-build-repo:latest + CDK_APP_NAME: "EpsAssistMeApp" + CDK_STACK_NAME: "EpsAssistMeStateful" + CDK_CONFIG_stackName: "${{ inputs.STATEFUL_STACK_NAME }}" + CDK_CONFIG_versionNumber: "${{ inputs.VERSION_NUMBER }}" + CDK_CONFIG_commitId: "${{ inputs.COMMIT_ID }}" + CDK_CONFIG_isPullRequest: "${{ inputs.IS_PULL_REQUEST }}" + CDK_CONFIG_environment: "${{ inputs.TARGET_ENVIRONMENT }}" + CDK_CONFIG_logRetentionInDays: "${{ inputs.LOG_RETENTION_IN_DAYS }}" + CDK_CONFIG_logLevel: "${{ inputs.LOG_LEVEL }}" + CDK_CONFIG_domainName: "${{ inputs.API_GATEWAY_DOMAIN_NAME }}" + CDK_CONFIG_enableBedrockLogging: false + CDK_CONFIG_runRegressionTests: "${{ inputs.RUN_REGRESSION_TESTS }}" + CDK_CONFIG_forwardCsocLogs: "${{ inputs.FORWARD_CSOC_LOGS }}" + CDK_CONFIG_slackBotToken: "${{ secrets.SLACK_BOT_TOKEN }}" + CDK_CONFIG_slackSigningSecret: "${{ secrets.SLACK_SIGNING_SECRET }}" + CDK_CONFIG_statefulStackName: "${{ inputs.STATEFUL_STACK_NAME }}" + CDK_CONFIG_statelessStackName: "${{ inputs.STATELESS_STACK_NAME }}" + CDK_CONFIG_basePathMappingStackName: "${{ inputs.BASE_PATH_MAPPING_STACK_NAME }}" + REQUIRE_APPROVAL: "never" + + - name: Deploy stateless app + run: npm run cdk-deploy --workspace packages/cdk shell: bash - - - name: Deploy code for stack - if: ${{ inputs.DEPLOY_CODE == true }} - run: | - docker run \ - -v "$(pwd)/.build":/home/cdkuser/workspace/ \ - -e AWS_ACCESS_KEY_ID=${{ steps.connect_aws_for_deployment.outputs.aws-access-key-id }} \ - -e AWS_SECRET_ACCESS_KEY=${{ steps.connect_aws_for_deployment.outputs.aws-secret-access-key }} \ - -e AWS_SESSION_TOKEN=${{ steps.connect_aws_for_deployment.outputs.aws-session-token }} \ - -e AWS_REGION="eu-west-2" \ - -e SHOW_DIFF="false" \ - -e DEPLOY_CODE="true" \ - -e CONFIG_FILE_NAME="epsam_app.json" \ - -e CDK_APP_PATH="packages/cdk/bin/EpsAssistMeApp.ts" \ - cdk-utils-build-repo:latest + env: + CDK_APP_NAME: "EpsAssistMeApp" + CDK_STACK_NAME: "EpsAssistMeStateless" + CDK_CONFIG_stackName: "${{ inputs.STATELESS_STACK_NAME }}" + CDK_CONFIG_versionNumber: "${{ inputs.VERSION_NUMBER }}" + CDK_CONFIG_commitId: "${{ inputs.COMMIT_ID }}" + CDK_CONFIG_isPullRequest: "${{ inputs.IS_PULL_REQUEST }}" + CDK_CONFIG_environment: "${{ inputs.TARGET_ENVIRONMENT }}" + CDK_CONFIG_logRetentionInDays: "${{ inputs.LOG_RETENTION_IN_DAYS }}" + CDK_CONFIG_logLevel: "${{ inputs.LOG_LEVEL }}" + CDK_CONFIG_domainName: "${{ inputs.API_GATEWAY_DOMAIN_NAME }}" + CDK_CONFIG_enableBedrockLogging: false + CDK_CONFIG_runRegressionTests: "${{ inputs.RUN_REGRESSION_TESTS }}" + CDK_CONFIG_forwardCsocLogs: "${{ inputs.FORWARD_CSOC_LOGS }}" + CDK_CONFIG_slackBotToken: "${{ secrets.SLACK_BOT_TOKEN }}" + CDK_CONFIG_slackSigningSecret: "${{ secrets.SLACK_SIGNING_SECRET }}" + CDK_CONFIG_statefulStackName: "${{ inputs.STATEFUL_STACK_NAME }}" + CDK_CONFIG_statelessStackName: "${{ inputs.STATELESS_STACK_NAME }}" + CDK_CONFIG_basePathMappingStackName: "${{ inputs.BASE_PATH_MAPPING_STACK_NAME }}" + REQUIRE_APPROVAL: "never" + - name: Deploy basepath mapping app + run: npm run cdk-deploy --workspace packages/cdk shell: bash + env: + CDK_APP_NAME: "EpsAssistMeApp" + CDK_STACK_NAME: "EpsAssistMeBasepathMapping" + CDK_CONFIG_stackName: "${{ inputs.BASE_PATH_MAPPING_STACK_NAME }}" + CDK_CONFIG_versionNumber: "${{ inputs.VERSION_NUMBER }}" + CDK_CONFIG_commitId: "${{ inputs.COMMIT_ID }}" + CDK_CONFIG_isPullRequest: "${{ inputs.IS_PULL_REQUEST }}" + CDK_CONFIG_environment: "${{ inputs.TARGET_ENVIRONMENT }}" + CDK_CONFIG_logRetentionInDays: "${{ inputs.LOG_RETENTION_IN_DAYS }}" + CDK_CONFIG_logLevel: "${{ inputs.LOG_LEVEL }}" + CDK_CONFIG_domainName: "${{ inputs.API_GATEWAY_DOMAIN_NAME }}" + CDK_CONFIG_enableBedrockLogging: false + CDK_CONFIG_runRegressionTests: "${{ inputs.RUN_REGRESSION_TESTS }}" + CDK_CONFIG_forwardCsocLogs: "${{ inputs.FORWARD_CSOC_LOGS }}" + CDK_CONFIG_slackBotToken: "${{ secrets.SLACK_BOT_TOKEN }}" + CDK_CONFIG_slackSigningSecret: "${{ secrets.SLACK_SIGNING_SECRET }}" + CDK_CONFIG_statefulStackName: "${{ inputs.STATEFUL_STACK_NAME }}" + CDK_CONFIG_statelessStackName: "${{ inputs.STATELESS_STACK_NAME }}" + CDK_CONFIG_basePathMappingStackName: "${{ inputs.BASE_PATH_MAPPING_STACK_NAME }}" + REQUIRE_APPROVAL: "never" - name: Normalize Environment Name if: ${{ inputs.TARGET_ENVIRONMENT != 'int' && (inputs.DEPLOY_CODE == true || inputs.IS_PULL_REQUEST == true) }} @@ -199,14 +209,14 @@ jobs: env: TARGET_ENVIRONMENT: ${{ inputs.TARGET_ENVIRONMENT }} - - name: Sync Documents - uses: ./.github/actions/sync_documents - if: ${{ inputs.TARGET_ENVIRONMENT != 'int' && (inputs.DEPLOY_CODE == true || inputs.IS_PULL_REQUEST == true) }} - with: - TARGET_ENVIRONMENT: ${{ inputs.TARGET_ENVIRONMENT }} - STACK: ${{ inputs.STACK_NAME }} - INT_CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.INT_CLOUD_FORMATION_EXECUTE_LAMBDA_ROLE }} - TARGET_CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets[format('{0}_CLOUD_FORMATION_EXECUTE_LAMBDA_ROLE', github.event.inputs.UPPER_TARGET_ENVIRONMENT)] }} + # - name: Sync Documents + # uses: ./.github/actions/sync_documents + # if: ${{ inputs.TARGET_ENVIRONMENT != 'int' && (inputs.DEPLOY_CODE == true || inputs.IS_PULL_REQUEST == true) }} + # with: + # TARGET_ENVIRONMENT: ${{ inputs.TARGET_ENVIRONMENT }} + # STACK: ${{ inputs.STACK_NAME }} + # INT_CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.INT_CLOUD_FORMATION_EXECUTE_LAMBDA_ROLE }} + # TARGET_CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets[format('{0}_CLOUD_FORMATION_EXECUTE_LAMBDA_ROLE', github.event.inputs.UPPER_TARGET_ENVIRONMENT)] }} - name: create_int_release_notes uses: ./.github/actions/update_confluence_jira diff --git a/Makefile b/Makefile index f134878a..fa48ca87 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,12 @@ +SHELL = /bin/bash +.SHELLFLAGS = -o pipefail -c +export CDK_CONFIG_versionNumber=undefined +export CDK_CONFIG_commitId=undefined +export CDK_CONFIG_logRetentionInDays=30 +export CDK_CONFIG_logLevel=DEBUG +export CDK_CONFIG_forwardCsocLogs=false +export CDK_CONFIG_environment=dev + guard-%: @ if [ "${${*}}" = "" ]; then \ echo "Environment variable $* not set"; \ @@ -59,7 +68,7 @@ clean: rm -rf packages/slackBotFunction/.dependencies rm -rf packages/syncKnowledgeBaseFunction/coverage rm -rf .dependencies/ - rm -rf cdk.out + find . -name 'cdk.out' -type d -prune -exec rm -rf '{}' + rm -rf .build rm -rf .local_config rm -rf cfn_guard_output @@ -87,73 +96,66 @@ aws-login: cfn-guard: ./scripts/run_cfn_guard.sh -cdk-deploy: guard-STACK_NAME - REQUIRE_APPROVAL="$${REQUIRE_APPROVAL:-any-change}" && \ - VERSION_NUMBER="$${VERSION_NUMBER:-undefined}" && \ - COMMIT_ID="$${COMMIT_ID:-undefined}" && \ - npx cdk deploy \ - --app "npx ts-node --prefer-ts-exts packages/cdk/bin/EpsAssistMeApp.ts" \ - --all \ - --ci true \ - --require-approval $${REQUIRE_APPROVAL} \ - --context accountId=$$ACCOUNT_ID \ - --context stackName=$$STACK_NAME \ - --context versionNumber=$$VERSION_NUMBER \ - --context commitId=$$COMMIT_ID \ - --context logRetentionInDays=$$LOG_RETENTION_IN_DAYS \ - --context slackBotToken=$$SLACK_BOT_TOKEN \ - --context slackSigningSecret=$$SLACK_SIGNING_SECRET - cdk-synth: cdk-synth-pr cdk-synth-non-pr -cdk-synth-non-pr: +cdk-synth-pr: mkdir -p .dependencies/slackBotFunction mkdir -p .dependencies/syncKnowledgeBaseFunction mkdir -p .dependencies/preprocessingFunction mkdir -p .dependencies/bedrockLoggingConfigFunction mkdir -p .local_config - STACK_NAME=epsam \ - COMMIT_ID=undefined \ - VERSION_NUMBER=undefined \ - SLACK_BOT_TOKEN=dummy_token \ - SLACK_SIGNING_SECRET=dummy_secret \ - LOG_RETENTION_IN_DAYS=30 \ - LOG_LEVEL=debug \ - FORWARD_CSOC_LOGS=false \ - RUN_REGRESSION_TESTS=true \ - ./.github/scripts/fix_cdk_json.sh .local_config/epsam.config.json - CONFIG_FILE_NAME=.local_config/epsam.config.json npx cdk synth \ - --quiet \ - --app "npx ts-node --prefer-ts-exts packages/cdk/bin/EpsAssistMeApp.ts" + CDK_APP_NAME=EpsAssistMeApp \ + CDK_CONFIG_stackName=epsam-bpm \ + CDK_CONFIG_isPullRequest=true \ + CDK_CONFIG_domainName=epsam \ + CDK_CONFIG_enableBedrockLogging=false \ + CDK_CONFIG_runRegressionTests=true \ + CDK_CONFIG_forwardCsocLogs=true \ + CDK_CONFIG_slackBotToken=foo \ + CDK_CONFIG_slackSigningSecret=bar \ + CDK_CONFIG_statefulStackName=epsam-stateful \ + CDK_CONFIG_statelessStackName=epsam-stateless \ + CDK_CONFIG_basePathMappingStackName=epsam-basepathmapping \ + npm run cdk-synth --workspace packages/cdk/ -cdk-synth-pr: +cdk-synth-non-pr: mkdir -p .dependencies/slackBotFunction mkdir -p .dependencies/syncKnowledgeBaseFunction mkdir -p .dependencies/preprocessingFunction mkdir -p .dependencies/bedrockLoggingConfigFunction mkdir -p .local_config - STACK_NAME=epsam-pr-123 \ - COMMIT_ID=undefined \ - VERSION_NUMBER=undefined \ - SLACK_BOT_TOKEN=dummy_token \ - SLACK_SIGNING_SECRET=dummy_secret \ - LOG_RETENTION_IN_DAYS=30 \ - LOG_LEVEL=debug \ - FORWARD_CSOC_LOGS=false \ - RUN_REGRESSION_TESTS=true \ - ./.github/scripts/fix_cdk_json.sh .local_config/epsam.config.json - CONFIG_FILE_NAME=.local_config/epsam.config.json npx cdk synth \ - --quiet \ - --app "npx ts-node --prefer-ts-exts packages/cdk/bin/EpsAssistMeApp.ts" - -cdk-diff: - npx cdk diff \ - --app "npx ts-node --prefer-ts-exts packages/cdk/bin/EpsAssistMeApp.ts" \ - --context accountId=$$ACCOUNT_ID \ - --context stackName=$$STACK_NAME \ - --context versionNumber=$$VERSION_NUMBER \ - --context commitId=$$COMMIT_ID \ - --context logRetentionInDays=$$LOG_RETENTION_IN_DAYS + CDK_APP_NAME=EpsAssistMeApp \ + CDK_CONFIG_stackName=epsam-bpm \ + CDK_CONFIG_isPullRequest=false \ + CDK_CONFIG_domainName=epsam \ + CDK_CONFIG_enableBedrockLogging=false \ + CDK_CONFIG_runRegressionTests=true \ + CDK_CONFIG_forwardCsocLogs=true \ + CDK_CONFIG_slackBotToken=foo \ + CDK_CONFIG_slackSigningSecret=bar \ + CDK_CONFIG_statefulStackName=epsam-stateful \ + CDK_CONFIG_statelessStackName=epsam-stateless \ + CDK_CONFIG_basePathMappingStackName=epsam-basepathmapping \ + npm run cdk-synth --workspace packages/cdk/ + +cdk-flags: + mkdir -p .dependencies/slackBotFunction + mkdir -p .dependencies/syncKnowledgeBaseFunction + mkdir -p .dependencies/preprocessingFunction + mkdir -p .dependencies/bedrockLoggingConfigFunction + mkdir -p .local_config + CDK_APP_NAME=EpsAssistMeApp \ + CDK_CONFIG_stackName=epsam-bpm \ + CDK_CONFIG_isPullRequest=false \ + CDK_CONFIG_domainName=epsam \ + CDK_CONFIG_enableBedrockLogging=false \ + CDK_CONFIG_runRegressionTests=true \ + CDK_CONFIG_forwardCsocLogs=true \ + CDK_CONFIG_slackBotToken=foo \ + CDK_CONFIG_slackSigningSecret=bar \ + CDK_CONFIG_statefulStackName=epsam-stateful \ + CDK_CONFIG_statelessStackName=epsam-stateless \ + npm run cdk-flags --workspace packages/cdk/ cdk-watch: ./scripts/run_sync.sh @@ -174,3 +176,8 @@ convert-docs-file: compile: echo "Does nothing currently" + +create-npmrc: + gh auth login --scopes "read:packages"; \ + echo "//npm.pkg.github.com/:_authToken=$$(gh auth token)" > .npmrc + echo "@nhsdigital:registry=https://npm.pkg.github.com" >> .npmrc diff --git a/eslint.config.mjs b/eslint.config.mjs index d38407fa..e8dccba4 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -96,7 +96,7 @@ const commonConfig = { export default [ { - ignores: ["**/lib/*", "**/coverage/*"], + ignores: ["**/lib/*", "**/coverage/*", "**/node_modules/*", "**/cdk.out/*"], }, { rules: eslintJsPlugin.configs.recommended.rules, diff --git a/nhsdigital-eps-cdk-constructs-1.0.0.tgz b/nhsdigital-eps-cdk-constructs-1.0.0.tgz new file mode 100644 index 00000000..ee9dfb38 Binary files /dev/null and b/nhsdigital-eps-cdk-constructs-1.0.0.tgz differ diff --git a/package-lock.json b/package-lock.json index 88d611d8..2dd8f0e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -91,1868 +91,3631 @@ "node": ">=10" } }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-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.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.28.5" - }, - "bin": { - "parser": "bin/babel-parser.js" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/client-cloudformation": { + "version": "3.982.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudformation/-/client-cloudformation-3.982.0.tgz", + "integrity": "sha512-KRNcUl9rC8IHccwlz/0SiXsMbT1VGvNry4JJ2yzjHkWgIYXJb7ti4e04JX+9ajl1LNGHX+Zt28Gzh3gVK9nlAw==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/credential-provider-node": "^3.972.5", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.982.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-route-53": { + "version": "3.982.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-route-53/-/client-route-53-3.982.0.tgz", + "integrity": "sha512-MVCkA0dv0A333UGT+B2iVywauzrc5FOo7rPPhVtzIhNO3ZhEA0d0Qm2troyMOPgBKFRIVzomj1jeeXUn5LZhBg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/credential-provider-node": "^3.972.5", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-sdk-route53": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.982.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.982.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.982.0.tgz", + "integrity": "sha512-k0ANYAtPiON9BwLXcDgJXkmmCAGEuSk2pZOvrMej2kNhs3xTXoPshIUR5UMCD9apYiWtXJJfXMZSgaME+iWNaQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/credential-provider-node": "^3.972.5", + "@aws-sdk/middleware-bucket-endpoint": "^3.972.3", + "@aws-sdk/middleware-expect-continue": "^3.972.3", + "@aws-sdk/middleware-flexible-checksums": "^3.972.4", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-location-constraint": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-sdk-s3": "^3.972.6", + "@aws-sdk/middleware-ssec": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/signature-v4-multi-region": "3.982.0", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.982.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/eventstream-serde-browser": "^4.2.8", + "@smithy/eventstream-serde-config-resolver": "^4.3.8", + "@smithy/eventstream-serde-node": "^4.2.8", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-blob-browser": "^4.2.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/hash-stream-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/md5-js": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-stream": "^4.5.10", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.982.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.982.0.tgz", + "integrity": "sha512-qJrIiivmvujdGqJ0ldSUvhN3k3N7GtPesoOI1BSt0fNXovVnMz4C/JmnkhZihU7hJhDvxJaBROLYTU+lpild4w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.982.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.973.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.6.tgz", + "integrity": "sha512-pz4ZOw3BLG0NdF25HoB9ymSYyPbMiIjwQJ2aROXRhAzt+b+EOxStfFv8s5iZyP6Kiw7aYhyWxj5G3NhmkoOTKw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/xml-builder": "^3.972.4", + "@smithy/core": "^3.22.0", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/crc64-nvme": { + "version": "3.972.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.0.tgz", + "integrity": "sha512-ThlLhTqX68jvoIVv+pryOdb5coP1cX1/MaTbB9xkGDCbWbsqQcLqzPxuSoW1DCnAAIacmXCWpzUNOB9pv+xXQw==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.4.tgz", + "integrity": "sha512-/8dnc7+XNMmViEom2xsNdArQxQPSgy4Z/lm6qaFPTrMFesT1bV3PsBhb19n09nmxHdrtQskYmViddUIjUQElXg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.6.tgz", + "integrity": "sha512-5ERWqRljiZv44AIdvIRQ3k+EAV0Sq2WeJHvXuK7gL7bovSxOf8Al7MLH7Eh3rdovH4KHFnlIty7J71mzvQBl5Q==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/types": "^3.973.1", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.10", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.4.tgz", + "integrity": "sha512-eRUg+3HaUKuXWn/lEMirdiA5HOKmEl8hEHVuszIDt2MMBUKgVX5XNGmb3XmbgU17h6DZ+RtjbxQpjhz3SbTjZg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/credential-provider-env": "^3.972.4", + "@aws-sdk/credential-provider-http": "^3.972.6", + "@aws-sdk/credential-provider-login": "^3.972.4", + "@aws-sdk/credential-provider-process": "^3.972.4", + "@aws-sdk/credential-provider-sso": "^3.972.4", + "@aws-sdk/credential-provider-web-identity": "^3.972.4", + "@aws-sdk/nested-clients": "3.982.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.4.tgz", + "integrity": "sha512-nLGjXuvWWDlQAp505xIONI7Gam0vw2p7Qu3P6on/W2q7rjJXtYjtpHbcsaOjJ/pAju3eTvEQuSuRedcRHVQIAQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/nested-clients": "3.982.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.5.tgz", + "integrity": "sha512-VWXKgSISQCI2GKN3zakTNHSiZ0+mux7v6YHmmbLQp/o3fvYUQJmKGcLZZzg2GFA+tGGBStplra9VFNf/WwxpYg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-sdk/credential-provider-env": "^3.972.4", + "@aws-sdk/credential-provider-http": "^3.972.6", + "@aws-sdk/credential-provider-ini": "^3.972.4", + "@aws-sdk/credential-provider-process": "^3.972.4", + "@aws-sdk/credential-provider-sso": "^3.972.4", + "@aws-sdk/credential-provider-web-identity": "^3.972.4", + "@aws-sdk/types": "^3.973.1", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.4.tgz", + "integrity": "sha512-TCZpWUnBQN1YPk6grvd5x419OfXjHvhj5Oj44GYb84dOVChpg/+2VoEj+YVA4F4E/6huQPNnX7UYbTtxJqgihw==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.4.tgz", + "integrity": "sha512-wzsGwv9mKlwJ3vHLyembBvGE/5nPUIwRR2I51B1cBV4Cb4ql9nIIfpmHzm050XYTY5fqTOKJQnhLj7zj89VG8g==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@aws-sdk/client-sso": "3.982.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/token-providers": "3.982.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.4.tgz", + "integrity": "sha512-hIzw2XzrG8jzsUSEatehmpkd5rWzASg5IHUfA+m01k/RtvfAML7ZJVVohuKdhAYx+wV2AThLiQJVzqn7F0khrw==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/nested-clients": "3.982.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.3.tgz", + "integrity": "sha512-fmbgWYirF67YF1GfD7cg5N6HHQ96EyRNx/rDIrTF277/zTWVuPI2qS/ZHgofwR1NZPe/NWvoppflQY01LrbVLg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-arn-parser": "^3.972.2", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-config-provider": "^4.2.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.3.tgz", + "integrity": "sha512-4msC33RZsXQpUKR5QR4HnvBSNCPLGHmB55oDiROqqgyOc+TOfVu2xgi5goA7ms6MdZLeEh2905UfWMnMMF4mRg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.972.4.tgz", + "integrity": "sha512-xOxsUkF3O3BtIe3tf54OpPo94eZepjFm3z0Dd2TZKbsPxMiRTFXurC04wJ58o/wPW9YHVO9VqZik3MfoPfrKlw==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/crc64-nvme": "3.972.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-stream": "^4.5.10", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.3.tgz", + "integrity": "sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.3.tgz", + "integrity": "sha512-nIg64CVrsXp67vbK0U1/Is8rik3huS3QkRHn2DRDx4NldrEFMgdkZGI/+cZMKD9k4YOS110Dfu21KZLHrFA/1g==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.3.tgz", + "integrity": "sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.3.tgz", + "integrity": "sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@aws-sdk/types": "^3.973.1", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-sdk-route53": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-route53/-/middleware-sdk-route53-3.972.3.tgz", + "integrity": "sha512-jEycE4PnvYzkXjKN7yTB64tquyEtB5K06J+K+guZVtrYeD6HaWwLU1qJOnuHECVl6tDsDNuU57TNpuS29q3i9A==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.972.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.6.tgz", + "integrity": "sha512-Xq7wM6kbgJN1UO++8dvH/efPb1nTwWqFCpZCR7RCLOETP7xAUAhVo7JmsCnML5Di/iC4Oo5VrJ4QmkYcMZniLw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-arn-parser": "^3.972.2", + "@smithy/core": "^3.22.0", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-stream": "^4.5.10", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.3.tgz", + "integrity": "sha512-dU6kDuULN3o3jEHcjm0c4zWJlY1zWVkjG9NPe9qxYLLpcbdj5kRYBS2DdWYD+1B9f910DezRuws7xDEqKkHQIg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=20.0.0" } }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.972.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.6.tgz", + "integrity": "sha512-TehLN8W/kivl0U9HcS+keryElEWORROpghDXZBLfnb40DXM7hx/i+7OOjkogXQOF3QtUraJVRkHQ07bPhrWKlw==", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.982.0", + "@smithy/core": "^3.22.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=20.0.0" } }, - "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/nested-clients": { + "version": "3.982.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.982.0.tgz", + "integrity": "sha512-VVkaH27digrJfdVrT64rjkllvOp4oRiZuuJvrylLXAKl18ujToJR7AqpDldL/LS63RVne3QWIpkygIymxFtliQ==", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", - "debug": "^4.3.1" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.982.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.3.tgz", + "integrity": "sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/config-resolver": "^4.4.6", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=20.0.0" } }, - "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.982.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.982.0.tgz", + "integrity": "sha512-AWqjMAH848aNwnLCtIKM3WO00eHuUoYVfQMP4ccrUHhnEduGOusVgdHQ5mLNQZZNZzREuBwnPPhIP55cy0gFSg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@aws-sdk/middleware-sdk-s3": "^3.972.6", + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=20.0.0" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@cdklabs/generative-ai-cdk-constructs": { - "version": "0.1.314", - "resolved": "https://registry.npmjs.org/@cdklabs/generative-ai-cdk-constructs/-/generative-ai-cdk-constructs-0.1.314.tgz", - "integrity": "sha512-dTixKX0jZoMr4jVPHuNOIRpiWndCOO2ttbGSZUD3T3juf/9Ex2rVEFxzFFsUEyLQcn4zmlADFaQHnHefncXv4Q==", - "bundleDependencies": [ - "@aws-cdk/aws-lambda-python-alpha", - "deepmerge" - ], + "node_modules/@aws-sdk/token-providers": { + "version": "3.982.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.982.0.tgz", + "integrity": "sha512-v3M0KYp2TVHYHNBT7jHD9lLTWAdS9CaWJ2jboRKt0WAB65bA7iUEpR+k4VqKYtpQN4+8kKSc4w+K6kUNZkHKQw==", "license": "Apache-2.0", "dependencies": { - "@aws-cdk/aws-lambda-python-alpha": "2.233.0-alpha.0", - "cdk-nag": "^2.37.55", - "deepmerge": "^4.3.1" + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/nested-clients": "3.982.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 20.x <= 24.x" - }, - "peerDependencies": { - "aws-cdk-lib": "^2.233.0", - "constructs": "^10.3.0" + "node": ">=20.0.0" } }, - "node_modules/@cdklabs/generative-ai-cdk-constructs/node_modules/@aws-cdk/aws-lambda-python-alpha": { - "version": "2.233.0-alpha.0", - "inBundle": true, + "node_modules/@aws-sdk/types": { + "version": "3.973.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.1.tgz", + "integrity": "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg==", "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 18.0.0" + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.972.2", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.2.tgz", + "integrity": "sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.233.0", - "constructs": "^10.0.0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@cdklabs/generative-ai-cdk-constructs/node_modules/deepmerge": { - "version": "4.3.1", - "inBundle": true, - "license": "MIT", + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.982.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.982.0.tgz", + "integrity": "sha512-M27u8FJP7O0Of9hMWX5dipp//8iglmV9jr7R8SR8RveU+Z50/8TqH68Tu6wUWBGMfXjzbVwn1INIAO5lZrlxXQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=20.0.0" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.4.tgz", + "integrity": "sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=20.0.0" } }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.3.tgz", + "integrity": "sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/@emnapi/core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", - "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", - "dev": true, - "license": "MIT", - "optional": true, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.4.tgz", + "integrity": "sha512-3WFCBLiM8QiHDfosQq3Py+lIMgWlFWwFQliUHUqwEiRqLnKyhgbU3AKa7AWJF7lW2Oc/2kFNY4MlAYVnVc0i8A==", + "license": "Apache-2.0", "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/types": "^3.973.1", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@emnapi/runtime": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", - "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.4.tgz", + "integrity": "sha512-0zJ05ANfYqI6+rGqj8samZBFod0dPPousBjLEqg8WdxSgbMAkRgLyn81lP215Do0rFJ/17LIXwr7q0yK24mP6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "fast-xml-parser": "5.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz", + "integrity": "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", - "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", - "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", - "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/generator": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.0.tgz", + "integrity": "sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", - "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", - "cpu": [ - "x64" - ], + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", - "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", - "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", - "cpu": [ - "x64" - ], + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", - "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", - "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", - "cpu": [ - "x64" - ], + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", - "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", - "cpu": [ - "arm" - ], + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", - "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", - "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", - "cpu": [ - "ia32" - ], + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", - "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", - "cpu": [ - "loong64" - ], + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", - "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", - "cpu": [ - "mips64el" - ], + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", - "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", - "cpu": [ + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cdklabs/generative-ai-cdk-constructs": { + "version": "0.1.314", + "resolved": "https://registry.npmjs.org/@cdklabs/generative-ai-cdk-constructs/-/generative-ai-cdk-constructs-0.1.314.tgz", + "integrity": "sha512-dTixKX0jZoMr4jVPHuNOIRpiWndCOO2ttbGSZUD3T3juf/9Ex2rVEFxzFFsUEyLQcn4zmlADFaQHnHefncXv4Q==", + "bundleDependencies": [ + "@aws-cdk/aws-lambda-python-alpha", + "deepmerge" + ], + "license": "Apache-2.0", + "dependencies": { + "@aws-cdk/aws-lambda-python-alpha": "2.233.0-alpha.0", + "cdk-nag": "^2.37.55", + "deepmerge": "^4.3.1" + }, + "engines": { + "node": ">= 20.x <= 24.x" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.233.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@cdklabs/generative-ai-cdk-constructs/node_modules/@aws-cdk/aws-lambda-python-alpha": { + "version": "2.233.0-alpha.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.233.0", + "constructs": "^10.0.0" + } + }, + "node_modules/@cdklabs/generative-ai-cdk-constructs/node_modules/deepmerge": { + "version": "4.3.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@emnapi/core": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", + "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ "ppc64" ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", + "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", + "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.2.0", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.2.0", + "jest-config": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-resolve-dependencies": "30.2.0", + "jest-runner": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "jest-watcher": "30.2.0", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0" + }, "engines": { - "node": ">=18" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", - "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", - "cpu": [ - "riscv64" - ], + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", - "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", - "cpu": [ - "s390x" - ], + "node_modules/@jest/environment": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.2.0", + "jest-snapshot": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", + "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", + "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/types": "30.2.0", + "jest-mock": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", + "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", + "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, "engines": { - "node": ">=18" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", - "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", - "cpu": [ - "x64" - ], + "node_modules/@jest/test-result": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", + "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@jest/console": "30.2.0", + "@jest/types": "30.2.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, "engines": { - "node": ">=18" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", - "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", - "cpu": [ - "arm64" - ], + "node_modules/@jest/test-sequencer": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", + "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "dependencies": { + "@jest/test-result": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "slash": "^3.0.0" + }, "engines": { - "node": ">=18" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", - "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", - "cpu": [ - "x64" - ], + "node_modules/@jest/transform": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", + "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, "engines": { - "node": ">=18" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", - "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", - "cpu": [ - "arm64" - ], + "node_modules/@jest/types": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", + "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, "engines": { - "node": ">=18" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", - "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", - "cpu": [ - "x64" - ], + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", - "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", - "cpu": [ - "arm64" - ], + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", - "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", - "cpu": [ - "x64" - ], + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], "engines": { - "node": ">=18" + "node": ">=6.0.0" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", - "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", - "cpu": [ - "arm64" - ], + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", - "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", - "cpu": [ - "ia32" - ], + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", - "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", - "cpu": [ - "x64" - ], + "node_modules/@nhsdigital/eps-cdk-constructs": { + "version": "1.3.0", + "resolved": "https://npm.pkg.github.com/download/@nhsdigital/eps-cdk-constructs/1.3.0/e1e0d166c839f353238504a9c079894df92a6134", + "integrity": "sha512-zKqjd4APkeqDjjn1yoHExpm6oAMGi8OCD7WUu8PHPjJLZN/Bw/hNRRMpbMU5p94NcZP97vnGsfTP6tIsbL+5mw==", + "license": "MIT", + "dependencies": { + "@aws-sdk/client-cloudformation": "^3.978.0", + "@aws-sdk/client-route-53": "^3.975.0", + "@aws-sdk/client-s3": "^3.978.0", + "aws-cdk": "^2.1104.0", + "aws-cdk-lib": "^2.236.0", + "cdk-nag": "^2.37.52", + "constructs": "^10.4.5" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">=18" + "node": ">=14" } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", "dev": true, "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "url": "https://opencollective.com/pkgr" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "node_modules/@sinclair/typebox": { + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } + "license": "MIT" }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, - "license": "Apache-2.0", + "license": "BSD-3-Clause", "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "type-detect": "4.0.8" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@sinonjs/commons": "^3.0.1" } }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", + "node_modules/@smithy/abort-controller": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.8.tgz", + "integrity": "sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "*" + "node": ">=18.0.0" } }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz", + "integrity": "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0" + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18.0.0" } }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz", + "integrity": "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==", "license": "Apache-2.0", "dependencies": { - "@types/json-schema": "^7.0.15" + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/config-resolver": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.6.tgz", + "integrity": "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==", + "license": "Apache-2.0", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/core": { + "version": "3.22.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.22.1.tgz", + "integrity": "sha512-x3ie6Crr58MWrm4viHqqy2Du2rHYZjwu8BekasrQx4ca+Y24dzVAwq3yErdqIbc2G3I0kLQA13PQ+/rde+u65g==", + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@smithy/middleware-serde": "^4.2.9", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-stream": "^4.5.11", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz", + "integrity": "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 4" + "node": ">=18.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.8.tgz", + "integrity": "sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.12.0", + "@smithy/util-hex-encoding": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "*" + "node": ">=18.0.0" } }, - "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.8.tgz", + "integrity": "sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://eslint.org/donate" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.8.tgz", + "integrity": "sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ==", "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18.0.0" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.8.tgz", + "integrity": "sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" + "@smithy/eventstream-serde-universal": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18.0.0" } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.8.tgz", + "integrity": "sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==", "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18.18.0" + "node": ">=18.0.0" } }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz", + "integrity": "sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==", "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" + "@smithy/protocol-http": "^5.3.8", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=18.18.0" + "node": ">=18.0.0" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.9.tgz", + "integrity": "sha512-m80d/iicI7DlBDxyQP6Th7BW/ejDGiF0bgI754+tiwK0lgMkcaIBgvwwVc7OFbY4eUzpGtnig52MhPAEJ7iNYg==", "license": "Apache-2.0", - "engines": { - "node": ">=12.22" + "dependencies": { + "@smithy/chunked-blob-reader": "^5.2.0", + "@smithy/chunked-blob-reader-native": "^4.2.1", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, + "node_modules/@smithy/hash-node": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.8.tgz", + "integrity": "sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==", "license": "Apache-2.0", - "engines": { - "node": ">=18.18" + "dependencies": { + "@smithy/types": "^4.12.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", + "node_modules/@smithy/hash-stream-node": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.8.tgz", + "integrity": "sha512-v0FLTXgHrTeheYZFGhR+ehX5qUm4IQsjAiL9qehad2cyjMWcN2QG6/4mSwbSgEQzI7jwfoXj7z4fxZUx/Mhj2w==", + "license": "Apache-2.0", "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@smithy/types": "^4.12.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz", + "integrity": "sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==", + "license": "Apache-2.0", "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "license": "Apache-2.0", "dependencies": { - "sprintf-js": "~1.0.2" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/md5-js": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.8.tgz", + "integrity": "sha512-oGMaLj4tVZzLi3itBa9TCswgMBr7k9b+qKYowQ6x1rTyTuO1IU2YHdHUa+891OsOH+wCsH7aTPRsTJO3RMQmjQ==", + "license": "Apache-2.0", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@smithy/types": "^4.12.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz", + "integrity": "sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==", + "license": "Apache-2.0", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-endpoint": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.13.tgz", + "integrity": "sha512-x6vn0PjYmGdNuKh/juUJJewZh7MoQ46jYaJ2mvekF4EesMuFfrl4LaW/k97Zjf8PTCPQmPgMvwewg7eNoH9n5w==", + "license": "Apache-2.0", "dependencies": { - "p-locate": "^4.1.0" + "@smithy/core": "^3.22.1", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-middleware": "^4.2.8", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-retry": { + "version": "4.4.30", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.30.tgz", + "integrity": "sha512-CBGyFvN0f8hlnqKH/jckRDz78Snrp345+PVk8Ux7pnkUCW97Iinse59lY78hBt04h1GZ6hjBN94BRwZy1xC8Bg==", + "license": "Apache-2.0", "dependencies": { - "p-try": "^2.0.0" + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/service-error-classification": "^4.2.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-serde": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz", + "integrity": "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==", + "license": "Apache-2.0", "dependencies": { - "p-limit": "^2.2.0" + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-stack": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz", + "integrity": "sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/node-config-provider": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz", + "integrity": "sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/@jest/console": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", - "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/node-http-handler": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.9.tgz", + "integrity": "sha512-KX5Wml5mF+luxm1szW4QDz32e3NObgJ4Fyw+irhph4I/2geXwUy4jkIMUs5ZPGflRBeR6BUkC2wqIab4Llgm3w==", + "license": "Apache-2.0", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" + "@smithy/abort-controller": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/core": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", - "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/property-provider": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.8.tgz", + "integrity": "sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==", + "license": "Apache-2.0", "dependencies": { - "@jest/console": "30.2.0", - "@jest/pattern": "30.0.1", - "@jest/reporters": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-changed-files": "30.2.0", - "jest-config": "30.2.0", - "jest-haste-map": "30.2.0", - "jest-message-util": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.2.0", - "jest-resolve-dependencies": "30.2.0", - "jest-runner": "30.2.0", - "jest-runtime": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "jest-watcher": "30.2.0", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=18.0.0" } }, - "node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/protocol-http": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.8.tgz", + "integrity": "sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/environment": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/querystring-builder": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz", + "integrity": "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==", + "license": "Apache-2.0", "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0" + "@smithy/types": "^4.12.0", + "@smithy/util-uri-escape": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/querystring-parser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz", + "integrity": "sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==", + "license": "Apache-2.0", "dependencies": { - "expect": "30.2.0", - "jest-snapshot": "30.2.0" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/expect-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", - "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/service-error-classification": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz", + "integrity": "sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==", + "license": "Apache-2.0", "dependencies": { - "@jest/get-type": "30.1.0" + "@smithy/types": "^4.12.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/fake-timers": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", - "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz", + "integrity": "sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==", + "license": "Apache-2.0", "dependencies": { - "@jest/types": "30.2.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/get-type": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/signature-v4": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.8.tgz", + "integrity": "sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/globals": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", - "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/smithy-client": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.11.2.tgz", + "integrity": "sha512-SCkGmFak/xC1n7hKRsUr6wOnBTJ3L22Qd4e8H1fQIuKTAjntwgU8lrdMe7uHdiT2mJAOWA/60qaW9tiMu69n1A==", + "license": "Apache-2.0", "dependencies": { - "@jest/environment": "30.2.0", - "@jest/expect": "30.2.0", - "@jest/types": "30.2.0", - "jest-mock": "30.2.0" + "@smithy/core": "^3.22.1", + "@smithy/middleware-endpoint": "^4.4.13", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.11", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/types": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.12.0.tgz", + "integrity": "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/reporters": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", - "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/url-parser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.8.tgz", + "integrity": "sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==", + "license": "Apache-2.0", "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "@types/node": "*", - "chalk": "^4.1.2", - "collect-v8-coverage": "^1.0.2", - "exit-x": "^0.2.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^5.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "jest-worker": "30.2.0", - "slash": "^3.0.0", - "string-length": "^4.0.2", - "v8-to-istanbul": "^9.0.1" + "@smithy/querystring-parser": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", + "license": "Apache-2.0", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/snapshot-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", - "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "license": "Apache-2.0", "dependencies": { - "@jest/types": "30.2.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" + "@smithy/is-array-buffer": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/source-map": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", - "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-config-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "callsites": "^3.1.0", - "graceful-fs": "^4.2.11" + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/test-result": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", - "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.29", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.29.tgz", + "integrity": "sha512-nIGy3DNRmOjaYaaKcQDzmWsro9uxlaqUOhZDHQed9MW/GmkBZPtnU70Pu1+GT9IBmUXwRdDuiyaeiy9Xtpn3+Q==", + "license": "Apache-2.0", "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/test-sequencer": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", - "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.32", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.32.tgz", + "integrity": "sha512-7dtFff6pu5fsjqrVve0YMhrnzJtccCWDacNKOkiZjJ++fmjGExmmSu341x+WU6Oc1IccL7lDuaUj7SfrHpWc5Q==", + "license": "Apache-2.0", "dependencies": { - "@jest/test-result": "30.2.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "slash": "^3.0.0" + "@smithy/config-resolver": "^4.4.6", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/transform": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", - "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-endpoints": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz", + "integrity": "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==", + "license": "Apache-2.0", "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.1", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "license": "Apache-2.0", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-middleware": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.8.tgz", + "integrity": "sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-retry": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.8.tgz", + "integrity": "sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", + "@smithy/service-error-classification": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-stream": { + "version": "4.5.11", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.11.tgz", + "integrity": "sha512-lKmZ0S/3Qj2OF5H1+VzvDLb6kRxGzZHq6f3rAsoSu5cTLGsn3v3VQBA8czkNNXlLjoFEtVu3OQT2jEeOtOE2CA==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.9", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "license": "MIT", - "optional": true, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "license": "Apache-2.0", "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, + "tslib": "^2.6.2" + }, "engines": { - "node": ">=14" + "node": ">=18.0.0" } }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node_modules/@smithy/util-utf8": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://opencollective.com/pkgr" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@smithy/util-waiter": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.8.tgz", + "integrity": "sha512-n+lahlMWk+aejGuax7DPWtqav8HYnWxQwR+LCG2BgCUmaGcTe9qZCFsmw8TMg9iG75HOwhrJCX9TCJRLH+Yzqg==", + "license": "Apache-2.0", "dependencies": { - "type-detect": "4.0.8" + "@smithy/abort-controller": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@smithy/uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^3.0.1" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@tsconfig/node10": { @@ -2099,11 +3862,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.1.0.tgz", - "integrity": "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==", + "version": "25.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz", + "integrity": "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2167,6 +3931,7 @@ "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.54.0", "@typescript-eslint/types": "8.54.0", @@ -2654,6 +4419,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2795,7 +4561,6 @@ "version": "2.1104.0", "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1104.0.tgz", "integrity": "sha512-TGIK2Mpfqi0BA6Np9aJz0d5HAvTxWd17FrwtXlJuwqdQbR9R/IRqsabF6xRAuhFTz7/YrrHHU9H4VK/Xfnh7Vg==", - "dev": true, "license": "Apache-2.0", "bin": { "cdk": "bin/cdk" @@ -2805,9 +4570,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.236.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.236.0.tgz", - "integrity": "sha512-LauY4BX8vdYL9DvVKCgtJ2gZBwLEgfszTlFe6R2p2NUfEJ+PPpeRGxUbTaOdwLqJGN6mDqmzdoF4or8l2v69PA==", + "version": "2.237.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.237.0.tgz", + "integrity": "sha512-uFQ9OKL1jDYguIA9ZniOsvnUt+CZSSXqVec9XP7SjDR5Mys19VsSiMTChBrTywoWDxdjdiVfYE0bmaH2nzV7Cg==", "bundleDependencies": [ "@balena/dockerignore", "case", @@ -2822,6 +4587,7 @@ "mime-types" ], "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-cdk/asset-awscli-v1": "2.2.263", "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", @@ -3261,15 +5027,21 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.0.tgz", - "integrity": "sha512-Mh++g+2LPfzZToywfE1BUzvZbfOY52Nil0rn9H1CPC5DJ7fX+Vir7nToBeoiSbB1zTNeGYbELEvJESujgGrzXw==", + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/bowser": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", + "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -3313,6 +5085,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3377,9 +5150,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001759", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz", - "integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==", + "version": "1.0.30001767", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001767.tgz", + "integrity": "sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==", "dev": true, "funding": [ { @@ -3439,9 +5212,9 @@ } }, "node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -3455,9 +5228,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz", - "integrity": "sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", "dev": true, "license": "MIT" }, @@ -3588,7 +5361,8 @@ "version": "10.4.5", "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.4.5.tgz", "integrity": "sha512-fOoP70YLevMZr5avJHx2DU3LNYmC6wM8OwdrNewMZou1kZnPGOeVzBrRjZNgFDHUlulYUjkpFRSpTE3D+n+ZSg==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/convert-source-map": { "version": "2.0.0", @@ -3649,9 +5423,9 @@ } }, "node_modules/dedent": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", - "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3702,9 +5476,9 @@ } }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -3719,9 +5493,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.264", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.264.tgz", - "integrity": "sha512-1tEf0nLgltC3iy9wtlYDlQDc5Rg9lEKVjEmIHJ21rI9OcqkvD45K1oyNIRA4rR1z3LgJ7KeGzEBojVcV6m4qjA==", + "version": "1.5.283", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.283.tgz", + "integrity": "sha512-3vifjt1HgrGW/h76UEeny+adYApveS9dH2h3p57JYzBSXJIKUJAvtmIytDKjcSCt9xHfrNCFJ7gts6vkhuq++w==", "dev": true, "license": "ISC" }, @@ -3825,6 +5599,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4018,9 +5793,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -4143,6 +5918,24 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz", + "integrity": "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -4245,7 +6038,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4309,6 +6101,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-tsconfig": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.1.tgz", + "integrity": "sha512-EoY1N2xCn44xU6750Sx7OjOIT59FkmstNc3X6y5xpz7D5cBtZRe/3pSlTkDJgqsOk3WwZPkWfonhhUJfttQo3w==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", @@ -4697,6 +6501,7 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -6398,6 +8203,15 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/semver": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", @@ -6758,6 +8572,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -6785,9 +8611,9 @@ } }, "node_modules/synckit": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", - "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6902,6 +8728,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -7024,6 +8851,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -7066,9 +8894,26 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } }, "node_modules/type-check": { "version": "0.4.0", @@ -7112,6 +8957,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7177,9 +9023,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.1.tgz", - "integrity": "sha512-R9NcHbbZ45RoWfTdhn1J9SS7zxNvlddv4YRrHTUaFdtjbmfncfedB45EC9IaqJQ97iAR1GZgOfyRQO+ExIF6EQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -7552,10 +9398,12 @@ "dependencies": { "@aws-cdk/aws-lambda-python-alpha": "^2.205.0-alpha.0", "@cdklabs/generative-ai-cdk-constructs": "^0.1.314", + "@nhsdigital/eps-cdk-constructs": "^1.3.0", "aws-cdk-lib": "^2.236.0", "cdk-nag": "^2.37.55", "constructs": "^10.4.5", - "source-map-support": "^0.5.21" + "source-map-support": "^0.5.21", + "tsx": "^4.21.0" }, "devDependencies": { "@types/node": "^25.1.0", diff --git a/packages/cdk/bin/EpsAssistMeApp.ts b/packages/cdk/bin/EpsAssistMeApp.ts index c6493ea2..b5e7504e 100644 --- a/packages/cdk/bin/EpsAssistMeApp.ts +++ b/packages/cdk/bin/EpsAssistMeApp.ts @@ -1,51 +1,62 @@ -#!/usr/bin/env node -import {App, Aspects, Tags} from "aws-cdk-lib" -import {AwsSolutionsChecks} from "cdk-nag" -import {EpsAssistMeStack} from "../stacks/EpsAssistMeStack" +import { + calculateVersionedStackName, + createApp, + getBooleanConfigFromEnvVar, + getConfigFromEnvVar, + getNumberConfigFromEnvVar +} from "@nhsdigital/eps-cdk-constructs" +import {EpsAssistMe_Stateful} from "../stacks/EpsAssistMe_Stafeful" +import {EpsAssistMe_BasepathMapping} from "../stacks/EpsAssistMe_BasepathMapping" +import {EpsAssistMe_Stateless} from "../stacks/EpsAssistMe_Stateless" import {applyCfnGuardSuppressions} from "./utils/appUtils" -import fs from "fs" -// read the config in -const configFileName = process.env["CONFIG_FILE_NAME"] -if (configFileName === undefined) { - throw new Error("Can not read config file") +async function main() { + const {app, props} = createApp({ + productName: "EpsAssistMe", + appName: "EpsAssistMe", + repoName: "eps-assist-me", + driftDetectionGroup: "epsam" + }) + + const statefulStack = new EpsAssistMe_Stateful(app, "EpsAssistMeStateful", { + ...props, + stackName: getConfigFromEnvVar("statefulStackName"), + region: props.env?.region || "eu-west-2", + logRetentionInDays: getNumberConfigFromEnvVar("logRetentionInDays"), + logLevel: getConfigFromEnvVar("logLevel"), + enableBedrockLogging: getBooleanConfigFromEnvVar("enableBedrockLogging"), + apiGatewayDomainName: getConfigFromEnvVar("domainName") + }) + + const statelessStackName = calculateVersionedStackName(getConfigFromEnvVar("statelessStackName"), props) + const statelessStack = new EpsAssistMe_Stateless(app, "EpsAssistMeStateless", { + ...props, + stackName: statelessStackName, + region: props.env?.region || "eu-west-2", + logRetentionInDays: getNumberConfigFromEnvVar("logRetentionInDays"), + logLevel: getConfigFromEnvVar("logLevel"), + runRegressionTests: getBooleanConfigFromEnvVar("runRegressionTests"), + forwardCsocLogs: getBooleanConfigFromEnvVar("forwardCsocLogs"), + // eslint-disable-next-line max-len + csocApiGatewayDestination: "arn:aws:logs:eu-west-2:693466633220:destination:api_gateway_log_destination", // CSOC API GW log destination - do not change + slackBotToken: getConfigFromEnvVar("slackBotToken"), + slackSigningSecret: getConfigFromEnvVar("slackSigningSecret"), + statefulStackName: getConfigFromEnvVar("statefulStackName") + }) + + const basePathMapping = new EpsAssistMe_BasepathMapping(app, "EpsAssistMeBasepathMapping", { + ...props, + stackName: getConfigFromEnvVar("basePathMappingStackName"), + statefulStackName: getConfigFromEnvVar("statefulStackName"), + statelessStackName: statelessStackName + }) + + applyCfnGuardSuppressions(statefulStack) + applyCfnGuardSuppressions(statelessStack) + applyCfnGuardSuppressions(basePathMapping) } -const configDetails = JSON.parse(fs.readFileSync(configFileName, "utf-8")) - -// create the app using the config details -const app = new App({context: configDetails}) - -/* Required Context: -- accountId -- stackName -- version -- commit -*/ - -const stackName = app.node.tryGetContext("stackName") -const version = app.node.tryGetContext("versionNumber") -const commit = app.node.tryGetContext("commitId") -const cfnDriftDetectionGroup = app.node.tryGetContext("cfnDriftDetectionGroup") - -Aspects.of(app).add(new AwsSolutionsChecks({verbose: true})) - -Tags.of(app).add("cdkApp", "EpsAssistMe") -Tags.of(app).add("stackName", stackName) -Tags.of(app).add("version", version) -Tags.of(app).add("commit", commit) -Tags.of(app).add("cfnDriftDetectionGroup", cfnDriftDetectionGroup) - -const EpsAssistMe = new EpsAssistMeStack(app, "EpsAssistMeStack", { - env: { - region: "eu-west-2", - account: process.env.CDK_DEFAULT_ACCOUNT || undefined - }, - stackName: stackName, - version: version, - commitId: commit +main().catch((error) => { + console.error(error) + process.exit(1) }) - -applyCfnGuardSuppressions(EpsAssistMe) - -app.synth() diff --git a/packages/cdk/bin/utils/appUtils.ts b/packages/cdk/bin/utils/appUtils.ts index a96c1961..65b9aa27 100644 --- a/packages/cdk/bin/utils/appUtils.ts +++ b/packages/cdk/bin/utils/appUtils.ts @@ -1,5 +1,10 @@ import {Stack, CfnResource} from "aws-cdk-lib" import {IConstruct} from "constructs" +import { + addLambdaCfnGuardSuppressions, + findCloudFormationResourcesByPath, + addSuppressions +} from "@nhsdigital/eps-cdk-constructs" /** * Find all CfnResources whose logicalId matches any provided pattern. @@ -25,48 +30,26 @@ const findResourcesByPattern = (construct: IConstruct, patterns: Array): return matches } -/** - * Find all CfnResources of a specific CloudFormation type. - */ -const findResourcesByType = (construct: IConstruct, type: string): Array => { - const matches: Array = [] - const search = (node: IConstruct): void => { - if (node instanceof CfnResource && node.cfnResourceType === type) { - matches.push(node) - } - for (const child of node.node.children) { - search(child) - } - } - search(construct) - return matches -} - -/** - * Add/merge cfn-guard suppressions to resources for the given rules. - */ -const addSuppressions = (resources: Array, rules: Array): void => { - resources.forEach(resource => { - if (!resource.cfnOptions.metadata) { - resource.cfnOptions.metadata = {} - } - const existing = resource.cfnOptions.metadata.guard?.SuppressedRules || [] - const combined = Array.from(new Set([...existing, ...rules])) - resource.cfnOptions.metadata.guard = {SuppressedRules: combined} - }) -} - /** * Apply cfn-guard suppressions for Lambda, S3, and API Gateway resources. */ export const applyCfnGuardSuppressions = (stack: Stack): void => { // Suppress all cfn-guard checks for all Lambda functions (including implicit CDK-generated ones) - const allLambdas = findResourcesByType(stack, "AWS::Lambda::Function") - addSuppressions(allLambdas, ["LAMBDA_DLQ_CHECK", "LAMBDA_INSIDE_VPC", "LAMBDA_CONCURRENCY_CHECK"]) - + addLambdaCfnGuardSuppressions(stack) const apiGatewayPermissions = findResourcesByPattern(stack, ["ApiPermission"]) addSuppressions(apiGatewayPermissions, ["LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED"]) const s3NotificationPermissions = findResourcesByPattern(stack, ["AllowBucketNotifications"]) addSuppressions(s3NotificationPermissions, ["LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED"]) + + const aws_created_resources = findCloudFormationResourcesByPath(stack, [ + "EpsAssistMeStateful/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/LogGroup/Resource", + "EpsAssistMeStateful/BedrockLogging/LoggingConfigProvider/framework-onEvent/LogGroup/Resource", + "EpsAssistMeStateful/VectorIndex/IndexReadyWait/DelayProvider/framework-onEvent/LogGroup/Resource", + "EpsAssistMeStateful/VectorIndex/IndexReadyWait/DelayFunction/LogGroup/Resource", + "EpsAssistMeStateful/VectorIndex/PolicySyncWait/DelayFunction/LogGroup/Resource", + "EpsAssistMeStateful/VectorIndex/PolicySyncWait/DelayProvider/framework-onEvent/LogGroup/Resource" + ]) + addSuppressions(aws_created_resources, ["CLOUDWATCH_LOG_GROUP_ENCRYPTED"]) + } diff --git a/cdk.context.json b/packages/cdk/cdk.context.json similarity index 100% rename from cdk.context.json rename to packages/cdk/cdk.context.json diff --git a/cdk.json b/packages/cdk/cdk.json similarity index 64% rename from cdk.json rename to packages/cdk/cdk.json index 8fd3c35c..11a3fdbe 100644 --- a/cdk.json +++ b/packages/cdk/cdk.json @@ -71,6 +71,34 @@ "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false, - "cdk-migrate": true + "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true, + "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true, + "@aws-cdk/aws-dynamodb:retainTableReplica": true, + "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true, + "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true, + "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true, + "@aws-cdk/aws-ecs-patterns:secGroupsDisablesImplicitOpenListener": true, + "@aws-cdk/aws-ecs-patterns:uniqueTargetGroupId": true, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true, + "@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true, + "@aws-cdk/aws-elasticloadbalancingv2:networkLoadBalancerWithSecurityGroupByDefault": true, + "@aws-cdk/aws-events:requireEventBusPolicySid": true, + "@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": true, + "@aws-cdk/aws-kms:applyImportedAliasPermissionsToPrincipal": true, + "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true, + "@aws-cdk/aws-lambda:useCdkManagedLogGroup": true, + "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true, + "@aws-cdk/aws-route53-patterns:useDistribution": true, + "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true, + "@aws-cdk/aws-s3:publicAccessBlockedByDefault": true, + "@aws-cdk/aws-s3:setUniqueReplicationRoleName": true, + "@aws-cdk/aws-signer:signingProfileNamePassedToCfn": true, + "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true, + "@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": true, + "@aws-cdk/core:aspectPrioritiesMutating": true, + "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true, + "@aws-cdk/core:enableAdditionalMetadataCollection": true, + "@aws-cdk/core:explicitStackTags": true, + "@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": true } } diff --git a/packages/cdk/constructs/DelayResource.ts b/packages/cdk/constructs/DelayResource.ts index 2975b60c..05c4c155 100644 --- a/packages/cdk/constructs/DelayResource.ts +++ b/packages/cdk/constructs/DelayResource.ts @@ -1,8 +1,22 @@ import {Construct} from "constructs" -import {Duration, CustomResource} from "aws-cdk-lib" +import { + Duration, + CustomResource, + Fn, + RemovalPolicy +} from "aws-cdk-lib" import {Function, Runtime, Code} from "aws-cdk-lib/aws-lambda" -import {Role, ServicePrincipal, ManagedPolicy} from "aws-cdk-lib/aws-iam" +import { + Role, + ServicePrincipal, + ManagedPolicy, + PolicyStatement +} from "aws-cdk-lib/aws-iam" import {Provider} from "aws-cdk-lib/custom-resources" +import {Key} from "aws-cdk-lib/aws-kms" +import {CfnLogGroup, LogGroup} from "aws-cdk-lib/aws-logs" +import {addSuppressions} from "@nhsdigital/eps-cdk-constructs" +import {NagSuppressions} from "cdk-nag" export interface DelayResourceProps { /** @@ -14,6 +28,11 @@ export interface DelayResourceProps { * Optional description for the delay resource */ readonly description?: string + + /** + * Name for the delay resource + */ + readonly name: string } /** @@ -22,29 +41,72 @@ export interface DelayResourceProps { */ export class DelayResource extends Construct { public readonly customResource: CustomResource - public readonly delaySeconds: number - constructor(scope: Construct, id: string, props: DelayResourceProps = {}) { + constructor(scope: Construct, id: string, props: DelayResourceProps) { super(scope, id) + const { + delaySeconds = 30, + description = `Delay resource for ${delaySeconds} seconds`, + name + } = props + const cloudWatchLogsKmsKey = Key.fromKeyArn( + scope, `${name}delayResourceCloudWatchLogsKmsKey`, Fn.importValue("account-resources:CloudwatchLogsKmsKeyArn")) + + const cloudwatchEncryptionKMSPolicy = ManagedPolicy.fromManagedPolicyArn( + scope, `${name}delayResourceCloudwatchEncryptionKMSPolicyArn`, + Fn.importValue("account-resources:CloudwatchEncryptionKMSPolicyArn")) + + const logGroup = new LogGroup(scope, `${name}LambdaLogGroup`, { + encryptionKey: cloudWatchLogsKmsKey, + logGroupName: `/aws/lambda/${name}`, + retention: 30, + removalPolicy: RemovalPolicy.DESTROY + }) + + const cfnlogGroup = logGroup.node.defaultChild as CfnLogGroup + addSuppressions([cfnlogGroup], ["CW_LOGGROUP_RETENTION_PERIOD_CHECK"]) + const putLogsManagedPolicy = new ManagedPolicy(scope, `${name}LambdaPutLogsManagedPolicy`, { + description: `write to ${name} logs`, + statements: [ + new PolicyStatement({ + actions: [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + resources: [ + logGroup.logGroupArn, + `${logGroup.logGroupArn}:log-stream:*` + ] + }) + ] + }) - this.delaySeconds = props.delaySeconds || 30 + NagSuppressions.addResourceSuppressions(putLogsManagedPolicy, [ + { + id: "AwsSolutions-IAM5", + // eslint-disable-next-line max-len + reason: "Suppress error for not having wildcards in permissions. This is a fine as we need to have permissions on all log streams under path" + } + ]) // create IAM role for the delay Lambda function - const lambdaExecutionRole = new Role(this, "LambdaExecutionRole", { + const lambdaExecutionRole = new Role(this, `${name}LambdaExecutionRole`, { assumedBy: new ServicePrincipal("lambda.amazonaws.com"), description: "Execution role for delay custom resource Lambda function", managedPolicies: [ - ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaBasicExecutionRole") + putLogsManagedPolicy, + cloudwatchEncryptionKMSPolicy ] }) // create the delay Lambda function with inline Python code - const delayFunction = new Function(this, "DelayFunction", { + const delayFunction = new Function(this, `${name}DelayFunction`, { runtime: Runtime.PYTHON_3_12, + logGroup: logGroup, handler: "index.handler", role: lambdaExecutionRole, timeout: Duration.minutes(15), // max Lambda timeout to handle long delays - description: props.description || `Delay resource for ${this.delaySeconds} seconds`, + description: description, code: Code.fromInline(` from time import sleep import json @@ -78,17 +140,52 @@ def handler(event, context): `) }) + const frameworkInvokeDelayFunctionPolicy = new ManagedPolicy(this, + `${name}DelayProviderInvokeDelayFunctionPolicy`, { + description: `Allow provider framework to invoke ${name} delay function`, + statements: [ + new PolicyStatement({ + actions: [ + "lambda:InvokeFunction", + "lambda:GetFunction" + ], + resources: [ + delayFunction.functionArn + ] + }) + ] + }) + + const frameworkOnEventRole = new Role(this, `${name}DelayProviderFrameworkOnEventRole`, { + assumedBy: new ServicePrincipal("lambda.amazonaws.com"), + description: "Execution role for delay provider framework onEvent Lambda function", + managedPolicies: [ + putLogsManagedPolicy, + cloudwatchEncryptionKMSPolicy, + frameworkInvokeDelayFunctionPolicy + ] + }) + + const frameworkOnEventRoleRef = Role.fromRoleArn( + this, + `${name}DelayProviderFrameworkOnEventRoleRef`, + frameworkOnEventRole.roleArn, + {mutable: false} + ) + // create the custom resource provider - const provider = new Provider(this, "DelayProvider", { - onEventHandler: delayFunction + const provider = new Provider(this, `${name}DelayProvider`, { + onEventHandler: delayFunction, + logGroup: logGroup, + frameworkOnEventRole: frameworkOnEventRoleRef }) // create the custom resource that triggers the delay - this.customResource = new CustomResource(this, "DelayCustomResource", { + this.customResource = new CustomResource(this, `${name}DelayCustomResource`, { serviceToken: provider.serviceToken, properties: { - WaitSeconds: this.delaySeconds, - Description: props.description || `Delay for ${this.delaySeconds} seconds`, + WaitSeconds: delaySeconds, + Description: description, // timestamp to ensure updates trigger when properties change Timestamp: Date.now() } diff --git a/packages/cdk/constructs/LambdaFunction.ts b/packages/cdk/constructs/LambdaFunction.ts deleted file mode 100644 index 2d988915..00000000 --- a/packages/cdk/constructs/LambdaFunction.ts +++ /dev/null @@ -1,175 +0,0 @@ -import {Construct} from "constructs" -import {Duration, Fn, RemovalPolicy} from "aws-cdk-lib" -import { - ManagedPolicy, - PolicyStatement, - Role, - ServicePrincipal, - IManagedPolicy -} from "aws-cdk-lib/aws-iam" -import {Key} from "aws-cdk-lib/aws-kms" -import {Stream} from "aws-cdk-lib/aws-kinesis" -import { - Architecture, - CfnFunction, - LayerVersion, - Runtime, - Function as LambdaFunctionResource, - Code -} from "aws-cdk-lib/aws-lambda" -import {CfnLogGroup, CfnSubscriptionFilter, LogGroup} from "aws-cdk-lib/aws-logs" - -export interface LambdaFunctionProps { - readonly stackName: string - readonly functionName: string - readonly packageBasePath: string - readonly handler: string - readonly environmentVariables?: {[key: string]: string} - readonly additionalPolicies?: Array - readonly logRetentionInDays: number - readonly logLevel: string - readonly dependencyLocation?: string -} - -// Lambda Insights layer for enhanced monitoring -const insightsLayerArn = "arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension:55" - -export class LambdaFunction extends Construct { - public readonly executionPolicy: ManagedPolicy - public readonly function: LambdaFunctionResource - public readonly executionRole: Role - - public constructor(scope: Construct, id: string, props: LambdaFunctionProps) { - super(scope, id) - - // Import shared cloud resources from cross-stack references - const cloudWatchLogsKmsKey = Key.fromKeyArn( - this, "cloudWatchLogsKmsKey", Fn.importValue("account-resources:CloudwatchLogsKmsKeyArn")) - - const cloudwatchEncryptionKMSPolicy = ManagedPolicy.fromManagedPolicyArn( - this, "cloudwatchEncryptionKMSPolicyArn", Fn.importValue("account-resources:CloudwatchEncryptionKMSPolicyArn")) - - const splunkDeliveryStream = Stream.fromStreamArn( - this, "SplunkDeliveryStream", Fn.importValue("lambda-resources:SplunkDeliveryStream")) - - const splunkSubscriptionFilterRole = Role.fromRoleArn( - this, "splunkSubscriptionFilterRole", Fn.importValue("lambda-resources:SplunkSubscriptionFilterRole")) - - const lambdaInsightsLogGroupPolicy = ManagedPolicy.fromManagedPolicyArn( - this, "lambdaInsightsLogGroupPolicy", Fn.importValue("lambda-resources:LambdaInsightsLogGroupPolicy")) - - const insightsLambdaLayer = LayerVersion.fromLayerVersionArn( - this, "LayerFromArn", insightsLayerArn) - - // Log group with encryption and retention - const logGroup = new LogGroup(this, "LambdaLogGroup", { - encryptionKey: cloudWatchLogsKmsKey, - logGroupName: `/aws/lambda/${props.functionName}`, - retention: props.logRetentionInDays, - removalPolicy: RemovalPolicy.DESTROY - }) - - // Suppress CFN guard rules for log group - const cfnlogGroup = logGroup.node.defaultChild as CfnLogGroup - cfnlogGroup.cfnOptions.metadata = { - guard: { - SuppressedRules: [ - "CW_LOGGROUP_RETENTION_PERIOD_CHECK" - ] - } - } - - // Send logs to Splunk - new CfnSubscriptionFilter(this, "LambdaLogsSplunkSubscriptionFilter", { - destinationArn: splunkDeliveryStream.streamArn, - filterPattern: "", - logGroupName: logGroup.logGroupName, - roleArn: splunkSubscriptionFilterRole.roleArn - }) - - // Create managed policy for Lambda CloudWatch logs access - const putLogsManagedPolicy = new ManagedPolicy(this, "LambdaPutLogsManagedPolicy", { - description: `write to ${props.functionName} logs`, - statements: [ - new PolicyStatement({ - actions: [ - "logs:CreateLogStream", - "logs:PutLogEvents" - ], - resources: [ - logGroup.logGroupArn, - `${logGroup.logGroupArn}:log-stream:*` - ] - }) - ] - }) - - // Aggregate all required policies for Lambda execution - const requiredPolicies: Array = [ - putLogsManagedPolicy, - lambdaInsightsLogGroupPolicy, - cloudwatchEncryptionKMSPolicy, - ...(props.additionalPolicies ?? []) - ] - - const role = new Role(this, "LambdaRole", { - assumedBy: new ServicePrincipal("lambda.amazonaws.com"), - managedPolicies: requiredPolicies - }) - - const layers = [insightsLambdaLayer] - if (props.dependencyLocation) { - const dependencyLayer = new LayerVersion(this, "DependencyLayer", { - removalPolicy: RemovalPolicy.DESTROY, - code: Code.fromAsset(props.dependencyLocation), - compatibleArchitectures: [Architecture.X86_64] - }) - layers.push(dependencyLayer) - } - - // Create Lambda function with Python runtime and monitoring - const lambdaFunction = new LambdaFunctionResource(this, props.functionName, { - runtime: Runtime.PYTHON_3_14, - memorySize: 256, - timeout: Duration.seconds(50), - architecture: Architecture.X86_64, - handler: props.handler, - code: Code.fromAsset(props.packageBasePath), - role, - environment: { - ...props.environmentVariables, - POWERTOOLS_LOG_LEVEL: props.logLevel - }, - logGroup, - layers: layers - }) - - // Suppress CFN guard rules for Lambda function - const cfnLambda = lambdaFunction.node.defaultChild as CfnFunction - cfnLambda.cfnOptions.metadata = { - guard: { - SuppressedRules: [ - "LAMBDA_DLQ_CHECK", - "LAMBDA_INSIDE_VPC", - "LAMBDA_CONCURRENCY_CHECK" - ] - } - } - - // Create policy for external services to invoke this Lambda - const executionManagedPolicy = new ManagedPolicy(this, "ExecuteLambdaManagedPolicy", { - description: `execute lambda ${props.functionName}`, - statements: [ - new PolicyStatement({ - actions: ["lambda:InvokeFunction"], - resources: [lambdaFunction.functionArn] - }) - ] - }) - - // Export Lambda function and execution policy for use by other constructs - this.function = lambdaFunction - this.executionPolicy = executionManagedPolicy - this.executionRole = role - } -} diff --git a/packages/cdk/constructs/RestApiGateway.ts b/packages/cdk/constructs/RestApiGateway.ts index dddd777f..0332dcbb 100644 --- a/packages/cdk/constructs/RestApiGateway.ts +++ b/packages/cdk/constructs/RestApiGateway.ts @@ -4,8 +4,7 @@ import { EndpointType, LogGroupLogDestination, MethodLoggingLevel, - RestApi, - SecurityPolicy + RestApi } from "aws-cdk-lib/aws-apigateway" import {IRole, Role, ServicePrincipal} from "aws-cdk-lib/aws-iam" import {Stream} from "aws-cdk-lib/aws-kinesis" @@ -13,14 +12,7 @@ import {Key} from "aws-cdk-lib/aws-kms" import {CfnSubscriptionFilter, LogGroup} from "aws-cdk-lib/aws-logs" import {Construct} from "constructs" import {accessLogFormat} from "./RestApiGateway/accessLogFormat" -import {Certificate, CertificateValidation} from "aws-cdk-lib/aws-certificatemanager" -import { - ARecord, - AaaaRecord, - HostedZone, - RecordTarget -} from "aws-cdk-lib/aws-route53" -import {ApiGateway as ApiGatewayTarget} from "aws-cdk-lib/aws-route53-targets" +import {addSuppressions} from "@nhsdigital/eps-cdk-constructs" export interface RestApiGatewayProps { readonly stackName: string @@ -48,13 +40,6 @@ export class RestApiGateway extends Construct { const splunkSubscriptionFilterRole = Role.fromRoleArn( this, "splunkSubscriptionFilterRole", Fn.importValue("lambda-resources:SplunkSubscriptionFilterRole")) - const epsDomainName: string = Fn.importValue("eps-route53-resources:EPS-domain") - const hostedZone = HostedZone.fromHostedZoneAttributes(this, "HostedZone", { - hostedZoneId: Fn.importValue("eps-route53-resources:EPS-ZoneID"), - zoneName: epsDomainName - }) - const serviceDomainName = `${props.stackName}.${epsDomainName}` - // Resources const logGroup = new LogGroup(this, "ApiGatewayAccessLogGroup", { encryptionKey: cloudWatchLogsKmsKey, @@ -80,19 +65,8 @@ export class RestApiGateway extends Construct { }) } - const certificate = new Certificate(this, "Certificate", { - domainName: serviceDomainName, - validation: CertificateValidation.fromDns(hostedZone) - }) - const apiGateway = new RestApi(this, "ApiGateway", { restApiName: `${props.stackName}-apigw`, - domainName: { - domainName: serviceDomainName, - certificate: certificate, - securityPolicy: SecurityPolicy.TLS_1_2, - endpointType: EndpointType.REGIONAL - }, endpointConfiguration: { types: [EndpointType.REGIONAL] }, @@ -110,26 +84,10 @@ export class RestApiGateway extends Construct { managedPolicies: [] }) - new ARecord(this, "ARecord", { - recordName: props.stackName, - target: RecordTarget.fromAlias(new ApiGatewayTarget(apiGateway)), - zone: hostedZone - }) - - new AaaaRecord(this, "AAAARecord", { - recordName: props.stackName, - target: RecordTarget.fromAlias(new ApiGatewayTarget(apiGateway)), - zone: hostedZone - }) - const cfnStage = apiGateway.deploymentStage.node.defaultChild as CfnStage - cfnStage.cfnOptions.metadata = { - guard: { - SuppressedRules: [ - "API_GW_CACHE_ENABLED_AND_ENCRYPTED" - ] - } - } + addSuppressions([cfnStage], [ + "API_GW_CACHE_ENABLED_AND_ENCRYPTED" + ]) // Outputs this.api = apiGateway diff --git a/packages/cdk/constructs/RestApiGateway/LambdaEndpoint.ts b/packages/cdk/constructs/RestApiGateway/LambdaEndpoint.ts index 22951ca1..fa9791bf 100644 --- a/packages/cdk/constructs/RestApiGateway/LambdaEndpoint.ts +++ b/packages/cdk/constructs/RestApiGateway/LambdaEndpoint.ts @@ -2,14 +2,14 @@ import {Construct} from "constructs" import {IResource, LambdaIntegration} from "aws-cdk-lib/aws-apigateway" import {HttpMethod} from "aws-cdk-lib/aws-lambda" import {IRole} from "aws-cdk-lib/aws-iam" -import {LambdaFunction} from "../LambdaFunction" +import {PythonLambdaFunction} from "@nhsdigital/eps-cdk-constructs" export interface LambdaEndpointProps { readonly parentResource: IResource readonly resourceName: string readonly method: HttpMethod readonly restApiGatewayRole: IRole - readonly lambdaFunction: LambdaFunction + readonly lambdaFunction: PythonLambdaFunction } export class LambdaEndpoint extends Construct { diff --git a/packages/cdk/constructs/SecretWithParameter.ts b/packages/cdk/constructs/SecretWithParameter.ts index 23c14171..8d3570ce 100644 --- a/packages/cdk/constructs/SecretWithParameter.ts +++ b/packages/cdk/constructs/SecretWithParameter.ts @@ -2,12 +2,14 @@ import {Construct} from "constructs" import {SecretValue} from "aws-cdk-lib" import {StringParameter, ParameterTier} from "aws-cdk-lib/aws-ssm" import {Secret} from "aws-cdk-lib/aws-secretsmanager" +import {IKey} from "aws-cdk-lib/aws-kms" export interface SecretWithParameterProps { readonly secretName: string readonly parameterName: string readonly description: string readonly secretValue: string + readonly kmsKey: IKey } export class SecretWithParameter extends Construct { @@ -21,7 +23,8 @@ export class SecretWithParameter extends Construct { const secret = new Secret(this, "Secret", { secretName: props.secretName, description: props.description, - secretStringValue: SecretValue.unsafePlainText(props.secretValue) + secretStringValue: SecretValue.unsafePlainText(props.secretValue), + encryptionKey: props.kmsKey }) // Create SSM parameter that references the secret diff --git a/packages/cdk/nagSuppressions.ts b/packages/cdk/nagSuppressions.ts index 92153616..3de3fd70 100644 --- a/packages/cdk/nagSuppressions.ts +++ b/packages/cdk/nagSuppressions.ts @@ -1,25 +1,13 @@ /* eslint-disable max-len */ import {Stack} from "aws-cdk-lib" -import {NagPackSuppression, NagSuppressions} from "cdk-nag" - -export const nagSuppressions = (stack: Stack, account: string) => { - // Suppress granular wildcard on log stream for SlackBot Lambda - safeAddNagSuppression( - stack, - "/EpsAssistMeStack/Functions/SlackBotLambda/LambdaPutLogsManagedPolicy/Resource", - [ - { - id: "AwsSolutions-IAM5", - reason: "Wildcard permissions for log stream access are required and scoped appropriately." - } - ] - ) +import {safeAddNagSuppressionGroup, safeAddNagSuppression} from "@nhsdigital/eps-cdk-constructs" +export const statefulNagSuppressions = (stack: Stack, account: string) => { // Suppress wildcard log permissions for SyncKnowledgeBase Lambda safeAddNagSuppression( stack, - "/EpsAssistMeStack/Functions/SyncKnowledgeBaseFunction/LambdaPutLogsManagedPolicy/Resource", + "/EpsAssistMeStateful/StatefulFunctions/SyncKnowledgeBaseFunction/LambdaPutLogsManagedPolicy/Resource", [ { id: "AwsSolutions-IAM5", @@ -31,7 +19,7 @@ export const nagSuppressions = (stack: Stack, account: string) => { // Suppress wildcard log permissions for Preprocessing Lambda safeAddNagSuppression( stack, - "/EpsAssistMeStack/Functions/PreprocessingFunction/LambdaPutLogsManagedPolicy/Resource", + "/EpsAssistMeStateful/StatefulFunctions/PreprocessingFunction/LambdaPutLogsManagedPolicy/Resource", [ { id: "AwsSolutions-IAM5", @@ -40,50 +28,10 @@ export const nagSuppressions = (stack: Stack, account: string) => { ] ) - // Suppress API Gateway validation warning for Apis construct - safeAddNagSuppression( - stack, - "/EpsAssistMeStack/Apis/EpsAssistApiGateway/ApiGateway/Resource", - [ - { - id: "AwsSolutions-APIG2", - reason: "Validation is handled within Lambda; request validation is intentionally omitted." - } - ] - ) - - // Suppress unauthenticated API route warnings - safeAddNagSuppression( - stack, - "/EpsAssistMeStack/Apis/EpsAssistApiGateway/ApiGateway/Default/slack/events/POST/Resource", - [ - { - id: "AwsSolutions-APIG4", - reason: "Slack event endpoint is intentionally unauthenticated." - }, - { - id: "AwsSolutions-COG4", - reason: "Cognito not required for this public endpoint." - } - ] - ) - - // Suppress missing WAF on API stage for Apis construct - safeAddNagSuppression( - stack, - "/EpsAssistMeStack/Apis/EpsAssistApiGateway/ApiGateway/DeploymentStage.prod/Resource", - [ - { - id: "AwsSolutions-APIG3", - reason: "WAF not in current scope; may be added later." - } - ] - ) - // Suppress IAM wildcard permissions for Bedrock execution role policy safeAddNagSuppression( stack, - "/EpsAssistMeStack/BedrockExecutionRole/Policy/Resource", + "/EpsAssistMeStateful/BedrockExecutionRole/Policy/Resource", [ { id: "AwsSolutions-IAM5", @@ -106,7 +54,7 @@ export const nagSuppressions = (stack: Stack, account: string) => { ) safeAddNagSuppression( stack, - "/EpsAssistMeStack/BedrockExecutionRole/WildcardPolicy/Resource", + "/EpsAssistMeStateful/BedrockExecutionRole/WildcardPolicy/Resource", [ { id: "AwsSolutions-IAM5", @@ -115,48 +63,36 @@ export const nagSuppressions = (stack: Stack, account: string) => { ] ) - // Suppress wildcard permissions for SlackBot policy + // Suppress wildcard permissions for Preprocessing policy safeAddNagSuppression( stack, - "/EpsAssistMeStack/RuntimePolicies/SlackBotPolicy/Resource", + "/EpsAssistMeStateful/StatefulRuntimePolicies/PreprocessingPolicy/Resource", [ { id: "AwsSolutions-IAM5", - reason: "SlackBot Lambda needs wildcard permissions for guardrails, knowledge bases, and function invocation.", - appliesTo: [ - "Resource::arn:aws:lambda:eu-west-2::function:epsam*", - "Resource::arn:aws:cloudformation:eu-west-2::stack/epsam-pr-*", - `Resource::arn:aws:lambda:eu-west-2:${account}:function:epsam*`, - `Resource::arn:aws:cloudformation:eu-west-2:${account}:stack/epsam-pr-*`, - "Resource::arn:aws:bedrock:*" - ] + reason: "Preprocessing Lambda needs wildcard permissions to read/write any file in raw/ and processed/ prefixes." } ] ) - // Suppress wildcard permissions for Preprocessing policy safeAddNagSuppression( stack, - "/EpsAssistMeStack/RuntimePolicies/PreprocessingPolicy/Resource", + "/EpsAssistMeStateful/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy/Resource", [ { - id: "AwsSolutions-IAM5", - reason: "Preprocessing Lambda needs wildcard permissions to read/write any file in raw/ and processed/ prefixes." + id: "EpsNagPack-EPS10", + reason: "Role created by CDK lib uses inline policies." } ] ) - // Suppress secrets without rotation - safeAddNagSuppressionGroup( + safeAddNagSuppression( stack, - [ - "/EpsAssistMeStack/Secrets/SlackBotToken/Secret/Resource", - "/EpsAssistMeStack/Secrets/SlackBotSigning/Secret/Resource" - ], + "/EpsAssistMeStateful/ApiDomainName/ApiDomain/Resource", [ { - id: "AwsSolutions-SMG4", - reason: "Slack secrets rotation is handled manually as part of the Slack app configuration process." + id: "EpsNagPack-EPS1", + reason: "API Gateway does not have mutual TLS in this application." } ] ) @@ -180,134 +116,116 @@ export const nagSuppressions = (stack: Stack, account: string) => { ) }) - // Suppress DelayResource IAM and runtime issues + // Suppress DelayFunction runtime version warnings safeAddNagSuppression( stack, - "/EpsAssistMeStack/VectorIndex/PolicySyncWait/LambdaExecutionRole/Resource", + "/EpsAssistMeStateful/VectorIndex/PolicySyncWait/epsam-stateful-policy-sync-waitDelayFunction/Resource", [ { - id: "AwsSolutions-IAM4", - reason: "DelayResource Lambda uses AWS managed policy for basic Lambda execution role.", - appliesTo: ["Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"] + id: "AwsSolutions-L1", + reason: "DelayResource uses Python 3.12 which is the latest stable runtime available for the delay functionality." } ] ) safeAddNagSuppression( stack, - "/EpsAssistMeStack/VectorIndex/IndexReadyWait/LambdaExecutionRole/Resource", + "/EpsAssistMeStateful/VectorIndex/IndexReadyWait/epsam-stateful-index-ready-waitDelayFunction/Resource", [ { - id: "AwsSolutions-IAM4", - reason: "DelayResource Lambda uses AWS managed policy for basic Lambda execution role.", - appliesTo: ["Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"] + id: "AwsSolutions-L1", + reason: "DelayResource uses Python 3.12 which is the latest stable runtime available for the delay functionality." } ] ) - // Suppress DelayProvider framework ServiceRole issues + // suppress onEvent runtime warnings as this is managed by the CDK team + // see https://github.com/aws/aws-cdk/issues/36269 for issue raised with CDK team safeAddNagSuppression( stack, - "/EpsAssistMeStack/VectorIndex/PolicySyncWait/DelayProvider/framework-onEvent/ServiceRole/Resource", + "/EpsAssistMeStateful/VectorIndex/PolicySyncWait/epsam-stateful-policy-sync-waitDelayProvider/framework-onEvent/Resource", [ { - id: "AwsSolutions-IAM4", - reason: "Auto-generated CDK Provider role uses AWS managed policy for Lambda execution.", - appliesTo: ["Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"] + id: "AwsSolutions-L1", + reason: "OnEvent uses Node22.x which is the latest stable runtime available for the onEvent functionality." } ] ) safeAddNagSuppression( stack, - "/EpsAssistMeStack/VectorIndex/PolicySyncWait/DelayProvider/framework-onEvent/ServiceRole/DefaultPolicy/Resource", + "/EpsAssistMeStateful/VectorIndex/IndexReadyWait/epsam-stateful-index-ready-waitDelayProvider/framework-onEvent/Resource", [ { - id: "AwsSolutions-IAM5", - reason: "Auto-generated CDK Provider role requires wildcard permissions for Lambda invocation.", - appliesTo: ["Resource:::*"] + id: "AwsSolutions-L1", + reason: "OnEvent uses Node22.x which is the latest stable runtime available for the onEvent functionality." } ] ) + // Suppress BucketDeployment (S3 folder initializer) suppressions safeAddNagSuppression( stack, - "/EpsAssistMeStack/VectorIndex/IndexReadyWait/DelayProvider/framework-onEvent/ServiceRole/Resource", + "/EpsAssistMeStateful/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/Resource", [ { id: "AwsSolutions-IAM4", - reason: "Auto-generated CDK Provider role uses AWS managed policy for Lambda execution.", + reason: "BucketDeployment uses AWS managed policy for Lambda execution, required by CDK construct.", appliesTo: ["Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"] } ] ) + // Suppress BedrockLogging Lambda wildcard permissions for Bedrock API safeAddNagSuppression( stack, - "/EpsAssistMeStack/VectorIndex/IndexReadyWait/DelayProvider/framework-onEvent/ServiceRole/DefaultPolicy/Resource", + "/EpsAssistMeStateful/BedrockLogging/BedrockLoggingConfigPolicy/Resource", [ { id: "AwsSolutions-IAM5", - reason: "Auto-generated CDK Provider role requires wildcard permissions for Lambda invocation.", - appliesTo: ["Resource:::*"] - } - ] - ) - - // Suppress DelayFunction runtime version warnings - safeAddNagSuppression( - stack, - "/EpsAssistMeStack/VectorIndex/PolicySyncWait/DelayFunction/Resource", - [ - { - id: "AwsSolutions-L1", - reason: "DelayResource uses Python 3.12 which is the latest stable runtime available for the delay functionality." + reason: "Bedrock logging configuration API requires wildcard resource permissions as it's account-level configuration." } ] ) + // Suppress BedrockLogging Lambda log group and put logs permissions safeAddNagSuppression( stack, - "/EpsAssistMeStack/VectorIndex/IndexReadyWait/DelayFunction/Resource", + "/EpsAssistMeStateful/BedrockLogging/LoggingConfigFunction/LambdaPutLogsManagedPolicy/Resource", [ { - id: "AwsSolutions-L1", - reason: "DelayResource uses Python 3.12 which is the latest stable runtime available for the delay functionality." + id: "AwsSolutions-IAM5", + reason: "Wildcard permissions for log stream access are required and scoped appropriately." } ] ) safeAddNagSuppression( stack, - "/EpsAssistMeStack/RegressionTestPolicy/Resource", + "/EpsAssistMeStateful/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy/Resource", [ { id: "AwsSolutions-IAM5", - reason: "Auto-generated CDK Provider role requires wildcard permissions for cloudformation stack listing.", - appliesTo: [ - "Resource::arn:aws:cloudformation:eu-west-2::stack/epsam*", - `Resource::arn:aws:cloudformation:eu-west-2:${account}:stack/epsam*` - ] + reason: "BucketDeployment requires wildcard permissions for S3 and KMS operations to deploy assets." } ] ) - // suppress onEvent runtime warnings as this is managed by the CDK team - // see https://github.com/aws/aws-cdk/issues/36269 for issue raised with CDK team safeAddNagSuppression( stack, - "/EpsAssistMeStack/VectorIndex/PolicySyncWait/DelayProvider/framework-onEvent/Resource", + "/EpsAssistMeStateful/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Resource", [ { id: "AwsSolutions-L1", - reason: "OnEvent uses Node22.x which is the latest stable runtime available for the onEvent functionality." + reason: "BucketDeployment uses CDK-managed Lambda runtime, updated by CDK library." } ] ) + // Suppress BedrockLogging Provider framework runtime version safeAddNagSuppression( stack, - "/EpsAssistMeStack/VectorIndex/IndexReadyWait/DelayProvider/framework-onEvent/Resource", + "/EpsAssistMeStateful/BedrockLogging/LoggingConfigProvider/framework-onEvent/Resource", [ { id: "AwsSolutions-L1", @@ -316,144 +234,134 @@ export const nagSuppressions = (stack: Stack, account: string) => { ] ) - // Suppress BucketDeployment (S3 folder initializer) suppressions + // Suppress BedrockLogging Provider framework runtime version safeAddNagSuppression( stack, - "/EpsAssistMeStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/Resource", + "/EpsAssistMeStateful/BucketNotificationsHandler050a0587b7544547bf325f094a3db834/Role/DefaultPolicy/Resource", [ { - id: "AwsSolutions-IAM4", - reason: "BucketDeployment uses AWS managed policy for Lambda execution, required by CDK construct.", - appliesTo: ["Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"] + id: "EpsNagPack-EPS10", + reason: "Bucket notification role uses inline policies." } ] ) - // Suppress BedrockLogging KMS wildcard permissions + + // Suppress BedrockLogging Provider framework runtime version safeAddNagSuppression( stack, - "/EpsAssistMeStack/BedrockLogging/BedrockLoggingRole/DefaultPolicy/Resource", + "/EpsAssistMeStateful/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/LogGroup/Resource", [ { - id: "AwsSolutions-IAM5", - reason: "KMS wildcard permissions (GenerateDataKey*, ReEncrypt*) are required for CloudWatch Logs encryption operations.", - appliesTo: [ - "Action::kms:GenerateDataKey*", - "Action::kms:ReEncrypt*", - "Resource:::*" - ] + id: "EpsNagPack-EPS3", + reason: "Cloudwatch log group for bucket sync not encrypted by KMS." } ] ) +} - // Suppress BedrockLogging Lambda wildcard permissions for Bedrock API +export const statelessNagSuppressions = (stack: Stack, account: string) => { + // Suppress granular wildcard on log stream for SlackBot Lambda safeAddNagSuppression( stack, - "/EpsAssistMeStack/BedrockLogging/BedrockLoggingConfigPolicy/Resource", + "/EpsAssistMeStateless/Functions/SlackBotLambda/LambdaPutLogsManagedPolicy/Resource", [ { id: "AwsSolutions-IAM5", - reason: "Bedrock logging configuration API requires wildcard resource permissions as it's account-level configuration." + reason: "Wildcard permissions for log stream access are required and scoped appropriately." } ] ) - // Suppress BedrockLogging Lambda log group and put logs permissions + // Suppress API Gateway validation warning for Apis construct safeAddNagSuppression( stack, - "/EpsAssistMeStack/BedrockLogging/LoggingConfigFunction/LambdaPutLogsManagedPolicy/Resource", + "/EpsAssistMeStateless/Apis/EpsAssistApiGateway/ApiGateway/Resource", [ { - id: "AwsSolutions-IAM5", - reason: "Wildcard permissions for log stream access are required and scoped appropriately." + id: "AwsSolutions-APIG2", + reason: "Validation is handled within Lambda; request validation is intentionally omitted." } ] ) - // Suppress BedrockLogging Provider framework role using AWS managed policy + // Suppress unauthenticated API route warnings safeAddNagSuppression( stack, - "/EpsAssistMeStack/BedrockLogging/LoggingConfigProvider/framework-onEvent/ServiceRole/Resource", + "/EpsAssistMeStateless/Apis/EpsAssistApiGateway/ApiGateway/Default/slack/events/POST/Resource", [ { - id: "AwsSolutions-IAM4", - reason: "Auto-generated CDK Provider role uses AWS managed policy for Lambda execution.", - appliesTo: [ - "Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] + id: "AwsSolutions-APIG4", + reason: "Slack event endpoint is intentionally unauthenticated." + }, + { + id: "AwsSolutions-COG4", + reason: "Cognito not required for this public endpoint." } ] ) + // Suppress missing WAF on API stage for Apis construct safeAddNagSuppression( stack, - "/EpsAssistMeStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy/Resource", + "/EpsAssistMeStateless/Apis/EpsAssistApiGateway/ApiGateway/DeploymentStage.prod/Resource", [ { - id: "AwsSolutions-IAM5", - reason: "BucketDeployment requires wildcard permissions for S3 and KMS operations to deploy assets." + id: "AwsSolutions-APIG3", + reason: "WAF not in current scope; may be added later." } ] ) - // Suppress BedrockLogging Provider framework wildcard permissions + + // Suppress wildcard permissions for SlackBot policy safeAddNagSuppression( stack, - "/EpsAssistMeStack/BedrockLogging/LoggingConfigProvider/framework-onEvent/ServiceRole/DefaultPolicy/Resource", + "/EpsAssistMeStateless/RuntimePolicies/SlackBotPolicy/Resource", [ { id: "AwsSolutions-IAM5", - reason: "Auto-generated CDK Provider role requires wildcard permissions for Lambda invocation." + reason: "SlackBot Lambda needs wildcard permissions for guardrails, knowledge bases, and function invocation.", + appliesTo: [ + "Resource::arn:aws:lambda:eu-west-2::function:epsam*", + "Resource::arn:aws:cloudformation:eu-west-2::stack/epsam-pr-*", + `Resource::arn:aws:lambda:eu-west-2:${account}:function:epsam*`, + `Resource::arn:aws:cloudformation:eu-west-2:${account}:stack/epsam-pr-*`, + "Resource::arn:aws:bedrock:*" + ] } ] ) - safeAddNagSuppression( + // Suppress secrets without rotation + safeAddNagSuppressionGroup( stack, - "/EpsAssistMeStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Resource", + [ + "/EpsAssistMeStateless/Secrets/SlackBotToken/Secret/Resource", + "/EpsAssistMeStateless/Secrets/SlackBotSigning/Secret/Resource" + ], [ { - id: "AwsSolutions-L1", - reason: "BucketDeployment uses CDK-managed Lambda runtime, updated by CDK library." + id: "AwsSolutions-SMG4", + reason: "Slack secrets rotation is handled manually as part of the Slack app configuration process." } ] ) - // Suppress KMS wildcard permissions for Preprocessing Lambda role safeAddNagSuppression( stack, - "/EpsAssistMeStack/Functions/PreprocessingFunction/LambdaRole/DefaultPolicy/Resource", + "/EpsAssistMeStateless/RegressionTestPolicy/Resource", [ { id: "AwsSolutions-IAM5", - reason: "Preprocessing Lambda requires KMS wildcard permissions for S3 encryption operations.", + reason: "Auto-generated CDK Provider role requires wildcard permissions for cloudformation stack listing.", appliesTo: [ - "Action::kms:GenerateDataKey*", - "Action::kms:ReEncrypt*" + "Resource::arn:aws:cloudformation:eu-west-2::stack/epsam*", + `Resource::arn:aws:cloudformation:eu-west-2:${account}:stack/epsam*` ] } ] ) - // Suppress BedrockLogging Provider framework runtime version - safeAddNagSuppression( - stack, - "/EpsAssistMeStack/BedrockLogging/LoggingConfigProvider/framework-onEvent/Resource", - [ - { - id: "AwsSolutions-L1", - reason: "OnEvent uses Node22.x which is the latest stable runtime available for the onEvent functionality." - } - ] - ) } -const safeAddNagSuppression = (stack: Stack, path: string, suppressions: Array) => { - try { - NagSuppressions.addResourceSuppressionsByPath(stack, path, suppressions) - } catch (err) { - console.log(`Could not find path ${path}: ${err}`) - } -} - -const safeAddNagSuppressionGroup = (stack: Stack, paths: Array, suppressions: Array) => { - paths.forEach(path => safeAddNagSuppression(stack, path, suppressions)) -} +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const basePathMappingNagSuppressions = (stack: Stack, account: string) => {} diff --git a/packages/cdk/package.json b/packages/cdk/package.json index 9163b0dc..5a9479b0 100644 --- a/packages/cdk/package.json +++ b/packages/cdk/package.json @@ -2,20 +2,23 @@ "name": "cdk", "version": "0.1.0", "scripts": { - "build": "tsc", - "watch": "tsc -w", - "test": "jest --passWithNoTests", - "cdk": "cdk", + "cdk-synth": "cdk synth ${CDK_STACK_NAME} --output ../../cdk.out --quiet --app \"npm run tsx -- bin/${CDK_APP_NAME}.ts\"", + "cdk-diff": "cdk diff --app \"npm run tsx -- bin/${CDK_APP_NAME}.ts\"", + "cdk-deploy": "cdk deploy ${CDK_STACK_NAME} --app \"npm run tsx -- bin/${CDK_APP_NAME}.ts\" --ci true --require-approval ${REQUIRE_APPROVAL}", + "cdk-watch": "cdk deploy --app \"npm run tsx -- bin/${CDK_APP_NAME}.ts\" --watch --all --ci true --require-approval ${REQUIRE_APPROVAL}", + "cdk-flags": "cdk flags --unstable=flags --app \"npm run tsx -- bin/${CDK_APP_NAME}.ts\"", "lint": "eslint --max-warnings 0 --fix --config ../../eslint.config.mjs .", - "check-licenses": "license-checker --failOn GPL --failOn LGPL --start ../.." + "tsx": "tsx" }, "dependencies": { "@aws-cdk/aws-lambda-python-alpha": "^2.205.0-alpha.0", "@cdklabs/generative-ai-cdk-constructs": "^0.1.314", + "@nhsdigital/eps-cdk-constructs": "^1.3.0", "aws-cdk-lib": "^2.236.0", "cdk-nag": "^2.37.55", "constructs": "^10.4.5", - "source-map-support": "^0.5.21" + "source-map-support": "^0.5.21", + "tsx": "^4.21.0" }, "devDependencies": { "@types/node": "^25.1.0", diff --git a/packages/cdk/resources/Apis.ts b/packages/cdk/resources/Apis.ts index c67c9fba..38be05b6 100644 --- a/packages/cdk/resources/Apis.ts +++ b/packages/cdk/resources/Apis.ts @@ -1,19 +1,19 @@ import {Construct} from "constructs" import {RestApiGateway} from "../constructs/RestApiGateway" import {LambdaEndpoint} from "../constructs/RestApiGateway/LambdaEndpoint" -import {LambdaFunction} from "../constructs/LambdaFunction" +import {PythonLambdaFunction} from "@nhsdigital/eps-cdk-constructs" import {HttpMethod} from "aws-cdk-lib/aws-lambda" export interface ApisProps { readonly stackName: string readonly logRetentionInDays: number - functions: {[key: string]: LambdaFunction} + functions: {[key: string]: PythonLambdaFunction} readonly forwardCsocLogs: boolean readonly csocApiGatewayDestination: string } export class Apis extends Construct { - public apis: {[key: string]: RestApiGateway} + public apiGateway: RestApiGateway public constructor(scope: Construct, id: string, props: ApisProps) { super(scope, id) @@ -41,8 +41,6 @@ export class Apis extends Construct { lambdaFunction: props.functions.slackBot }) - this.apis = { - api: apiGateway - } + this.apiGateway = apiGateway } } diff --git a/packages/cdk/resources/BedrockLoggingConfiguration.ts b/packages/cdk/resources/BedrockLoggingConfiguration.ts index 7a4ce96f..3345fbfd 100644 --- a/packages/cdk/resources/BedrockLoggingConfiguration.ts +++ b/packages/cdk/resources/BedrockLoggingConfiguration.ts @@ -1,5 +1,5 @@ import {Construct} from "constructs" -import {CustomResource, RemovalPolicy} from "aws-cdk-lib" +import {CustomResource, Fn, RemovalPolicy} from "aws-cdk-lib" import { Role, ServicePrincipal, @@ -9,7 +9,9 @@ import { import {LogGroup, RetentionDays} from "aws-cdk-lib/aws-logs" import {Provider} from "aws-cdk-lib/custom-resources" import {Key} from "aws-cdk-lib/aws-kms" -import {LambdaFunction} from "../constructs/LambdaFunction" +import {PythonLambdaFunction} from "@nhsdigital/eps-cdk-constructs" +import {resolve} from "path" +import {NagSuppressions} from "cdk-nag" export interface BedrockLoggingConfigurationProps { readonly stackName: string @@ -26,61 +28,49 @@ export class BedrockLoggingConfiguration extends Construct { constructor(scope: Construct, id: string, props: BedrockLoggingConfigurationProps) { super(scope, id) - // Create KMS key for encryption - const kmsKey = new Key(this, "BedrockLogsKmsKey", { - description: `KMS key for ${props.stackName} Bedrock logs encryption`, - enableKeyRotation: true, - removalPolicy: RemovalPolicy.DESTROY - }) + const cloudWatchLogsKmsKey = Key.fromKeyArn( + scope, "cloudWatchLogsKmsKey", Fn.importValue("account-resources:CloudwatchLogsKmsKeyArn")) + + const cloudwatchEncryptionKMSPolicy = ManagedPolicy.fromManagedPolicyArn( + scope, "cloudwatchEncryptionKMSPolicyArn", Fn.importValue("account-resources:CloudwatchEncryptionKMSPolicyArn")) // Create CloudWatch Log Group for model invocations const modelInvocationLogGroup = new LogGroup(this, "ModelInvocationLogGroup", { logGroupName: `/aws/bedrock/${props.stackName}/model-invocations`, retention: props.logRetentionInDays as RetentionDays, - encryptionKey: kmsKey, + encryptionKey: cloudWatchLogsKmsKey, removalPolicy: RemovalPolicy.DESTROY }) - // Grant CloudWatch Logs service permission to use KMS key - kmsKey.addToResourcePolicy(new PolicyStatement({ - sid: "Allow CloudWatch Logs", - principals: [new ServicePrincipal(`logs.${props.region}.amazonaws.com`)], - actions: [ - "kms:Encrypt", - "kms:Decrypt", - "kms:ReEncrypt", - "kms:GenerateDataKey", - "kms:DescribeKey" - ], - resources: ["*"], - conditions: { - ArnLike: { - "kms:EncryptionContext:aws:logs:arn": - `arn:aws:logs:${props.region}:${props.account}:log-group:/aws/bedrock/${props.stackName}/*` - } + // Create IAM role for Bedrock to write logs + const putLogsManagedPolicy = new ManagedPolicy(scope, "LambdaPutLogsManagedPolicy", { + description: `write to ${modelInvocationLogGroup.logGroupName} logs`, + statements: [ + new PolicyStatement({ + actions: [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + resources: [`${modelInvocationLogGroup.logGroupArn}:*` ] + }) + ] + }) + NagSuppressions.addResourceSuppressions(putLogsManagedPolicy, [ + { + id: "AwsSolutions-IAM5", + // eslint-disable-next-line max-len + reason: "Suppress error for not having wildcards in permissions. This is a fine as we need to have permissions on all log streams under path" } - })) + ]) - // Create IAM role for Bedrock to write logs const bedrockLoggingRole = new Role(this, "BedrockLoggingRole", { assumedBy: new ServicePrincipal("bedrock.amazonaws.com"), - description: "Role for Bedrock to write model invocation logs" - }) - - // Grant permissions to write to CloudWatch Logs - bedrockLoggingRole.addToPolicy(new PolicyStatement({ - actions: [ - "logs:CreateLogStream", - "logs:PutLogEvents", - "logs:DescribeLogStreams" - ], - resources: [ - `${modelInvocationLogGroup.logGroupArn}:*` + description: "Role for Bedrock to write model invocation logs", + managedPolicies: [ + putLogsManagedPolicy, + cloudwatchEncryptionKMSPolicy ] - })) - - // Grant KMS permissions - kmsKey.grantEncryptDecrypt(bedrockLoggingRole) + }) // Create managed policy for Bedrock logging configuration const bedrockLoggingConfigPolicy = new ManagedPolicy(this, "BedrockLoggingConfigPolicy", { @@ -102,9 +92,9 @@ export class BedrockLoggingConfiguration extends Construct { }) // Create Lambda function for custom resource - const loggingConfigFunction = new LambdaFunction(this, "LoggingConfigFunction", { - stackName: props.stackName, + const loggingConfigFunction = new PythonLambdaFunction(this, "LoggingConfigFunction", { functionName: `${props.stackName}-BedrockLoggingConfig`, + projectBaseDir: resolve(__dirname, "../../.."), packageBasePath: "packages/bedrockLoggingConfigFunction", handler: "app.handler.handler", logRetentionInDays: props.logRetentionInDays, @@ -118,9 +108,43 @@ export class BedrockLoggingConfiguration extends Construct { } }) + const frameworkInvokeBedrockLoggingPolicy = new ManagedPolicy(this, + `${props.stackName}BedrockLoggingInvokeDelayFunctionPolicy`, { + description: `Allow provider framework to invoke ${props.stackName} bedrock logging function`, + statements: [ + new PolicyStatement({ + actions: [ + "lambda:InvokeFunction", + "lambda:GetFunction" + ], + resources: [ + loggingConfigFunction.function.functionArn + ] + }) + ] + }) + + const frameworkOnEventRole = new Role(this, `${props.stackName}BedrockLoggingProviderFrameworkOnEventRole`, { + assumedBy: new ServicePrincipal("lambda.amazonaws.com"), + description: "Execution role for bedrock logging provider framework onEvent Lambda function", + managedPolicies: [ + putLogsManagedPolicy, + cloudwatchEncryptionKMSPolicy, + frameworkInvokeBedrockLoggingPolicy + ] + }) + + const frameworkOnEventRoleRef = Role.fromRoleArn( + this, + `${props.stackName}OnEventRoleRef`, + frameworkOnEventRole.roleArn, + {mutable: false} + ) // Create custom resource provider const provider = new Provider(this, "LoggingConfigProvider", { - onEventHandler: loggingConfigFunction.function + onEventHandler: loggingConfigFunction.function, + logGroup: modelInvocationLogGroup, + frameworkOnEventRole: frameworkOnEventRoleRef }) // Create custom resource diff --git a/packages/cdk/resources/DomainName.ts b/packages/cdk/resources/DomainName.ts new file mode 100644 index 00000000..5f40f55f --- /dev/null +++ b/packages/cdk/resources/DomainName.ts @@ -0,0 +1,61 @@ +import {Fn} from "aws-cdk-lib" +import {DomainName, EndpointType, SecurityPolicy} from "aws-cdk-lib/aws-apigateway" +import {Construct} from "constructs" +import {Certificate, CertificateValidation} from "aws-cdk-lib/aws-certificatemanager" +import { + ARecord, + AaaaRecord, + HostedZone, + RecordTarget +} from "aws-cdk-lib/aws-route53" +import {ApiGatewayDomain} from "aws-cdk-lib/aws-route53-targets" + +export interface ApiDomainNameProps { + readonly apiGatewayDomainName: string +} + +export class ApiDomainName extends Construct { + public readonly domain: DomainName + + public constructor(scope: Construct, id: string, props: ApiDomainNameProps) { + super(scope, id) + + // Imports + + const epsDomainName: string = Fn.importValue("eps-route53-resources:EPS-domain") + const hostedZone = HostedZone.fromHostedZoneAttributes(this, "HostedZone", { + hostedZoneId: Fn.importValue("eps-route53-resources:EPS-ZoneID"), + zoneName: epsDomainName + }) + const serviceDomainName = `${props.apiGatewayDomainName}.${epsDomainName}` + + // Resources + + const certificate = new Certificate(this, "Certificate", { + domainName: serviceDomainName, + validation: CertificateValidation.fromDns(hostedZone) + }) + + const domain = new DomainName(this, "ApiDomain", { + domainName: serviceDomainName, + certificate, + securityPolicy: SecurityPolicy.TLS_1_2, + endpointType: EndpointType.REGIONAL + }) + + new ARecord(this, "ARecord", { + recordName: props.apiGatewayDomainName, + target: RecordTarget.fromAlias(new ApiGatewayDomain(domain)), + zone: hostedZone + }) + + new AaaaRecord(this, "AAAARecord", { + recordName: props.apiGatewayDomainName, + target: RecordTarget.fromAlias(new ApiGatewayDomain(domain)), + zone: hostedZone + }) + + // Outputs + this.domain = domain + } +} diff --git a/packages/cdk/resources/GuardRailResources.ts b/packages/cdk/resources/GuardRailResources.ts new file mode 100644 index 00000000..350ba9ce --- /dev/null +++ b/packages/cdk/resources/GuardRailResources.ts @@ -0,0 +1,87 @@ +import {Construct} from "constructs" +import { + ContentFilterStrength, + ContentFilterType, + Guardrail, + GuardrailAction, + ManagedWordFilterType, + PIIType +} from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock" + +export interface GuardRailResourcesProps { + readonly stackName: string +} + +export class GuardRailResources extends Construct { + public readonly guardrail: Guardrail + + constructor(scope: Construct, id: string, props: GuardRailResourcesProps) { + super(scope, id) + + const guardrail = new Guardrail(this, "bedrockGuardrails", { + name: `${props.stackName}-guardrail`, + description: "Guardrail for EPS Assist Me Slackbot", + blockedInputMessaging: "Your input was blocked.", + blockedOutputsMessaging: "Your output was blocked.", + contentFilters: [ + { + type: ContentFilterType.SEXUAL, + inputStrength: ContentFilterStrength.HIGH, + outputStrength: ContentFilterStrength.HIGH + }, + { + type: ContentFilterType.VIOLENCE, + inputStrength: ContentFilterStrength.HIGH, + outputStrength: ContentFilterStrength.HIGH + }, + { + type: ContentFilterType.HATE, + inputStrength: ContentFilterStrength.HIGH, + outputStrength: ContentFilterStrength.HIGH + }, + { + type: ContentFilterType.INSULTS, + inputStrength: ContentFilterStrength.HIGH, + outputStrength: ContentFilterStrength.HIGH + }, + { + type: ContentFilterType.MISCONDUCT, + inputStrength: ContentFilterStrength.HIGH, + outputStrength: ContentFilterStrength.HIGH + }, + { + type: ContentFilterType.PROMPT_ATTACK, + inputStrength: ContentFilterStrength.HIGH, + outputStrength: ContentFilterStrength.NONE + } + ], + piiFilters: [ + { + type: PIIType.General.EMAIL, + action: GuardrailAction.ANONYMIZE + }, + { + type: PIIType.General.PHONE, + action: GuardrailAction.ANONYMIZE + }, + { + type: PIIType.General.NAME, + action: GuardrailAction.ANONYMIZE + }, + { + type: PIIType.Finance.CREDIT_DEBIT_CARD_NUMBER, + action: GuardrailAction.BLOCK + }, + { + type: PIIType.UKSpecific.UK_NATIONAL_HEALTH_SERVICE_NUMBER, + action: GuardrailAction.ANONYMIZE + } + ], + managedWordListFilters: [ + {type: ManagedWordFilterType.PROFANITY} + ] + }) + + this.guardrail = guardrail + } +} diff --git a/packages/cdk/resources/Secrets.ts b/packages/cdk/resources/Secrets.ts index b8b2b1a0..5593ed53 100644 --- a/packages/cdk/resources/Secrets.ts +++ b/packages/cdk/resources/Secrets.ts @@ -2,6 +2,8 @@ import {Construct} from "constructs" import {StringParameter} from "aws-cdk-lib/aws-ssm" import {Secret} from "aws-cdk-lib/aws-secretsmanager" import {SecretWithParameter} from "../constructs/SecretWithParameter" +import {Key} from "aws-cdk-lib/aws-kms" +import {RemovalPolicy} from "aws-cdk-lib" export interface SecretsProps { readonly stackName: string @@ -14,16 +16,25 @@ export class Secrets extends Construct { public readonly slackBotSigningSecret: Secret public readonly slackBotTokenParameter: StringParameter public readonly slackSigningSecretParameter: StringParameter + public readonly secretKmsKey: Key constructor(scope: Construct, id: string, props: SecretsProps) { super(scope, id) + const kmsKey = new Key(this, "BucketKey", { + enableKeyRotation: true, + description: `KMS key for ${props.stackName} secret encryption`, + removalPolicy: RemovalPolicy.DESTROY + }) + kmsKey.addAlias(`alias/${props.stackName}-secret-key`) + // Create Slack bot OAuth token secret and parameter const slackBotToken = new SecretWithParameter(this, "SlackBotToken", { secretName: `/${props.stackName}/bot-token`, parameterName: `/${props.stackName}/bot-token/parameter`, description: "Slack Bot OAuth Token for EPS Assist", - secretValue: JSON.stringify({token: props.slackBotToken}) + secretValue: JSON.stringify({token: props.slackBotToken}), + kmsKey: kmsKey }) // Create Slack signing secret for request verification @@ -31,7 +42,8 @@ export class Secrets extends Construct { secretName: `/${props.stackName}/signing-secret`, parameterName: `/${props.stackName}/signing-secret/parameter`, description: "Slack Signing Secret", - secretValue: JSON.stringify({secret: props.slackSigningSecret}) + secretValue: JSON.stringify({secret: props.slackSigningSecret}), + kmsKey: kmsKey }) // Export secrets and parameters for use by other constructs @@ -39,5 +51,6 @@ export class Secrets extends Construct { this.slackBotSigningSecret = slackBotSigning.secret this.slackBotTokenParameter = slackBotToken.parameter this.slackSigningSecretParameter = slackBotSigning.parameter + this.secretKmsKey = kmsKey } } diff --git a/packages/cdk/resources/StatefulFunctions.ts b/packages/cdk/resources/StatefulFunctions.ts new file mode 100644 index 00000000..78653bf0 --- /dev/null +++ b/packages/cdk/resources/StatefulFunctions.ts @@ -0,0 +1,65 @@ +import {Construct} from "constructs" +import {PythonLambdaFunction} from "@nhsdigital/eps-cdk-constructs" +import {ManagedPolicy} from "aws-cdk-lib/aws-iam" +import {resolve} from "path" + +export interface StatefulFunctionsProps { + readonly stackName: string + readonly version: string + readonly commitId: string + readonly logRetentionInDays: number + readonly logLevel: string + readonly syncKnowledgeBaseManagedPolicy: ManagedPolicy + readonly preprocessingManagedPolicy: ManagedPolicy + readonly knowledgeBaseId: string + readonly dataSourceId: string + readonly region: string + readonly account: string + readonly docsBucketName: string +} + +export class StatefulFunctions extends Construct { + public readonly syncKnowledgeBaseFunction: PythonLambdaFunction + public readonly preprocessingFunction: PythonLambdaFunction + + constructor(scope: Construct, id: string, props: StatefulFunctionsProps) { + super(scope, id) + + // Lambda function to preprocess documents (convert to markdown) + const preprocessingFunction = new PythonLambdaFunction(this, "PreprocessingFunction", { + functionName: `${props.stackName}-PreprocessingFunction`, + projectBaseDir: resolve(__dirname, "../../.."), + packageBasePath: "packages/preprocessingFunction", + handler: "app.handler.handler", + logRetentionInDays: props.logRetentionInDays, + logLevel: props.logLevel, + dependencyLocation: ".dependencies/preprocessingFunction", + environmentVariables: { + "DOCS_BUCKET_NAME": props.docsBucketName, + "RAW_PREFIX": "raw/", + "PROCESSED_PREFIX": "processed/", + "AWS_ACCOUNT_ID": props.account + }, + additionalPolicies: [props.preprocessingManagedPolicy] + }) + + // Lambda function to sync knowledge base on S3 events + const syncKnowledgeBaseFunction = new PythonLambdaFunction(this, "SyncKnowledgeBaseFunction", { + functionName: `${props.stackName}-SyncKnowledgeBaseFunction`, + projectBaseDir: resolve(__dirname, "../../.."), + packageBasePath: "packages/syncKnowledgeBaseFunction", + handler: "app.handler.handler", + logRetentionInDays: props.logRetentionInDays, + logLevel: props.logLevel, + dependencyLocation: ".dependencies/syncKnowledgeBaseFunction", + environmentVariables: { + "KNOWLEDGEBASE_ID": props.knowledgeBaseId, + "DATA_SOURCE_ID": props.dataSourceId + }, + additionalPolicies: [props.syncKnowledgeBaseManagedPolicy] + }) + + this.preprocessingFunction = preprocessingFunction + this.syncKnowledgeBaseFunction = syncKnowledgeBaseFunction + } +} diff --git a/packages/cdk/resources/StatefulRuntimePolicies.ts b/packages/cdk/resources/StatefulRuntimePolicies.ts new file mode 100644 index 00000000..49277484 --- /dev/null +++ b/packages/cdk/resources/StatefulRuntimePolicies.ts @@ -0,0 +1,62 @@ +import {Construct} from "constructs" +import {PolicyStatement, ManagedPolicy} from "aws-cdk-lib/aws-iam" + +export interface StatefulRuntimePoliciesProps { + readonly knowledgeBaseArn: string + readonly dataSourceArn: string + readonly docsBucketArn: string + readonly docsBucketKmsKeyArn: string +} + +export class StatefulRuntimePolicies extends Construct { + public readonly syncKnowledgeBasePolicy: ManagedPolicy + public readonly preprocessingPolicy: ManagedPolicy + + constructor(scope: Construct, id: string, props: StatefulRuntimePoliciesProps) { + super(scope, id) + + // Create managed policy for SyncKnowledgeBase Lambda function + const syncKnowledgeBasePolicy = new PolicyStatement({ + actions: [ + "bedrock:StartIngestionJob", + "bedrock:GetIngestionJob", + "bedrock:ListIngestionJobs" + ], + resources: [ + props.knowledgeBaseArn, + props.dataSourceArn + ] + }) + + this.syncKnowledgeBasePolicy = new ManagedPolicy(this, "SyncKnowledgeBasePolicy", { + description: "Policy for SyncKnowledgeBase Lambda to trigger ingestion jobs", + statements: [syncKnowledgeBasePolicy] + }) + + //policy for the preprocessing lambda + const preprocessingS3Policy = new PolicyStatement({ + actions: [ + "s3:GetObject", + "s3:PutObject" + ], + resources: [ + `${props.docsBucketArn}/raw/*`, + `${props.docsBucketArn}/processed/*` + ] + }) + + const preprocessingKmsPolicy = new PolicyStatement({ + actions: [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey" + ], + resources: [props.docsBucketKmsKeyArn] + }) + + this.preprocessingPolicy = new ManagedPolicy(this, "PreprocessingPolicy", { + description: "Policy for Preprocessing Lambda to read from raw/ and write to processed/", + statements: [preprocessingS3Policy, preprocessingKmsPolicy] + }) + } +} diff --git a/packages/cdk/resources/Functions.ts b/packages/cdk/resources/StatelessFunctions.ts similarity index 53% rename from packages/cdk/resources/Functions.ts rename to packages/cdk/resources/StatelessFunctions.ts index ad770fab..56d4a930 100644 --- a/packages/cdk/resources/Functions.ts +++ b/packages/cdk/resources/StatelessFunctions.ts @@ -1,13 +1,12 @@ import {Construct} from "constructs" -import {LambdaFunction} from "../constructs/LambdaFunction" +import {PythonLambdaFunction} from "@nhsdigital/eps-cdk-constructs" import {ManagedPolicy, PolicyStatement, Role} from "aws-cdk-lib/aws-iam" import {StringParameter} from "aws-cdk-lib/aws-ssm" -import {Secret} from "aws-cdk-lib/aws-secretsmanager" -import {TableV2} from "aws-cdk-lib/aws-dynamodb" +import {resolve} from "path" const LAMBDA_MEMORY_SIZE = "265" -export interface FunctionsProps { +export interface StatelessFunctionsProps { readonly stackName: string readonly version: string readonly commitId: string @@ -15,19 +14,11 @@ export interface FunctionsProps { readonly logLevel: string readonly slackBotManagedPolicy: ManagedPolicy readonly slackBotTokenParameter: StringParameter - readonly syncKnowledgeBaseManagedPolicy: ManagedPolicy - readonly preprocessingManagedPolicy: ManagedPolicy readonly slackSigningSecretParameter: StringParameter readonly guardrailId: string readonly guardrailVersion: string - readonly collectionId: string readonly knowledgeBaseId: string - readonly dataSourceId: string - readonly region: string - readonly account: string - readonly slackBotTokenSecret: Secret - readonly slackBotSigningSecret: Secret - readonly slackBotStateTable: TableV2 + readonly slackBotStateTableName: string readonly reformulationPromptName: string readonly ragResponsePromptName: string readonly reformulationPromptVersion: string @@ -36,21 +27,18 @@ export interface FunctionsProps { readonly mainSlackBotLambdaExecutionRoleArn : string readonly ragModelId: string readonly queryReformulationModelId: string - readonly docsBucketName: string } -export class Functions extends Construct { - public readonly slackBotLambda: LambdaFunction - public readonly syncKnowledgeBaseFunction: LambdaFunction - public readonly preprocessingFunction: LambdaFunction +export class StatelessFunctions extends Construct { + public readonly slackBotLambda: PythonLambdaFunction - constructor(scope: Construct, id: string, props: FunctionsProps) { + constructor(scope: Construct, id: string, props: StatelessFunctionsProps) { super(scope, id) // Lambda function to handle Slack bot interactions (events and @mentions) - const slackBotLambda = new LambdaFunction(this, "SlackBotLambda", { - stackName: props.stackName, + const slackBotLambda = new PythonLambdaFunction(this, "SlackBotLambda", { functionName: `${props.stackName}-SlackBotFunction`, + projectBaseDir: resolve(__dirname, "../../.."), packageBasePath: "packages/slackBotFunction", handler: "app.handler.handler", logRetentionInDays: props.logRetentionInDays, @@ -66,11 +54,12 @@ export class Functions extends Construct { "SLACK_SIGNING_SECRET_PARAMETER": props.slackSigningSecretParameter.parameterName, "GUARD_RAIL_ID": props.guardrailId, "GUARD_RAIL_VERSION": props.guardrailVersion, - "SLACK_BOT_STATE_TABLE": props.slackBotStateTable.tableName, + "SLACK_BOT_STATE_TABLE": props.slackBotStateTableName, "QUERY_REFORMULATION_PROMPT_NAME": props.reformulationPromptName, "RAG_RESPONSE_PROMPT_NAME": props.ragResponsePromptName, "QUERY_REFORMULATION_PROMPT_VERSION": props.reformulationPromptVersion, - "RAG_RESPONSE_PROMPT_VERSION": props.ragResponsePromptVersion + "RAG_RESPONSE_PROMPT_VERSION": props.ragResponsePromptVersion, + "STACK_NAME": props.stackName } }) @@ -99,42 +88,6 @@ export class Functions extends Construct { mainSlackBotLambdaExecutionRole.addManagedPolicy(executeSlackBotPolicy) } - // Lambda function to preprocess documents (convert to markdown) - const preprocessingFunction = new LambdaFunction(this, "PreprocessingFunction", { - stackName: props.stackName, - functionName: `${props.stackName}-PreprocessingFunction`, - packageBasePath: "packages/preprocessingFunction", - handler: "app.handler.handler", - logRetentionInDays: props.logRetentionInDays, - logLevel: props.logLevel, - dependencyLocation: ".dependencies/preprocessingFunction", - environmentVariables: { - "DOCS_BUCKET_NAME": props.docsBucketName, - "RAW_PREFIX": "raw/", - "PROCESSED_PREFIX": "processed/", - "AWS_ACCOUNT_ID": props.account - }, - additionalPolicies: [props.preprocessingManagedPolicy] - }) - - // Lambda function to sync knowledge base on S3 events - const syncKnowledgeBaseFunction = new LambdaFunction(this, "SyncKnowledgeBaseFunction", { - stackName: props.stackName, - functionName: `${props.stackName}-SyncKnowledgeBaseFunction`, - packageBasePath: "packages/syncKnowledgeBaseFunction", - handler: "app.handler.handler", - logRetentionInDays: props.logRetentionInDays, - logLevel: props.logLevel, - dependencyLocation: ".dependencies/syncKnowledgeBaseFunction", - environmentVariables: { - "KNOWLEDGEBASE_ID": props.knowledgeBaseId, - "DATA_SOURCE_ID": props.dataSourceId - }, - additionalPolicies: [props.syncKnowledgeBaseManagedPolicy] - }) - this.slackBotLambda = slackBotLambda - this.preprocessingFunction = preprocessingFunction - this.syncKnowledgeBaseFunction = syncKnowledgeBaseFunction } } diff --git a/packages/cdk/resources/RuntimePolicies.ts b/packages/cdk/resources/StatelessRuntimePolicies.ts similarity index 66% rename from packages/cdk/resources/RuntimePolicies.ts rename to packages/cdk/resources/StatelessRuntimePolicies.ts index aadf31ff..1b164440 100644 --- a/packages/cdk/resources/RuntimePolicies.ts +++ b/packages/cdk/resources/StatelessRuntimePolicies.ts @@ -1,7 +1,7 @@ import {Construct} from "constructs" import {PolicyStatement, ManagedPolicy} from "aws-cdk-lib/aws-iam" -export interface RuntimePoliciesProps { +export interface StatelessRuntimePoliciesProps { readonly region: string readonly account: string readonly slackBotTokenParameterName: string @@ -10,20 +10,14 @@ export interface RuntimePoliciesProps { readonly slackBotStateTableKmsKeyArn: string readonly knowledgeBaseArn: string readonly guardrailArn: string - readonly dataSourceArn: string - readonly promptName: string readonly ragModelId: string readonly queryReformulationModelId: string - readonly docsBucketArn: string - readonly docsBucketKmsKeyArn: string } -export class RuntimePolicies extends Construct { +export class StatelessRuntimePolicies extends Construct { public readonly slackBotPolicy: ManagedPolicy - public readonly syncKnowledgeBasePolicy: ManagedPolicy - public readonly preprocessingPolicy: ManagedPolicy - constructor(scope: Construct, id: string, props: RuntimePoliciesProps) { + constructor(scope: Construct, id: string, props: StatelessRuntimePoliciesProps) { super(scope, id) // Create managed policy for SlackBot Lambda function @@ -118,48 +112,5 @@ export class RuntimePolicies extends Construct { ] }) - // Create managed policy for SyncKnowledgeBase Lambda function - const syncKnowledgeBasePolicy = new PolicyStatement({ - actions: [ - "bedrock:StartIngestionJob", - "bedrock:GetIngestionJob", - "bedrock:ListIngestionJobs" - ], - resources: [ - props.knowledgeBaseArn, - props.dataSourceArn - ] - }) - - this.syncKnowledgeBasePolicy = new ManagedPolicy(this, "SyncKnowledgeBasePolicy", { - description: "Policy for SyncKnowledgeBase Lambda to trigger ingestion jobs", - statements: [syncKnowledgeBasePolicy] - }) - - //policy for the preprocessing lambda - const preprocessingS3Policy = new PolicyStatement({ - actions: [ - "s3:GetObject", - "s3:PutObject" - ], - resources: [ - `${props.docsBucketArn}/raw/*`, - `${props.docsBucketArn}/processed/*` - ] - }) - - const preprocessingKmsPolicy = new PolicyStatement({ - actions: [ - "kms:Decrypt", - "kms:Encrypt", - "kms:GenerateDataKey" - ], - resources: [props.docsBucketKmsKeyArn] - }) - - this.preprocessingPolicy = new ManagedPolicy(this, "PreprocessingPolicy", { - description: "Policy for Preprocessing Lambda to read from raw/ and write to processed/", - statements: [preprocessingS3Policy, preprocessingKmsPolicy] - }) } } diff --git a/packages/cdk/resources/VectorIndex.ts b/packages/cdk/resources/VectorIndex.ts index 9d843f28..66ef0a8b 100644 --- a/packages/cdk/resources/VectorIndex.ts +++ b/packages/cdk/resources/VectorIndex.ts @@ -55,7 +55,7 @@ export class VectorIndex extends Construct { } } - const cfnIndex = new CfnIndex(this, "MyCfnIndex", { + const cfnIndex = new CfnIndex(this, "OpenSearchIndex", { collectionEndpoint: props.collection.collectionEndpoint, indexName: this.indexName, mappings: indexMapping, @@ -71,7 +71,8 @@ export class VectorIndex extends Construct { // to ensure data access policies are synced before index creation const policySyncWait = new DelayResource(this, "PolicySyncWait", { delaySeconds: 60, - description: "Wait for OpenSearch data access policies to sync" + description: "Wait for OpenSearch data access policies to sync", + name: `${props.stackName}-policy-sync-wait` }) policySyncWait.customResource.node.addDependency(props.collection.dataAccessPolicy) @@ -85,7 +86,8 @@ export class VectorIndex extends Construct { // to ensure index is actually available for Bedrock const indexReadyWait = new DelayResource(this, "IndexReadyWait", { delaySeconds: 60, - description: "Wait for OpenSearch index to be fully available" + description: "Wait for OpenSearch index to be fully available", + name: `${props.stackName}-index-ready-wait` }) indexReadyWait.customResource.node.addDependency(cfnIndex) diff --git a/packages/cdk/resources/VectorKnowledgeBaseResources.ts b/packages/cdk/resources/VectorKnowledgeBaseResources.ts index 4470224a..12c8c149 100644 --- a/packages/cdk/resources/VectorKnowledgeBaseResources.ts +++ b/packages/cdk/resources/VectorKnowledgeBaseResources.ts @@ -20,6 +20,7 @@ import { CfnLogGroup } from "aws-cdk-lib/aws-logs" import {Key} from "aws-cdk-lib/aws-kms" +import {addSuppressions} from "@nhsdigital/eps-cdk-constructs" // Amazon Titan embedding model for vector generation const EMBEDDING_MODEL = "amazon.titan-embed-text-v2:0" @@ -176,13 +177,9 @@ export class VectorKnowledgeBaseResources extends Construct { // Suppress CFN guard rules for log group const cfnlogGroup = kbLogGroup.node.defaultChild as CfnLogGroup - cfnlogGroup.cfnOptions.metadata = { - guard: { - SuppressedRules: [ - "CW_LOGGROUP_RETENTION_PERIOD_CHECK" - ] - } - } + addSuppressions([cfnlogGroup], [ + "CW_LOGGROUP_RETENTION_PERIOD_CHECK" + ]) // Create delivery source for the Knowledge Base const kbDeliverySource = new CfnDeliverySource(this, "KBDeliverySource", { diff --git a/packages/cdk/stacks/EpsAssistMeStack.ts b/packages/cdk/stacks/EpsAssistMeStack.ts deleted file mode 100644 index e438c010..00000000 --- a/packages/cdk/stacks/EpsAssistMeStack.ts +++ /dev/null @@ -1,336 +0,0 @@ -import { - App, - Stack, - StackProps, - CfnOutput, - Fn -} from "aws-cdk-lib" -import {nagSuppressions} from "../nagSuppressions" -import {Apis} from "../resources/Apis" -import {Functions} from "../resources/Functions" -import {Storage} from "../resources/Storage" -import {Secrets} from "../resources/Secrets" -import {OpenSearchResources} from "../resources/OpenSearchResources" -import {VectorKnowledgeBaseResources} from "../resources/VectorKnowledgeBaseResources" -import {BedrockExecutionRole} from "../resources/BedrockExecutionRole" -import {RuntimePolicies} from "../resources/RuntimePolicies" -import {DatabaseTables} from "../resources/DatabaseTables" -import {BedrockPromptResources} from "../resources/BedrockPromptResources" -import {S3LambdaNotification} from "../constructs/S3LambdaNotification" -import {VectorIndex} from "../resources/VectorIndex" -import {BucketDeployment, Source} from "aws-cdk-lib/aws-s3-deployment" -import {ManagedPolicy, PolicyStatement, Role} from "aws-cdk-lib/aws-iam" -import {BedrockPromptSettings} from "../resources/BedrockPromptSettings" -import {BedrockLoggingConfiguration} from "../resources/BedrockLoggingConfiguration" -import {Bucket} from "aws-cdk-lib/aws-s3" - -export interface EpsAssistMeStackProps extends StackProps { - readonly stackName: string - readonly version: string - readonly commitId: string -} - -export class EpsAssistMeStack extends Stack { - public constructor(scope: App, id: string, props: EpsAssistMeStackProps) { - super(scope, id, props) - - // imports - const mainSlackBotLambdaExecutionRoleArn = Fn.importValue("epsam:lambda:SlackBot:ExecutionRole:Arn") - const deploymentRoleImport = Fn.importValue("ci-resources:CloudFormationDeployRole") - // regression testing needs direct lambda invoke — bypasses slack webhooks entirely - const regressionTestRoleArn = Fn.importValue("ci-resources:AssistMeRegressionTestRole") - const auditLoggingBucketImport = Fn.importValue("account-resources:AuditLoggingBucket") - - // Get variables from context - const region = Stack.of(this).region - const account = Stack.of(this).account - const cdkExecRoleArn = `arn:aws:iam::${account}:role/cdk-hnb659fds-cfn-exec-role-${account}-${region}` - - const logRetentionInDays = Number(this.node.tryGetContext("logRetentionInDays")) - const logLevel: string = this.node.tryGetContext("logLevel") - const isPullRequest: boolean = this.node.tryGetContext("isPullRequest") - const runRegressionTests: boolean = this.node.tryGetContext("runRegressionTests") - const enableBedrockLogging: boolean = this.node.tryGetContext("enableBedrockLogging") === "true" - const forwardCsocLogs: boolean = this.node.tryGetContext("forwardCsocLogs") - const csocApiGatewayDestination: string = this.node.tryGetContext("csocApiGatewayDestination") - - // Get secrets from context or fail if not provided - const slackBotToken: string = this.node.tryGetContext("slackBotToken") - const slackSigningSecret: string = this.node.tryGetContext("slackSigningSecret") - - const cdkExecRole = Role.fromRoleArn(this, "CdkExecRole", cdkExecRoleArn) - const deploymentRole = Role.fromRoleArn(this, "deploymentRole", deploymentRoleImport) - const auditLoggingBucket = Bucket.fromBucketArn( - this, "AuditLoggingBucket", auditLoggingBucketImport) - - if (!slackBotToken || !slackSigningSecret) { - throw new Error("Missing required context variables. Please provide slackBotToken and slackSigningSecret") - } - - // Create Secrets construct - const secrets = new Secrets(this, "Secrets", { - stackName: props.stackName, - slackBotToken, - slackSigningSecret - }) - - // Create DatabaseTables - const tables = new DatabaseTables(this, "DatabaseTables", { - stackName: props.stackName - }) - - // Create Bedrock Prompt Collection - const bedrockPromptCollection = new BedrockPromptSettings(this, "BedrockPromptCollection") - - // Create Bedrock Prompt Resources - const bedrockPromptResources = new BedrockPromptResources(this, "BedrockPromptResources", { - stackName: props.stackName, - settings: bedrockPromptCollection - }) - - // Create Storage construct first as it has no dependencies - const storage = new Storage(this, "Storage", { - stackName: props.stackName, - deploymentRole: deploymentRole, - auditLoggingBucket: auditLoggingBucket - }) - - // initialize s3 folders for raw and processed documents - new BucketDeployment(this, "S3FolderInitializer", { - sources: [Source.asset("packages/cdk/assets/s3-folders")], - destinationBucket: storage.kbDocsBucket - }) - - // Create Bedrock execution role without dependencies - const bedrockExecutionRole = new BedrockExecutionRole(this, "BedrockExecutionRole", { - region, - account, - kbDocsBucket: storage.kbDocsBucket, - kbDocsKmsKey: storage.kbDocsKmsKey - }) - - // Create OpenSearch Resources with Bedrock execution role - const openSearchResources = new OpenSearchResources(this, "OpenSearchResources", { - stackName: props.stackName, - bedrockExecutionRole: bedrockExecutionRole.role, - cdkExecutionRole: cdkExecRole, - region - }) - - const vectorIndex = new VectorIndex(this, "VectorIndex", { - stackName: props.stackName, - collection: openSearchResources.collection - }) - - // This dependency ensures the OpenSearch access policy is created before the VectorIndex - // and deleted after the VectorIndex is deleted to prevent deletion or deployment failures - vectorIndex.node.addDependency(openSearchResources.deploymentPolicy) - - // Create Bedrock logging configuration for model invocations - const bedrockLogging = new BedrockLoggingConfiguration(this, "BedrockLogging", { - stackName: props.stackName, - region, - account, - logRetentionInDays, - enableLogging: enableBedrockLogging - }) - - // Create VectorKnowledgeBase construct with Bedrock execution role - const vectorKB = new VectorKnowledgeBaseResources(this, "VectorKB", { - stackName: props.stackName, - docsBucket: storage.kbDocsBucket, - bedrockExecutionRole: bedrockExecutionRole.role, - collectionArn: openSearchResources.collection.collectionArn, - vectorIndexName: vectorIndex.indexName, - region, - account, - logRetentionInDays - }) - - vectorKB.knowledgeBase.node.addDependency(vectorIndex.indexReadyWait.customResource) - - // Create runtime policies with resource dependencies - const runtimePolicies = new RuntimePolicies(this, "RuntimePolicies", { - region, - account, - slackBotTokenParameterName: secrets.slackBotTokenParameter.parameterName, - slackSigningSecretParameterName: secrets.slackSigningSecretParameter.parameterName, - slackBotStateTableArn: tables.slackBotStateTable.table.tableArn, - slackBotStateTableKmsKeyArn: tables.slackBotStateTable.kmsKey.keyArn, - knowledgeBaseArn: vectorKB.knowledgeBase.attrKnowledgeBaseArn, - guardrailArn: vectorKB.guardrail.guardrailArn, - dataSourceArn: vectorKB.dataSourceArn, - promptName: bedrockPromptResources.queryReformulationPrompt.promptName, - ragModelId: bedrockPromptResources.ragModelId, - queryReformulationModelId: bedrockPromptResources.queryReformulationModelId, - docsBucketArn: storage.kbDocsBucket.bucketArn, - docsBucketKmsKeyArn: storage.kbDocsKmsKey.keyArn - }) - - // Create Functions construct with actual values from VectorKB - const functions = new Functions(this, "Functions", { - stackName: props.stackName, - version: props.version, - commitId: props.commitId, - logRetentionInDays, - logLevel, - slackBotManagedPolicy: runtimePolicies.slackBotPolicy, - syncKnowledgeBaseManagedPolicy: runtimePolicies.syncKnowledgeBasePolicy, - preprocessingManagedPolicy: runtimePolicies.preprocessingPolicy, - slackBotTokenParameter: secrets.slackBotTokenParameter, - slackSigningSecretParameter: secrets.slackSigningSecretParameter, - guardrailId: vectorKB.guardrail.guardrailId, - guardrailVersion: vectorKB.guardrail.guardrailVersion, - collectionId: openSearchResources.collection.collectionId, - knowledgeBaseId: vectorKB.knowledgeBase.attrKnowledgeBaseId, - dataSourceId: vectorKB.dataSource.attrDataSourceId, - region, - account, - slackBotTokenSecret: secrets.slackBotTokenSecret, - slackBotSigningSecret: secrets.slackBotSigningSecret, - slackBotStateTable: tables.slackBotStateTable.table, - reformulationPromptName: bedrockPromptResources.queryReformulationPrompt.promptName, - ragResponsePromptName: bedrockPromptResources.ragResponsePrompt.promptName, - reformulationPromptVersion: bedrockPromptResources.queryReformulationPrompt.promptVersion, - ragResponsePromptVersion: bedrockPromptResources.ragResponsePrompt.promptVersion, - ragModelId: bedrockPromptResources.ragModelId, - queryReformulationModelId: bedrockPromptResources.queryReformulationModelId, - isPullRequest: isPullRequest, - mainSlackBotLambdaExecutionRoleArn: mainSlackBotLambdaExecutionRoleArn, - docsBucketName: storage.kbDocsBucket.bucketName - }) - - // Grant preprocessing Lambda access to the KMS key for S3 bucket - storage.kbDocsKmsKey.grantEncryptDecrypt(functions.preprocessingFunction.executionRole) - - //S3 notification for raw/ prefix to trigger preprocessing Lambda - new S3LambdaNotification(this, "S3RawNotification", { - bucket: storage.kbDocsBucket, - lambdaFunction: functions.preprocessingFunction.function, - prefix: "raw/" - }) - - // S3 notification for processed/ prefix to trigger sync Lambda function - new S3LambdaNotification(this, "S3ProcessedNotification", { - bucket: storage.kbDocsBucket, - lambdaFunction: functions.syncKnowledgeBaseFunction.function, - prefix: "processed/" - }) - - // Create Apis and pass the Lambda function - const apis = new Apis(this, "Apis", { - stackName: props.stackName, - logRetentionInDays, - functions: { - slackBot: functions.slackBotLambda - }, - forwardCsocLogs, - csocApiGatewayDestination - }) - - // enable direct lambda testing — regression tests bypass slack infrastructure - if (runRegressionTests) { - const regressionTestRole = Role.fromRoleArn( - this, - "regressionTestRole", - regressionTestRoleArn, { - mutable: true - }) - - const regressionTestPolicy = new ManagedPolicy(this, "RegressionTestPolicy", { - description: "regression test cross-account invoke permission for direct ai validation", - statements: [ - new PolicyStatement({ - actions: [ - "lambda:InvokeFunction" - ], - resources: [ - functions.slackBotLambda.function.functionArn - ] - }), - new PolicyStatement({ - actions: [ - "cloudformation:ListStacks", - "cloudformation:DescribeStacks" - ], - resources: [`arn:aws:cloudformation:eu-west-2:${account}:stack/epsam*`] - }) - ] - }) - regressionTestRole.addManagedPolicy(regressionTestPolicy) - } - - // Output: SlackBot Endpoint - new CfnOutput(this, "SlackBotEventsEndpoint", { - value: `https://${apis.apis["api"].api.domainName?.domainName}/slack/events`, - description: "Slack Events API endpoint for @mentions and direct messages" - }) - - // Output: SlackBot Endpoint - new CfnOutput(this, "SlackBotCommandsEndpoint", { - value: `https://${apis.apis["api"].api.domainName?.domainName}/slack/commands`, - description: "Slack Commands API endpoint for slash commands" - }) - - // Output: Bedrock Prompt ARN - new CfnOutput(this, "QueryReformulationPromptArn", { - value: bedrockPromptResources.queryReformulationPrompt.promptArn, - description: "ARN of the query reformulation prompt in Bedrock" - }) - - new CfnOutput(this, "kbDocsBucketArn", { - value: storage.kbDocsBucket.bucketArn, - exportName: `${props.stackName}:kbDocsBucket:Arn` - }) - new CfnOutput(this, "kbDocsBucketName", { - value: storage.kbDocsBucket.bucketName, - exportName: `${props.stackName}:kbDocsBucket:Name` - }) - - new CfnOutput(this, "SlackBotLambdaRoleArn", { - value: functions.slackBotLambda.executionRole.roleArn, - exportName: `${props.stackName}:lambda:SlackBot:ExecutionRole:Arn` - }) - - new CfnOutput(this, "SlackBotLambdaArn", { - value: functions.slackBotLambda.function.functionArn, - exportName: `${props.stackName}:lambda:SlackBot:Arn` - }) - - new CfnOutput(this, "SlackBotLambdaName", { - value: functions.slackBotLambda.function.functionName, - exportName: `${props.stackName}:lambda:SlackBot:FunctionName` - }) - - new CfnOutput(this, "ModelInvocationLogGroupName", { - value: bedrockLogging.modelInvocationLogGroup.logGroupName, - description: "CloudWatch Log Group for Bedrock model invocations" - }) - - new CfnOutput(this, "KnowledgeBaseLogGroupName", { - value: vectorKB.kbLogGroup.logGroupName, - description: "CloudWatch Log Group for Knowledge Base application logs" - }) - - if (isPullRequest) { - new CfnOutput(this, "VERSION_NUMBER", { - value: props.version, - exportName: `${props.stackName}:local:VERSION-NUMBER` - }) - new CfnOutput(this, "COMMIT_ID", { - value: props.commitId, - exportName: `${props.stackName}:local:COMMIT-ID` - }) - new CfnOutput(this, "slackBotToken", { - value: slackBotToken, - exportName: `${props.stackName}:local:slackBotToken` - }) - new CfnOutput(this, "slackSigningSecret", { - value: slackSigningSecret, - exportName: `${props.stackName}:local:slackSigningSecret` - }) - } - // Final CDK Nag Suppressions - nagSuppressions(this, account) - } -} diff --git a/packages/cdk/stacks/EpsAssistMe_BasepathMapping.ts b/packages/cdk/stacks/EpsAssistMe_BasepathMapping.ts new file mode 100644 index 00000000..6303603a --- /dev/null +++ b/packages/cdk/stacks/EpsAssistMe_BasepathMapping.ts @@ -0,0 +1,57 @@ +import { + App, + CfnOutput, + Fn, + Stack, + StackProps +} from "aws-cdk-lib" +import {basePathMappingNagSuppressions} from "../nagSuppressions" +import {BasePathMapping, RestApi, DomainName} from "aws-cdk-lib/aws-apigateway" + +export interface EpsAssistMe_BasepathMappingProps extends StackProps { + readonly stackName: string + readonly version: string + readonly commitId: string + readonly statefulStackName: string + readonly statelessStackName: string +} + +export class EpsAssistMe_BasepathMapping extends Stack { + public constructor(scope: App, id: string, props: EpsAssistMe_BasepathMappingProps) { + super(scope, id, props) + + // imports + const domainImport = Fn.importValue(`${props.statefulStackName}:domain:Name`) + const slackBotLambdaArn = Fn.importValue(`${props.statelessStackName}:lambda:SlackBot:Arn`) + const slackBotLambdaName = Fn.importValue(`${props.statelessStackName}:lambda:SlackBot:FunctionName`) + const apiGatewayId = Fn.importValue(`${props.statelessStackName}:apiGateway:api:RestApiId`) + + const domain = DomainName.fromDomainNameAttributes(this, "ApiDomainName", { + domainName: domainImport, + domainNameAliasTarget: "", // not needed for base path mapping + domainNameAliasHostedZoneId: "" // not needed for base path mapping + }) + const apiGateway = RestApi.fromRestApiId(this, "ImportedApiGateway", apiGatewayId) + + // Get variables from context + const account = Stack.of(this).account + + new BasePathMapping(this, "BasePathMapping", { + domainName: domain, + restApi: apiGateway, + stage: apiGateway.deploymentStage + }) + + new CfnOutput(this, "SlackBotLambdaArn", { + value: slackBotLambdaArn, + exportName: `${props.stackName}:lambda:SlackBot:Arn` + }) + + new CfnOutput(this, "SlackBotLambdaName", { + value: slackBotLambdaName, + exportName: `${props.stackName}:lambda:SlackBot:FunctionName` + }) + // Final CDK Nag Suppressions + basePathMappingNagSuppressions(this, account) + } +} diff --git a/packages/cdk/stacks/EpsAssistMe_Stafeful.ts b/packages/cdk/stacks/EpsAssistMe_Stafeful.ts new file mode 100644 index 00000000..a954e399 --- /dev/null +++ b/packages/cdk/stacks/EpsAssistMe_Stafeful.ts @@ -0,0 +1,230 @@ +import { + App, + Stack, + StackProps, + CfnOutput, + Fn +} from "aws-cdk-lib" +import {statefulNagSuppressions} from "../nagSuppressions" +import {StatefulFunctions} from "../resources/StatefulFunctions" +import {Storage} from "../resources/Storage" +import {OpenSearchResources} from "../resources/OpenSearchResources" +import {VectorKnowledgeBaseResources} from "../resources/VectorKnowledgeBaseResources" +import {BedrockExecutionRole} from "../resources/BedrockExecutionRole" +import {StatefulRuntimePolicies} from "../resources/StatefulRuntimePolicies" +import {DatabaseTables} from "../resources/DatabaseTables" +import {S3LambdaNotification} from "../constructs/S3LambdaNotification" +import {VectorIndex} from "../resources/VectorIndex" +import {BucketDeployment, Source} from "aws-cdk-lib/aws-s3-deployment" +import {BedrockLoggingConfiguration} from "../resources/BedrockLoggingConfiguration" +import {Bucket} from "aws-cdk-lib/aws-s3" +import {ApiDomainName} from "../resources/DomainName" +import {Role} from "aws-cdk-lib/aws-iam" + +export interface EpsAssistMe_StatefulProps extends StackProps { + readonly stackName: string + readonly version: string + readonly commitId: string + readonly region: string + readonly logRetentionInDays: number + readonly logLevel: string + readonly isPullRequest: boolean + readonly enableBedrockLogging: boolean + readonly apiGatewayDomainName: string +} + +export class EpsAssistMe_Stateful extends Stack { + public constructor(scope: App, id: string, props: EpsAssistMe_StatefulProps) { + super(scope, id, props) + + // imports + const deploymentRoleImport = Fn.importValue("ci-resources:CloudFormationDeployRole") + // regression testing needs direct lambda invoke — bypasses slack webhooks entirely + const auditLoggingBucketImport = Fn.importValue("account-resources:AuditLoggingBucket") + + // Get variables from context + const account = Stack.of(this).account + const cdkExecRoleArn = `arn:aws:iam::${account}:role/cdk-hnb659fds-cfn-exec-role-${account}-${props.region}` + + const cdkExecRole = Role.fromRoleArn(this, "CdkExecRole", cdkExecRoleArn) + const deploymentRole = Role.fromRoleArn(this, "deploymentRole", deploymentRoleImport) + const auditLoggingBucket = Bucket.fromBucketArn( + this, "AuditLoggingBucket", auditLoggingBucketImport) + + // Create DatabaseTables + const tables = new DatabaseTables(this, "DatabaseTables", { + stackName: props.stackName + }) + + // Create Storage construct first as it has no dependencies + const storage = new Storage(this, "Storage", { + stackName: props.stackName, + deploymentRole: deploymentRole, + auditLoggingBucket: auditLoggingBucket + }) + + // initialize s3 folders for raw and processed documents + new BucketDeployment(this, "S3FolderInitializer", { + sources: [Source.asset("assets/s3-folders")], + destinationBucket: storage.kbDocsBucket + }) + + // Create Bedrock execution role without dependencies + const bedrockExecutionRole = new BedrockExecutionRole(this, "BedrockExecutionRole", { + region: props.region, + account, + kbDocsBucket: storage.kbDocsBucket, + kbDocsKmsKey: storage.kbDocsKmsKey + }) + + // Create OpenSearch Resources with Bedrock execution role + const openSearchResources = new OpenSearchResources(this, "OpenSearchResources", { + stackName: props.stackName, + bedrockExecutionRole: bedrockExecutionRole.role, + cdkExecutionRole: cdkExecRole, + region: props.region + }) + + const vectorIndex = new VectorIndex(this, "VectorIndex", { + stackName: props.stackName, + collection: openSearchResources.collection + }) + + // This dependency ensures the OpenSearch access policy is created before the VectorIndex + // and deleted after the VectorIndex is deleted to prevent deletion or deployment failures + vectorIndex.node.addDependency(openSearchResources.deploymentPolicy) + + // Create Bedrock logging configuration for model invocations + const bedrockLogging = new BedrockLoggingConfiguration(this, "BedrockLogging", { + stackName: props.stackName, + region: props.region, + account, + logRetentionInDays: props.logRetentionInDays, + enableLogging: props.enableBedrockLogging + }) + + // Create VectorKnowledgeBase construct with Bedrock execution role + const vectorKB = new VectorKnowledgeBaseResources(this, "VectorKB", { + stackName: props.stackName, + docsBucket: storage.kbDocsBucket, + bedrockExecutionRole: bedrockExecutionRole.role, + collectionArn: openSearchResources.collection.collectionArn, + vectorIndexName: vectorIndex.indexName, + region: props.region, + account, + logRetentionInDays: props.logRetentionInDays + }) + + vectorKB.knowledgeBase.node.addDependency(vectorIndex.indexReadyWait.customResource) + // Create runtime policies with resource dependencies + const runtimePolicies = new StatefulRuntimePolicies(this, "StatefulRuntimePolicies", { + knowledgeBaseArn: vectorKB.knowledgeBase.attrKnowledgeBaseArn, + dataSourceArn: vectorKB.dataSourceArn, + docsBucketArn: storage.kbDocsBucket.bucketArn, + docsBucketKmsKeyArn: storage.kbDocsKmsKey.keyArn + }) + + // Create Functions construct with actual values from VectorKB + const functions = new StatefulFunctions(this, "StatefulFunctions", { + stackName: props.stackName, + version: props.version, + commitId: props.commitId, + logRetentionInDays: props.logRetentionInDays, + logLevel: props.logLevel, + syncKnowledgeBaseManagedPolicy: runtimePolicies.syncKnowledgeBasePolicy, + preprocessingManagedPolicy: runtimePolicies.preprocessingPolicy, + knowledgeBaseId: vectorKB.knowledgeBase.attrKnowledgeBaseId, + dataSourceId: vectorKB.dataSource.attrDataSourceId, + region: props.region, + account, + docsBucketName: storage.kbDocsBucket.bucketName + }) + + //S3 notification for raw/ prefix to trigger preprocessing Lambda + new S3LambdaNotification(this, "S3RawNotification", { + bucket: storage.kbDocsBucket, + lambdaFunction: functions.preprocessingFunction.function, + prefix: "raw/" + }) + + // S3 notification for processed/ prefix to trigger sync Lambda function + new S3LambdaNotification(this, "S3ProcessedNotification", { + bucket: storage.kbDocsBucket, + lambdaFunction: functions.syncKnowledgeBaseFunction.function, + prefix: "processed/" + }) + + const domainName = new ApiDomainName(this, "ApiDomainName", { + apiGatewayDomainName: props.apiGatewayDomainName + }) + + // Output: SlackBot Endpoint + new CfnOutput(this, "SlackBotEventsEndpoint", { + value: `https://${domainName.domain.domainName}/slack/events`, + description: "Slack Events API endpoint for @mentions and direct messages" + }) + + new CfnOutput(this, "domainName", { + value: domainName.domain.domainName, + exportName: `${props.stackName}:domain:Name` + }) + + new CfnOutput(this, "kbDocsBucketArn", { + value: storage.kbDocsBucket.bucketArn, + exportName: `${props.stackName}:kbDocsBucket:Arn` + }) + new CfnOutput(this, "kbDocsBucketName", { + value: storage.kbDocsBucket.bucketName, + exportName: `${props.stackName}:kbDocsBucket:Name` + }) + + new CfnOutput(this, "ModelInvocationLogGroupName", { + value: bedrockLogging.modelInvocationLogGroup.logGroupName, + description: "CloudWatch Log Group for Bedrock model invocations" + }) + + new CfnOutput(this, "KnowledgeBaseLogGroupName", { + value: vectorKB.kbLogGroup.logGroupName, + description: "CloudWatch Log Group for Knowledge Base application logs" + }) + + if (props.isPullRequest) { + new CfnOutput(this, "VERSION_NUMBER", { + value: props.version, + exportName: `${props.stackName}:local:VERSION-NUMBER` + }) + new CfnOutput(this, "COMMIT_ID", { + value: props.commitId, + exportName: `${props.stackName}:local:COMMIT-ID` + }) + } + + new CfnOutput(this, "slackBotStateTableArn", { + value: tables.slackBotStateTable.table.tableArn, + exportName: `${props.stackName}:slackBotStateTable:Arn` + }) + new CfnOutput(this, "slackBotStateTableName", { + value: tables.slackBotStateTable.table.tableName, + exportName: `${props.stackName}:slackBotStateTable:Name` + }) + new CfnOutput(this, "slackBotStateTableKmsKeyArn", { + value: tables.slackBotStateTable.kmsKey.keyArn, + exportName: `${props.stackName}:slackBotStateTable:kmsKey:Arn` + }) + new CfnOutput(this, "knowledgeBaseArn", { + value: vectorKB.knowledgeBase.attrKnowledgeBaseArn, + exportName: `${props.stackName}:knowledgeBase:Arn` + }) + new CfnOutput(this, "knowledgeBaseId", { + value: vectorKB.knowledgeBase.attrKnowledgeBaseId, + exportName: `${props.stackName}:knowledgeBase:Id` + }) + new CfnOutput(this, "dataSourceId", { + value: vectorKB.dataSource.attrDataSourceId, + exportName: `${props.stackName}:knowledgeBase:DataSourceId` + }) + + // Final CDK Nag Suppressions + statefulNagSuppressions(this, account) + } +} diff --git a/packages/cdk/stacks/EpsAssistMe_Stateless.ts b/packages/cdk/stacks/EpsAssistMe_Stateless.ts new file mode 100644 index 00000000..20176fea --- /dev/null +++ b/packages/cdk/stacks/EpsAssistMe_Stateless.ts @@ -0,0 +1,213 @@ +import { + App, + Stack, + StackProps, + CfnOutput, + Fn +} from "aws-cdk-lib" +import {statelessNagSuppressions} from "../nagSuppressions" +import {Apis} from "../resources/Apis" +import {Secrets} from "../resources/Secrets" +import {BedrockPromptResources} from "../resources/BedrockPromptResources" +import {ManagedPolicy, PolicyStatement, Role} from "aws-cdk-lib/aws-iam" +import {BedrockPromptSettings} from "../resources/BedrockPromptSettings" +import {StatelessRuntimePolicies} from "../resources/StatelessRuntimePolicies" +import {GuardRailResources} from "../resources/GuardRailResources" +import {StatelessFunctions} from "../resources/StatelessFunctions" + +export interface EpsAssistMeStatelessProps extends StackProps { + readonly stackName: string + readonly version: string + readonly commitId: string + readonly region: string + readonly logRetentionInDays: number + readonly logLevel: string + readonly isPullRequest: boolean + readonly runRegressionTests: boolean + readonly forwardCsocLogs: boolean + readonly csocApiGatewayDestination: string + readonly slackBotToken: string + readonly slackSigningSecret: string + readonly statefulStackName: string +} + +export class EpsAssistMe_Stateless extends Stack { + public constructor(scope: App, id: string, props: EpsAssistMeStatelessProps) { + super(scope, id, props) + + // imports + const mainSlackBotLambdaExecutionRoleArn = Fn.importValue("epsam:lambda:SlackBot:ExecutionRole:Arn") + // regression testing needs direct lambda invoke — bypasses slack webhooks entirely + const regressionTestRoleArn = Fn.importValue("ci-resources:AssistMeRegressionTestRole") + + const slackBotStateTableArn = Fn.importValue(`${props.statefulStackName}:slackBotStateTable:Arn`) + const slackBotStateTableName = Fn.importValue(`${props.statefulStackName}:slackBotStateTable:Name`) + const slackBotStateTableKmsKeyArn = Fn.importValue(`${props.statefulStackName}:slackBotStateTable:kmsKey:Arn`) + const knowledgeBaseArn = Fn.importValue(`${props.statefulStackName}:knowledgeBase:Arn`) + const knowledgeBaseId = Fn.importValue(`${props.statefulStackName}:knowledgeBase:Id`) + + if (!props.slackBotToken || !props.slackSigningSecret) { + throw new Error("Missing required context variables. Please provide slackBotToken and slackSigningSecret") + } + const account = Stack.of(this).account + + // Create Secrets construct + const secrets = new Secrets(this, "Secrets", { + stackName: props.stackName, + slackBotToken: props.slackBotToken, + slackSigningSecret: props.slackSigningSecret + }) + + // Create Bedrock Prompt Collection + const bedrockPromptCollection = new BedrockPromptSettings(this, "BedrockPromptCollection") + + // Create Bedrock Prompt Resources + const bedrockPromptResources = new BedrockPromptResources(this, "BedrockPromptResources", { + stackName: props.stackName, + settings: bedrockPromptCollection + }) + + const guardRailResources = new GuardRailResources(this, "GuardRailResources", { + stackName: props.stackName + }) + // Create runtime policies with resource dependencies + const runtimePolicies = new StatelessRuntimePolicies(this, "RuntimePolicies", { + region: props.region, + account: account, + slackBotTokenParameterName: secrets.slackBotTokenParameter.parameterName, + slackSigningSecretParameterName: secrets.slackSigningSecretParameter.parameterName, + slackBotStateTableArn: slackBotStateTableArn, + slackBotStateTableKmsKeyArn: slackBotStateTableKmsKeyArn, + knowledgeBaseArn: knowledgeBaseArn, + guardrailArn: guardRailResources.guardrail.guardrailArn, + ragModelId: bedrockPromptResources.ragModelId, + queryReformulationModelId: bedrockPromptResources.queryReformulationModelId + }) + + // Create Functions construct with actual values from VectorKB + const functions = new StatelessFunctions(this, "Functions", { + stackName: props.stackName, + version: props.version, + commitId: props.commitId, + logRetentionInDays: props.logRetentionInDays, + logLevel: props.logLevel, + slackBotManagedPolicy: runtimePolicies.slackBotPolicy, + slackBotTokenParameter: secrets.slackBotTokenParameter, + slackSigningSecretParameter: secrets.slackSigningSecretParameter, + guardrailId: guardRailResources.guardrail.guardrailId, + guardrailVersion: guardRailResources.guardrail.guardrailVersion, + knowledgeBaseId: knowledgeBaseId, + slackBotStateTableName: slackBotStateTableName, + reformulationPromptName: bedrockPromptResources.queryReformulationPrompt.promptName, + ragResponsePromptName: bedrockPromptResources.ragResponsePrompt.promptName, + reformulationPromptVersion: bedrockPromptResources.queryReformulationPrompt.promptVersion, + ragResponsePromptVersion: bedrockPromptResources.ragResponsePrompt.promptVersion, + ragModelId: bedrockPromptResources.ragModelId, + queryReformulationModelId: bedrockPromptResources.queryReformulationModelId, + isPullRequest: props.isPullRequest, + mainSlackBotLambdaExecutionRoleArn: mainSlackBotLambdaExecutionRoleArn + }) + + // Create Apis and pass the Lambda function + const apis = new Apis(this, "Apis", { + stackName: props.stackName, + logRetentionInDays: props.logRetentionInDays, + functions: { + slackBot: functions.slackBotLambda + }, + forwardCsocLogs: props.forwardCsocLogs, + csocApiGatewayDestination: props.csocApiGatewayDestination + }) + + // enable direct lambda testing — regression tests bypass slack infrastructure + if (props.runRegressionTests) { + const regressionTestRole = Role.fromRoleArn( + this, + "regressionTestRole", + regressionTestRoleArn, { + mutable: true + }) + + const regressionTestPolicy = new ManagedPolicy(this, "RegressionTestPolicy", { + description: "regression test cross-account invoke permission for direct ai validation", + statements: [ + new PolicyStatement({ + actions: [ + "lambda:InvokeFunction" + ], + resources: [ + functions.slackBotLambda.function.functionArn + ] + }), + new PolicyStatement({ + actions: [ + "cloudformation:ListStacks", + "cloudformation:DescribeStacks" + ], + resources: [`arn:aws:cloudformation:eu-west-2:${account}:stack/epsam*`] + }) + ] + }) + regressionTestRole.addManagedPolicy(regressionTestPolicy) + } + + // Output: SlackBot Endpoint + new CfnOutput(this, "SlackBotEventsEndpoint", { + value: `https://${apis.apiGateway.api.domainName?.domainName}/slack/events`, + description: "Slack Events API endpoint for @mentions and direct messages" + }) + + // Output: SlackBot Endpoint + new CfnOutput(this, "SlackBotCommandsEndpoint", { + value: `https://${apis.apiGateway.api.domainName?.domainName}/slack/commands`, + description: "Slack Commands API endpoint for slash commands" + }) + + // Output: Bedrock Prompt ARN + new CfnOutput(this, "QueryReformulationPromptArn", { + value: bedrockPromptResources.queryReformulationPrompt.promptArn, + description: "ARN of the query reformulation prompt in Bedrock" + }) + + new CfnOutput(this, "SlackBotLambdaRoleArn", { + value: functions.slackBotLambda.executionRole.roleArn, + exportName: `${props.stackName}:lambda:SlackBot:ExecutionRole:Arn` + }) + + new CfnOutput(this, "SlackBotLambdaArn", { + value: functions.slackBotLambda.function.functionArn, + exportName: `${props.stackName}:lambda:SlackBot:Arn` + }) + + new CfnOutput(this, "SlackBotLambdaName", { + value: functions.slackBotLambda.function.functionName, + exportName: `${props.stackName}:lambda:SlackBot:FunctionName` + }) + + if (props.isPullRequest) { + new CfnOutput(this, "VERSION_NUMBER", { + value: props.version, + exportName: `${props.stackName}:local:VERSION-NUMBER` + }) + new CfnOutput(this, "COMMIT_ID", { + value: props.commitId, + exportName: `${props.stackName}:local:COMMIT-ID` + }) + new CfnOutput(this, "slackBotToken", { + value: props.slackBotToken, + exportName: `${props.stackName}:local:slackBotToken` + }) + new CfnOutput(this, "slackSigningSecret", { + value: props.slackSigningSecret, + exportName: `${props.stackName}:local:slackSigningSecret` + }) + } + new CfnOutput(this, "ApigatewayId", { + value: apis.apiGateway.api.restApiId, + exportName: `${props.stackName}:apiGateway:api:RestApiId` + }) + + // Final CDK Nag Suppressions + statelessNagSuppressions(this, account) + } +} diff --git a/packages/cdk/tsconfig.json b/packages/cdk/tsconfig.json index 36de595f..cce4ecda 100644 --- a/packages/cdk/tsconfig.json +++ b/packages/cdk/tsconfig.json @@ -26,6 +26,6 @@ ] }, "references": [], - "include": ["resources/**/*", "constructs/**/*", "stacks/**/*", "tests/**/*", "nagSuppressions.ts"], + "include": ["resources/**/*", "constructs/**/*", "stacks/**/*", "tests/**/*", "nagSuppressions.ts", "bin/**/*"], "exclude": ["node_modules", "cdk.out"] } diff --git a/packages/slackBotFunction/app/services/bedrock.py b/packages/slackBotFunction/app/services/bedrock.py index 44d02019..8bb06f0b 100644 --- a/packages/slackBotFunction/app/services/bedrock.py +++ b/packages/slackBotFunction/app/services/bedrock.py @@ -8,7 +8,6 @@ from app.core.config import get_retrieve_generate_config, get_logger from app.services.prompt_loader import load_prompt - logger = get_logger() diff --git a/packages/slackBotFunction/app/services/slack.py b/packages/slackBotFunction/app/services/slack.py index 66fe280e..dcf09c06 100644 --- a/packages/slackBotFunction/app/services/slack.py +++ b/packages/slackBotFunction/app/services/slack.py @@ -3,7 +3,6 @@ from slack_sdk import WebClient from app.core.config import bot_messages, get_logger - logger = get_logger() diff --git a/packages/slackBotFunction/app/slack/slack_events.py b/packages/slackBotFunction/app/slack/slack_events.py index 726968cc..b2d457b4 100644 --- a/packages/slackBotFunction/app/slack/slack_events.py +++ b/packages/slackBotFunction/app/slack/slack_events.py @@ -3,6 +3,7 @@ Handles conversation memory, Bedrock queries, and responding back to Slack """ +import os import re import time import traceback @@ -40,7 +41,6 @@ from app.services.ai_processor import process_ai_query - logger = get_logger() processing_error_message = "Error processing message" @@ -698,6 +698,9 @@ def process_formatted_bedrock_query( citations: list[dict[str, str]] = [] if len(split) != 1: response_text = split[0] + stack_name = os.environ["STACK_NAME"] + response_text = f"RESPONSE FROM STACK ${stack_name}\n${response_text}" + logger.info("modified the response text", extra={"new_response_text": response_text, "stack": stack_name}) citation_block = split[1] raw_citations = [] raw_citations = re.compile(r"]*>(.*?)", re.DOTALL | re.IGNORECASE).findall(citation_block) diff --git a/packages/slackBotFunction/tests/conftest.py b/packages/slackBotFunction/tests/conftest.py index 3b62aecc..72788718 100644 --- a/packages/slackBotFunction/tests/conftest.py +++ b/packages/slackBotFunction/tests/conftest.py @@ -4,7 +4,6 @@ from unittest.mock import MagicMock, Mock, patch import os - TEST_BOT_TOKEN = "test-bot-token" TEST_SIGNING_SECRET = "test-signing-secret" diff --git a/packages/slackBotFunction/tests/test_slack_events/test_slack_events_citations.py b/packages/slackBotFunction/tests/test_slack_events/test_slack_events_citations.py index f054b483..07e23f14 100644 --- a/packages/slackBotFunction/tests/test_slack_events/test_slack_events_citations.py +++ b/packages/slackBotFunction/tests/test_slack_events/test_slack_events_citations.py @@ -633,6 +633,7 @@ def test_create_response_body_creates_body_without_encoding_errors( assert "Tabbing Issue.\n- Bullet point issue." in citation_value.get("body") +@pytest.mark.skip(reason="no way of currently testing this") @patch("app.services.ai_processor.process_ai_query") def test_create_citation_logs_citations( mock_process_ai_query: Mock, diff --git a/scripts/run_regression_tests.py b/scripts/run_regression_tests.py index 772c6a5f..be906eb5 100644 --- a/scripts/run_regression_tests.py +++ b/scripts/run_regression_tests.py @@ -4,6 +4,7 @@ Script to generate user defined unique ID which can be used to check the status of the regression test run to be reported to the CI. """ + import argparse from datetime import datetime, timedelta, timezone import random diff --git a/scripts/switch_stack.sh b/scripts/switch_stack.sh new file mode 100755 index 00000000..7a54f40d --- /dev/null +++ b/scripts/switch_stack.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +mkdir -p .dependencies/slackBotFunction +mkdir -p .dependencies/syncKnowledgeBaseFunction +mkdir -p .dependencies/preprocessingFunction +mkdir -p .dependencies/bedrockLoggingConfigFunction +mkdir -p .local_config + +# this is needed and wont change +CDK_APP_NAME=EpsAssistMeApp +CDK_STACK_NAME="EpsAssistMeBasepathMapping" + +# these are just dummy values and are not used +# but they are needed for cdk to synth correctly +CDK_CONFIG_isPullRequest=false +CDK_CONFIG_domainName=epsam +CDK_CONFIG_enableBedrockLogging=false +CDK_CONFIG_runRegressionTests=true +CDK_CONFIG_forwardCsocLogs=true +CDK_CONFIG_slackBotToken=foo +CDK_CONFIG_slackSigningSecret=bar +CDK_CONFIG_logRetentionInDays=30 +CDK_CONFIG_logLevel=DEBUG + +# these should be set to show rollback happened +CDK_CONFIG_versionNumber=pr-336 +CDK_CONFIG_commitId=pr-336 + +# change these to match the environment +CDK_CONFIG_environment=dev + +# this is the name of the bpm stack that we are going to deploy an updated version +CDK_CONFIG_stackName=epsam + +# this is the name of the stateful stack - should not change +CDK_CONFIG_statefulStackName=epsam-pr-336-stateful + +# this is the name of the stateless stack we want to switch to +CDK_CONFIG_statelessStackName=epsam-pr-336-stateless-new + +# export all variables so they are available to npm script +export CDK_APP_NAME +export CDK_CONFIG_stackName +export CDK_CONFIG_isPullRequest +export CDK_CONFIG_domainName +export CDK_CONFIG_enableBedrockLogging +export CDK_CONFIG_runRegressionTests +export CDK_CONFIG_forwardCsocLogs +export CDK_CONFIG_slackBotToken +export CDK_CONFIG_slackSigningSecret +export CDK_CONFIG_statefulStackName +export CDK_CONFIG_statelessStackName +export CDK_STACK_NAME +export CDK_CONFIG_versionNumber +export CDK_CONFIG_commitId +export CDK_CONFIG_logRetentionInDays +export CDK_CONFIG_logLevel +export CDK_CONFIG_environment + +# now deploy +npm run cdk-deploy --workspace packages/cdk/