diff --git a/.github/workflows/actions/build-lambdas/action.yml b/.github/workflows/actions/build-lambdas/action.yml new file mode 100644 index 00000000..7691bf27 --- /dev/null +++ b/.github/workflows/actions/build-lambdas/action.yml @@ -0,0 +1,36 @@ +name: Build Lambda +description: Builds all Lambda functions + +inputs: + working_directory: + description: The working directory where the code will be built. + required: true + node_version: + description: The node version that will be used. + required: true + +runs: + using: composite + + steps: + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.node_version }} + + - run: npm ci + shell: bash + working-directory: ${{ inputs.working_directory }} + + - run: npm run lint + shell: bash + working-directory: ${{ inputs.working_directory }} + continue-on-error: false + + - run: npm run build + shell: bash + working-directory: ${{ inputs.working_directory }} + + - run: npm run test --if-present + shell: bash + working-directory: ${{ inputs.working_directory }} diff --git a/.github/workflows/actions/deploy-to-aws/action.yml b/.github/workflows/actions/deploy-app/action.yml similarity index 89% rename from .github/workflows/actions/deploy-to-aws/action.yml rename to .github/workflows/actions/deploy-app/action.yml index e1ce40a1..0c26e51b 100644 --- a/.github/workflows/actions/deploy-to-aws/action.yml +++ b/.github/workflows/actions/deploy-app/action.yml @@ -1,5 +1,5 @@ -name: Deploy to AWS -description: Deploy image to AWS Instance +name: Deploy App +description: Deploy web or API image to AWS ECS inputs: environment: @@ -41,11 +41,11 @@ runs: id: vars shell: bash run: | - echo "task_definition_name=${{ inputs.app_name }}-${{ inputs.tier_name }}-task-definition-${{ inputs.environment }}" >> $GITHUB_OUTPUT + echo "task_definition_name=${{ inputs.app_name }}-${{ inputs.tier_name }}-td-${{ inputs.environment }}" >> $GITHUB_OUTPUT echo "container_name=${{ inputs.app_name }}-${{ inputs.tier_name }}-container-${{ inputs.environment }}" >> $GITHUB_OUTPUT - echo "ecs_cluster_name=${{ inputs.app_name }}-ecs-cluster-${{ inputs.environment }}" >> $GITHUB_OUTPUT - echo "ecs_service_name=${{ inputs.app_name }}-ecs-${{ inputs.tier_name }}-service-${{ inputs.environment }}" >> $GITHUB_OUTPUT - echo "full_ecr_repo_url=${{ inputs.aws_account }}.dkr.ecr.${{ inputs.region }}.amazonaws.com/${{ inputs.app_name }}-ecr-repo-${{ inputs.environment }}" >> $GITHUB_OUTPUT + echo "ecs_cluster_name=${{ inputs.app_name }}-app-cluster-${{ inputs.environment }}" >> $GITHUB_OUTPUT + echo "ecs_service_name=${{ inputs.app_name }}-${{ inputs.tier_name }}-ecs-service-${{ inputs.environment }}" >> $GITHUB_OUTPUT + echo "full_ecr_repo_url=${{ inputs.aws_account }}.dkr.ecr.${{ inputs.region }}.amazonaws.com/${{ inputs.app_name }}-app-repo-${{ inputs.environment }}" >> $GITHUB_OUTPUT - name: Log in to the GHCR uses: docker/login-action@v2 @@ -71,7 +71,7 @@ runs: shell: bash run: | IMAGE_TAG=${{ inputs.image_name }}-${{ inputs.short_sha}} - REPOSITORY_NAME=${{ inputs.app_name }}-ecr-repo-${{ inputs.environment }} + REPOSITORY_NAME=${{ inputs.app_name }}-app-repo-${{ inputs.environment }} IMAGE_EXISTS=$(aws ecr describe-images --repository-name $REPOSITORY_NAME --query "imageDetails[?contains(imageTags, '$IMAGE_TAG')]" --output text) diff --git a/.github/workflows/actions/deploy-lambda/action.yml b/.github/workflows/actions/deploy-lambda/action.yml new file mode 100644 index 00000000..7f4b3adc --- /dev/null +++ b/.github/workflows/actions/deploy-lambda/action.yml @@ -0,0 +1,100 @@ +name: Deploy Lambda +description: Deploy image to a Lambda function to AWS + +inputs: + environment: + description: The environment to which the image will be deployed. + required: true + aws_account: + description: The AWS Account ID. + required: true + region: + description: The AWS Region of the AWS Account. + required: true + app_name: + description: The application name. + required: true + resource: + description: The resource path of the lambda function. + required: true + lambda_name: + description: The lambda function name name. + required: true + aws_role_arn: + description: The AWS Role ARN to assume. + required: true + ghcr_token: + description: The token to use to login to the GHCR. + required: true + github_image_repo: + description: The GCHR repo where images are stored. + required: true + image_name: + description: The name of the image to be deployed. + required: true + short_sha: + description: The short SHA used to tag image in GCHR. + required: true + +runs: + using: composite + + steps: + - name: Set reusable variables + id: vars + shell: bash + run: | + echo "full_ecr_repo_url=${{ inputs.aws_account }}.dkr.ecr.${{ inputs.region }}.amazonaws.com/${{ inputs.app_name }}-lambda-repo-${{ inputs.environment }}" >> $GITHUB_OUTPUT + echo "container_name=${{ inputs.app_name }}-${{ inputs.tier_name }}-container-${{ inputs.environment }}" >> $GITHUB_OUTPUT + + + - name: Log in to the GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ inputs.ghcr_token }} + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-skip-session-tagging: true + aws-region: ${{ inputs.region }} + role-to-assume: ${{ inputs.aws_role_arn }} + role-duration-seconds: 1800 + role-session-name: ci-deployment + + - name: Login to Amazon ECR + uses: aws-actions/amazon-ecr-login@v2 + + - name: Check ECR Image exists + id: ecr-check + shell: bash + run: | + IMAGE_TAG=${{ inputs.resource }}${{ inputs.lambda_name }}-${{ inputs.short_sha }} + REPOSITORY_NAME=${{ inputs.app_name }}-lambda-repo-${{ inputs.environment }} + + IMAGE_EXISTS=$(aws ecr describe-images --repository-name $REPOSITORY_NAME --query "imageDetails[?contains(imageTags, '$IMAGE_TAG')]" --output text) + + if [ -z "$IMAGE_EXISTS" ]; then + echo "Image with tag $IMAGE_TAG does not exist." + echo "exists=false" >> $GITHUB_OUTPUT + else + echo "Image with tag $IMAGE_TAG already exists." + echo "exists=true" >> $GITHUB_OUTPUT + fi + + - name: Push if Docker image does not exist + if: steps.ecr-check.outputs.exists == 'false' + shell: bash + run: | + docker pull ${{ inputs.github_image_repo }}/${{ inputs.image_name }}:${{ inputs.short_sha}} + docker tag ${{ inputs.github_image_repo }}/${{ inputs.image_name }}:${{ inputs.short_sha}} ${{ steps.vars.outputs.full_ecr_repo_url }}:${{ inputs.image_name }}-${{ inputs.short_sha }} + docker push ${{ steps.vars.outputs.full_ecr_repo_url }}:${{ inputs.image_name }}-${{ inputs.short_sha }} + + - name: Update Lambda Function + shell: bash + run: | + aws lambda update-function-code \ + --function-name ${{ inputs.app_name }}-${{ inputs.lambda_name }}-lambda-${{ inputs.environment }} \ + --image-uri ${{ env.full_ecr_repo_url }}:${{ inputs.resource }}.${{ inputs.lambda_name }}-${{ inputs.short_sha }} diff --git a/.github/workflows/aws-template-terraform.yml b/.github/workflows/aws-template-terraform.yml index 13e58d72..b63cc51a 100644 --- a/.github/workflows/aws-template-terraform.yml +++ b/.github/workflows/aws-template-terraform.yml @@ -12,9 +12,6 @@ on: CHANGE_FOLDER_NAME: required: true type: string - TEST_BUCKET_NAME: - required: true - type: string APPLY_TF_CODE: required: true default: false @@ -117,7 +114,6 @@ jobs: id: plan env: CONTEXT_FOLDER: ${{ inputs.CONTEXT_FOLDER }} - #TF_VAR_test_s3_bucket_name: ${{ inputs.TEST_BUCKET_NAME }} run: | terraform plan -no-color -input=false -var-file=${{ inputs.ENVIRONMENT_NAME }}.tfvars continue-on-error: true @@ -129,7 +125,6 @@ jobs: if: inputs.APPLY_TF_CODE == true env: CONTEXT_FOLDER: ${{ inputs.CONTEXT_FOLDER }} - #TF_VAR_test_s3_bucket_name: ${{ inputs.TEST_BUCKET_NAME }} run: | terraform apply --auto-approve -input=false -var-file=${{ inputs.ENVIRONMENT_NAME }}.tfvars working-directory: ${{ inputs.CONTEXT_FOLDER }} diff --git a/.github/workflows/build-and-test-lambdas.yml b/.github/workflows/build-and-test-lambdas.yml new file mode 100644 index 00000000..cf43dd89 --- /dev/null +++ b/.github/workflows/build-and-test-lambdas.yml @@ -0,0 +1,28 @@ +name: Build and Test Lambdas + +on: + pull_request: + branches: + - master + paths: + - "aws/**" + + workflow_dispatch: + +env: + WORKING_DIRECTORY: ./aws + NODE_VERSION: 20 + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Building Lambdas codebase + uses: ./.github/workflows/actions/build-lambdas + with: + working_directory: ${{ env.WORKING_DIRECTORY }} + node_version: ${{ env.NODE_VERSION }} diff --git a/.github/workflows/build-infra.yml b/.github/workflows/build-infra.yml index 2eaec906..1a6d36c4 100644 --- a/.github/workflows/build-infra.yml +++ b/.github/workflows/build-infra.yml @@ -22,12 +22,22 @@ on: - prod jobs: + build-initial: + uses: ./.github/workflows/aws-template-terraform.yml + with: + CONTEXT_FOLDER: "./infrastructure/cloud/environments/initial" + CHANGE_FOLDER_NAME: environments/${{ inputs.environment || 'dev' }} + ENVIRONMENT_NAME: ${{ inputs.environment || 'dev' }} + APPLY_TF_CODE: false + secrets: inherit + build: + needs: [build-initial] uses: ./.github/workflows/aws-template-terraform.yml with: CONTEXT_FOLDER: "./infrastructure/cloud/environments/${{ inputs.environment || 'dev' }}" CHANGE_FOLDER_NAME: environments/${{ inputs.environment || 'dev' }} ENVIRONMENT_NAME: ${{ inputs.environment || 'dev' }} - TEST_BUCKET_NAME: jasper-test-bucket APPLY_TF_CODE: false secrets: inherit + \ No newline at end of file diff --git a/.github/workflows/publish-api.yml b/.github/workflows/publish-api.yml index 35eae812..07b3ab14 100644 --- a/.github/workflows/publish-api.yml +++ b/.github/workflows/publish-api.yml @@ -90,7 +90,7 @@ jobs: uses: actions/checkout@v4 - name: Deploy to ${{ env.ENVIRONMENT }} - uses: ./.github/workflows/actions/deploy-to-aws + uses: ./.github/workflows/actions/deploy-app with: environment: ${{ env.ENVIRONMENT }} aws_account: ${{ vars.AWS_ACCOUNT }} @@ -120,7 +120,7 @@ jobs: # Uncomment when infra in AWS in TEST environment has been configured # - name: Deploy to ${{ env.ENVIRONMENT }} - # uses: ./.github/workflows/actions/deploy-to-aws + # uses: ./.github/workflows/actions/deploy-app # with: # environment: ${{ env.ENVIRONMENT }} # aws_account: ${{ vars.AWS_ACCOUNT }} @@ -150,7 +150,7 @@ jobs: # Uncomment when infra in AWS in PROD environment has been configured # - name: Deploy to ${{ env.ENVIRONMENT }} - # uses: ./.github/workflows/actions/deploy-to-aws + # uses: ./.github/workflows/actions/deploy-app # with: # environment: ${{ env.ENVIRONMENT }} # aws_account: ${{ vars.AWS_ACCOUNT }} diff --git a/.github/workflows/publish-infra.yml b/.github/workflows/publish-infra.yml index d08303d0..a3a9d17b 100644 --- a/.github/workflows/publish-infra.yml +++ b/.github/workflows/publish-infra.yml @@ -20,14 +20,106 @@ on: - dev - test - prod + run_initial: + description: "Run initial Terraform setup?" + required: false + default: "No" + type: choice + options: + - "No" + - "Yes" jobs: + initial: + if: ${{ inputs.run_initial == 'Yes' }} + uses: ./.github/workflows/aws-template-terraform.yml + with: + CONTEXT_FOLDER: "./infrastructure/cloud/environments/initial" + CHANGE_FOLDER_NAME: environments/${{ inputs.environment }} + ENVIRONMENT_NAME: ${{ inputs.environment || 'dev' }} + APPLY_TF_CODE: true + secrets: inherit + + post-initial: + runs-on: ubuntu-latest + needs: initial + if: ${{ inputs.run_initial == 'Yes' }} + environment: ${{ inputs.environment }} + permissions: + id-token: write + packages: write + env: + DUMMY_IMAGE_NAME: dummy-image + GITHUB_IMAGE_REPO: ghcr.io/bcgov/jasper + APP_ECR_REPO_URL: ${{ vars.AWS_ACCOUNT }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com/${{ vars.APP_NAME }}-app-repo-${{ inputs.environment }} + LAMBDA_ECR_REPO_URL: ${{ vars.AWS_ACCOUNT }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com/${{ vars.APP_NAME }}-lambda-repo-${{ inputs.environment }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Image Metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.GITHUB_IMAGE_REPO }}/${{ env.DUMMY_IMAGE_NAME }} + tags: | + type=raw,value=latest + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver: docker + + - name: Build ${{ env.DUMMY_IMAGE_NAME }} image + uses: docker/build-push-action@v6 + with: + push: true + file: ./docker/dummy-image/Dockerfile.release + context: . + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-skip-session-tagging: true + aws-region: ${{ vars.AWS_REGION }} + role-to-assume: ${{ vars.AWS_ROLE_ARN }} + role-duration-seconds: 1800 + role-session-name: ci-deployment + + - name: Login to Amazon ECR + uses: aws-actions/amazon-ecr-login@v2 + + - name: Push dummy images to App ECR + shell: bash + run: | + echo 'Deploying ${{ env.DUMMY_IMAGE_NAME }} to App ECR' + docker tag ${{ env.GITHUB_IMAGE_REPO }}/${{ env.DUMMY_IMAGE_NAME }}:latest ${{ env.APP_ECR_REPO_URL }}:${{ env.DUMMY_IMAGE_NAME }} + docker push ${{ env.APP_ECR_REPO_URL }}:${{ env.DUMMY_IMAGE_NAME }} + + - name: Push dummy images to Lambda ECR + shell: bash + run: | + echo 'Deploying ${{ env.env.DUMMY_IMAGE_NAME }} to Lambda ECR' + docker tag ${{ env.GITHUB_IMAGE_REPO }}/${{ env.DUMMY_IMAGE_NAME }}:latest ${{ env.LAMBDA_ECR_REPO_URL }}:${{ env.DUMMY_IMAGE_NAME }} + docker push ${{ env.LAMBDA_ECR_REPO_URL }}:${{ env.DUMMY_IMAGE_NAME }} + deploy: + needs: [initial, post-initial] + if: always() uses: ./.github/workflows/aws-template-terraform.yml with: CONTEXT_FOLDER: "./infrastructure/cloud/environments/${{ inputs.environment || 'dev' }}" CHANGE_FOLDER_NAME: environments/${{ inputs.environment || 'dev' }} ENVIRONMENT_NAME: ${{ inputs.environment || 'dev' }} - TEST_BUCKET_NAME: jasper-test-bucket APPLY_TF_CODE: true secrets: inherit diff --git a/.github/workflows/publish-lambdas.yml b/.github/workflows/publish-lambdas.yml index 7a5de6bf..bf2203dc 100644 --- a/.github/workflows/publish-lambdas.yml +++ b/.github/workflows/publish-lambdas.yml @@ -8,44 +8,156 @@ on: - "aws/**" workflow_dispatch: - + inputs: + environment: + description: "Select target environment" + required: true + default: "dev" + type: choice + options: + - dev + - test + - prod env: WORKING_DIRECTORY: ./aws NODE_VERSION: 20 + GITHUB_IMAGE_REPO: ghcr.io/bcgov/jasper jobs: - build-and-deploy: + get-lambdas: runs-on: ubuntu-latest - + outputs: + lambda_dir_list: ${{ steps.convert.outputs.LAMBDA_DIR_LIST }} steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - - - name: Install dependencies - run: npm install + - name: Get Lambda directories + id: lambdas + shell: bash working-directory: ${{ env.WORKING_DIRECTORY }} - - - name: Run build - run: npm run build + run: | + dirs=$(find lambdas -mindepth 2 -maxdepth 2 -type d | sed 's|lambdas/||' | paste -sd ';' -) # Space-separated + echo "LAMBDA_DIRS=$dirs" >> $GITHUB_ENV + + - name: Convert FOLDERS to JSON array + id: convert + shell: bash working-directory: ${{ env.WORKING_DIRECTORY }} + run: | + LAMBDA_DIR_LIST=$(echo "${LAMBDA_DIRS}" | jq -R 'split(";")' -c) + echo "LAMBDA_DIR_LIST=$LAMBDA_DIR_LIST" >> $GITHUB_OUTPUT + + deploy-to-gchr: + needs: get-lambdas + environment: ${{ inputs.environment }} + permissions: + id-token: write + packages: write + runs-on: ubuntu-latest + env: + LAMBDA_ECR_REPO_URL: ${{ vars.AWS_ACCOUNT }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com/${{ vars.APP_NAME }}-lambda-repo-${{ vars.ENVIRONMENT_NAME }} + + strategy: + matrix: + lambda: ${{ fromJSON(needs.get-lambdas.outputs.lambda_dir_list) }} - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build Lambdas codebase + uses: ./.github/workflows/actions/build-lambdas with: - role-skip-session-tagging: true - aws-region: ${{ vars.AWS_REGION }} - role-to-assume: ${{ vars.AWS_ROLE_ARN }} - role-duration-seconds: 1800 - role-session-name: ci-deployment + working_directory: ${{ env.WORKING_DIRECTORY }} + node_version: ${{ env.NODE_VERSION }} - name: Log in to the GHCR uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get short SHA + id: short_sha + run: | + echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_ENV + + - name: Parse Resource and Lambda Name + id: parse + run: | + echo "Lambda: ${{ matrix.lambda }}" + RESOURCE=$(echo "${{ matrix.lambda }}" | cut -d'/' -f1) + LAMBDA=$(echo "${{ matrix.lambda }}" | cut -d'/' -f2) + echo "RESOURCE=$RESOURCE" >> $GITHUB_ENV + echo "LAMBDA=$LAMBDA" >> $GITHUB_ENV + + - name: Setup Image Metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.GITHUB_IMAGE_REPO }}/${{ env.RESOURCE }}.${{ env.LAMBDA }} + tags: | + type=raw,value=${{ env.SHORT_SHA }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver: docker + + - name: Build ${{ matrix.lambda }} image + uses: docker/build-push-action@v6 + with: + push: true + file: ./docker/aws/Dockerfile.release + context: ./aws + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + TARGET_FUNCTION=${{ matrix.lambda }} + NODE_VERSION=${{ env.NODE_VERSION }} + + deploy2dev: + name: Deploy to DEV + needs: [get-lambdas, deploy-to-gchr] + env: + ENVIRONMENT: dev + permissions: + id-token: write + packages: write + runs-on: ubuntu-latest + environment: dev + + strategy: + matrix: + lambda: ${{ fromJSON(needs.get-lambdas.outputs.lambda_dir_list) }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Parse Resource and Lambda Name + id: parse + run: | + echo "Lambda: ${{ matrix.lambda }}" + RESOURCE=$(echo "${{ matrix.lambda }}" | cut -d'/' -f1) + LAMBDA=$(echo "${{ matrix.lambda }}" | cut -d'/' -f2) + echo "RESOURCE=$RESOURCE" >> $GITHUB_ENV + echo "LAMBDA=$LAMBDA" >> $GITHUB_ENV + + - name: Deploy to ${{ env.ENVIRONMENT }} + uses: ./.github/workflows/actions/deploy-lambda + with: + environment: ${{ env.ENVIRONMENT }} + aws_account: ${{ vars.AWS_ACCOUNT }} + region: ${{ vars.AWS_REGION }} + app_name: ${{ vars.APP_NAME }} + aws_role_arn: ${{ vars.AWS_ROLE_ARN }} + ghcr_token: ${{ secrets.GITHUB_TOKEN }} + github_image_repo: ${{ env.GITHUB_IMAGE_REPO }} + resource: ${{ env.RESOURCE }} + lambda_name: ${{ env.LAMBDA }} + image_name: ${{ env.RESOURCE }}.${{ env.LAMBDA }} + short_sha: ${{ needs.deploy-to-gchr.outputs.short_sha }} diff --git a/.github/workflows/publish-web.yml b/.github/workflows/publish-web.yml index 64ba9dee..8ae85912 100644 --- a/.github/workflows/publish-web.yml +++ b/.github/workflows/publish-web.yml @@ -96,7 +96,7 @@ jobs: uses: actions/checkout@v4 - name: Deploy to ${{ env.ENVIRONMENT }} - uses: ./.github/workflows/actions/deploy-to-aws + uses: ./.github/workflows/actions/deploy-app with: environment: ${{ env.ENVIRONMENT }} aws_account: ${{ vars.AWS_ACCOUNT }} @@ -126,7 +126,7 @@ jobs: # Uncomment when infra in AWS in TEST environment has been configured # - name: Deploy to ${{ env.ENVIRONMENT }} - # uses: ./.github/workflows/actions/deploy-to-aws + # uses: ./.github/workflows/actions/deploy-app # with: # environment: ${{ env.ENVIRONMENT }} # aws_account: ${{ vars.AWS_ACCOUNT }} @@ -156,7 +156,7 @@ jobs: # Uncomment when infra in AWS in PROD environment has been configured # - name: Deploy to ${{ env.ENVIRONMENT }} - # uses: ./.github/workflows/actions/deploy-to-aws + # uses: ./.github/workflows/actions/deploy-app # with: # environment: ${{ env.ENVIRONMENT }} # aws_account: ${{ vars.AWS_ACCOUNT }} diff --git a/.gitignore b/.gitignore index 6079bd30..fc0aa67e 100644 --- a/.gitignore +++ b/.gitignore @@ -481,4 +481,8 @@ dist .yarn/cache .yarn/unplugged .yarn/build-state.yml -.pnp.* \ No newline at end of file +.pnp.* + +# Terraform +.terraform +*.hcl \ No newline at end of file diff --git a/api/Controllers/TestController.cs b/api/Controllers/TestController.cs index 86ba26e1..9e874f89 100644 --- a/api/Controllers/TestController.cs +++ b/api/Controllers/TestController.cs @@ -16,4 +16,4 @@ public ActionResult Headers() return Ok(Request.Headers); } } -} +} \ No newline at end of file diff --git a/api/Infrastructure/ServiceCollectionExtensions.cs b/api/Infrastructure/ServiceCollectionExtensions.cs index eafaa058..43dc10fa 100644 --- a/api/Infrastructure/ServiceCollectionExtensions.cs +++ b/api/Infrastructure/ServiceCollectionExtensions.cs @@ -57,10 +57,12 @@ public static IServiceCollection AddHttpClientsAndScvServices(this IServiceColle services.AddHttpClient(client => { - client.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue( - configuration.GetNonEmptyValue("LocationServicesClient:Username"), - configuration.GetNonEmptyValue("LocationServicesClient:Password")); + // client.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue( + // configuration.GetNonEmptyValue("LocationServicesClient:Username"), + // configuration.GetNonEmptyValue("LocationServicesClient:Password")); client.BaseAddress = new Uri(configuration.GetNonEmptyValue("LocationServicesClient:Url").EnsureEndingForwardSlash()); + var apiKey = configuration.GetNonEmptyValue("AWS_API_GATEWAY_API_KEY"); + client.DefaultRequestHeaders.Add("x-api-key", apiKey); }).AddHttpMessageHandler(); services.AddHttpClient(client => diff --git a/aws/esbuild.config.js b/aws/esbuild.config.js new file mode 100644 index 00000000..1f7672a6 --- /dev/null +++ b/aws/esbuild.config.js @@ -0,0 +1,26 @@ +const esbuild = require("esbuild") +const glob = require("glob") +const path = require("path") + +const entryPoints = glob.sync("./lambdas/*/*/*.ts") + +const builds = entryPoints.map((entry) => { + const [category, handlerName] = entry.split(path.sep).slice(-3, -1) + + // Format output file name as 'dist/[category].[handlerName]/index.js' + const outputDir = `dist/${category}.${handlerName}` + + return esbuild.build({ + entryPoints: [entry], + outfile: path.join(outputDir, "index.js"), + bundle: true, + platform: "node", + target: "node14", + format: "cjs", + sourcemap: true, + minify: true + }) +}) + +// Run all builds in parallel +Promise.all(builds).catch(() => process.exit(1)) diff --git a/aws/eslint.config.js b/aws/eslint.config.js new file mode 100644 index 00000000..34ed6b50 --- /dev/null +++ b/aws/eslint.config.js @@ -0,0 +1,20 @@ +// eslint.config.js +module.exports = [ + { + files: ["**/*.ts"], + languageOptions: { + parser: require("@typescript-eslint/parser"), + parserOptions: { + ecmaVersion: 2020, + sourceType: "module" + } + }, + plugins: { + "@typescript-eslint": require("@typescript-eslint/eslint-plugin") + }, + rules: { + ...require("@typescript-eslint/eslint-plugin").configs.recommended.rules + // Add custom rules here + } + } +] diff --git a/aws/helpers/getSecret.ts b/aws/helpers/getSecret.ts new file mode 100644 index 00000000..8ba2428b --- /dev/null +++ b/aws/helpers/getSecret.ts @@ -0,0 +1,24 @@ +import { + GetSecretValueCommand, + SecretsManagerClient +} from "@aws-sdk/client-secrets-manager" + +const secretsManagerClient = new SecretsManagerClient() + +export const getSecret = async (secretName: string): Promise => { + try { + const command = new GetSecretValueCommand({ SecretId: secretName }) + const data = await secretsManagerClient.send(command) + + if (data.SecretString) { + return data.SecretString + } else { + throw new Error( + `Secret with ID ${secretName} does not contain SecretString` + ) + } + } catch (error) { + console.error(`Error retrieving secret ${secretName}:`, error) + throw error + } +} diff --git a/aws/lambdas/files/search-civil-files/index.ts b/aws/lambdas/files/search-civil-files/index.ts new file mode 100644 index 00000000..5da21999 --- /dev/null +++ b/aws/lambdas/files/search-civil-files/index.ts @@ -0,0 +1,15 @@ +import { APIGatewayEvent, APIGatewayProxyResult, Context } from "aws-lambda" + +export const handler = async ( + event: APIGatewayEvent, + context: Context +): Promise => { + console.log(`Event: ${JSON.stringify(event, null, 2)}`) + console.log(`Context: ${JSON.stringify(context, null, 2)}`) + return { + statusCode: 200, + body: JSON.stringify({ + message: "hello from Search Civil" + }) + } +} diff --git a/aws/lambdas/files/search-criminal-files/index.ts b/aws/lambdas/files/search-criminal-files/index.ts new file mode 100644 index 00000000..e47db932 --- /dev/null +++ b/aws/lambdas/files/search-criminal-files/index.ts @@ -0,0 +1,15 @@ +import { APIGatewayEvent, APIGatewayProxyResult, Context } from "aws-lambda" + +export const handler = async ( + event: APIGatewayEvent, + context: Context +): Promise => { + console.log(`Event: ${JSON.stringify(event, null, 2)}`) + console.log(`Context: ${JSON.stringify(context, null, 2)}`) + return { + statusCode: 200, + body: JSON.stringify({ + message: "hello from Search Criminal" + }) + } +} diff --git a/aws/lambdas/locations/get-locations/index.ts b/aws/lambdas/locations/get-locations/index.ts new file mode 100644 index 00000000..edfd53fd --- /dev/null +++ b/aws/lambdas/locations/get-locations/index.ts @@ -0,0 +1,28 @@ +import { APIGatewayEvent, APIGatewayProxyResult, Context } from "aws-lambda" + +export const handler = async ( + event: APIGatewayEvent, + context: Context +): Promise => { + console.log(event, context) + + return { + statusCode: 200, + body: JSON.stringify([ + { + codeType: "COURT_LOCATIONS", + code: "10230.0001", + shortDesc: "1011", + longDesc: "Alert Bay", + flex: "N" + }, + { + codeType: "COURT_LOCATIONS", + code: "10231.0001", + shortDesc: "1051", + longDesc: "Duncan Law Courts", + flex: "Y" + } + ]) + } +} diff --git a/aws/lambdas/locations/get-rooms/index.ts b/aws/lambdas/locations/get-rooms/index.ts new file mode 100644 index 00000000..4efd1ef4 --- /dev/null +++ b/aws/lambdas/locations/get-rooms/index.ts @@ -0,0 +1,28 @@ +import { APIGatewayEvent, APIGatewayProxyResult, Context } from "aws-lambda" + +export const handler = async ( + event: APIGatewayEvent, + context: Context +): Promise => { + console.log(event, context) + + return { + statusCode: 200, + body: JSON.stringify([ + { + codeType: "COURT_ROOMS", + code: "00", + shortDesc: "CRT", + longDesc: "NIDD-00", + flex: "NIDD" + }, + { + codeType: "COURT_ROOMS", + code: "001", + shortDesc: "CRT", + longDesc: "1031-001", + flex: "1031" + } + ]) + } +} diff --git a/aws/package-lock.json b/aws/package-lock.json new file mode 100644 index 00000000..61da6cd3 --- /dev/null +++ b/aws/package-lock.json @@ -0,0 +1,3875 @@ +{ + "name": "aws", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "aws", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-secrets-manager": "^3.686.0", + "axios": "^1.7.7", + "glob": "^11.0.0" + }, + "devDependencies": { + "@types/aws-lambda": "^8.10.145", + "@types/axios": "^0.9.36", + "@types/node": "^22.9.0", + "@typescript-eslint/eslint-plugin": "^8.14.0", + "@typescript-eslint/parser": "^8.14.0", + "esbuild": "^0.24.0", + "eslint": "^9.14.0", + "eslint-define-config": "^2.1.0" + } + }, + "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/@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": ">=14.0.0" + } + }, + "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": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "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": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "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": ">=16.0.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/@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/@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": ">=14.0.0" + } + }, + "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": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "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": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.686.0.tgz", + "integrity": "sha512-cGp4ZWS1X8p4ZmmG5GXc9nup1LsmbcBLNjZqAm4OXUINSTymrL9WKSQC/Yv48WnVlloZnQvwbK7/biCPrAS06Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.686.0", + "@aws-sdk/client-sts": "3.686.0", + "@aws-sdk/core": "3.686.0", + "@aws-sdk/credential-provider-node": "3.686.0", + "@aws-sdk/middleware-host-header": "3.686.0", + "@aws-sdk/middleware-logger": "3.686.0", + "@aws-sdk/middleware-recursion-detection": "3.686.0", + "@aws-sdk/middleware-user-agent": "3.686.0", + "@aws-sdk/region-config-resolver": "3.686.0", + "@aws-sdk/types": "3.686.0", + "@aws-sdk/util-endpoints": "3.686.0", + "@aws-sdk/util-user-agent-browser": "3.686.0", + "@aws-sdk/util-user-agent-node": "3.686.0", + "@smithy/config-resolver": "^3.0.10", + "@smithy/core": "^2.5.1", + "@smithy/fetch-http-handler": "^4.0.0", + "@smithy/hash-node": "^3.0.8", + "@smithy/invalid-dependency": "^3.0.8", + "@smithy/middleware-content-length": "^3.0.10", + "@smithy/middleware-endpoint": "^3.2.1", + "@smithy/middleware-retry": "^3.0.25", + "@smithy/middleware-serde": "^3.0.8", + "@smithy/middleware-stack": "^3.0.8", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/node-http-handler": "^3.2.5", + "@smithy/protocol-http": "^4.1.5", + "@smithy/smithy-client": "^3.4.2", + "@smithy/types": "^3.6.0", + "@smithy/url-parser": "^3.0.8", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.25", + "@smithy/util-defaults-mode-node": "^3.0.25", + "@smithy/util-endpoints": "^2.1.4", + "@smithy/util-middleware": "^3.0.8", + "@smithy/util-retry": "^3.0.8", + "@smithy/util-utf8": "^3.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.686.0.tgz", + "integrity": "sha512-D8huL2BSHNP9QdQrqPcx4DCJXcG/vrPimNbymgCBgnYyS1HNs11Hu27ZPrbWCZFC8n/bvfXGXOhm8WAHOi4Vtw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.686.0", + "@aws-sdk/middleware-host-header": "3.686.0", + "@aws-sdk/middleware-logger": "3.686.0", + "@aws-sdk/middleware-recursion-detection": "3.686.0", + "@aws-sdk/middleware-user-agent": "3.686.0", + "@aws-sdk/region-config-resolver": "3.686.0", + "@aws-sdk/types": "3.686.0", + "@aws-sdk/util-endpoints": "3.686.0", + "@aws-sdk/util-user-agent-browser": "3.686.0", + "@aws-sdk/util-user-agent-node": "3.686.0", + "@smithy/config-resolver": "^3.0.10", + "@smithy/core": "^2.5.1", + "@smithy/fetch-http-handler": "^4.0.0", + "@smithy/hash-node": "^3.0.8", + "@smithy/invalid-dependency": "^3.0.8", + "@smithy/middleware-content-length": "^3.0.10", + "@smithy/middleware-endpoint": "^3.2.1", + "@smithy/middleware-retry": "^3.0.25", + "@smithy/middleware-serde": "^3.0.8", + "@smithy/middleware-stack": "^3.0.8", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/node-http-handler": "^3.2.5", + "@smithy/protocol-http": "^4.1.5", + "@smithy/smithy-client": "^3.4.2", + "@smithy/types": "^3.6.0", + "@smithy/url-parser": "^3.0.8", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.25", + "@smithy/util-defaults-mode-node": "^3.0.25", + "@smithy/util-endpoints": "^2.1.4", + "@smithy/util-middleware": "^3.0.8", + "@smithy/util-retry": "^3.0.8", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.686.0.tgz", + "integrity": "sha512-bV8yw1tpEj9WOVEnIJTcHPmTqikGccvh9RCg9ohc5DVKLajt/pUF4b+8dDyqNrEijUqlpDDwpSnh1GFhfe298A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.686.0", + "@aws-sdk/credential-provider-node": "3.686.0", + "@aws-sdk/middleware-host-header": "3.686.0", + "@aws-sdk/middleware-logger": "3.686.0", + "@aws-sdk/middleware-recursion-detection": "3.686.0", + "@aws-sdk/middleware-user-agent": "3.686.0", + "@aws-sdk/region-config-resolver": "3.686.0", + "@aws-sdk/types": "3.686.0", + "@aws-sdk/util-endpoints": "3.686.0", + "@aws-sdk/util-user-agent-browser": "3.686.0", + "@aws-sdk/util-user-agent-node": "3.686.0", + "@smithy/config-resolver": "^3.0.10", + "@smithy/core": "^2.5.1", + "@smithy/fetch-http-handler": "^4.0.0", + "@smithy/hash-node": "^3.0.8", + "@smithy/invalid-dependency": "^3.0.8", + "@smithy/middleware-content-length": "^3.0.10", + "@smithy/middleware-endpoint": "^3.2.1", + "@smithy/middleware-retry": "^3.0.25", + "@smithy/middleware-serde": "^3.0.8", + "@smithy/middleware-stack": "^3.0.8", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/node-http-handler": "^3.2.5", + "@smithy/protocol-http": "^4.1.5", + "@smithy/smithy-client": "^3.4.2", + "@smithy/types": "^3.6.0", + "@smithy/url-parser": "^3.0.8", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.25", + "@smithy/util-defaults-mode-node": "^3.0.25", + "@smithy/util-endpoints": "^2.1.4", + "@smithy/util-middleware": "^3.0.8", + "@smithy/util-retry": "^3.0.8", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.686.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.686.0.tgz", + "integrity": "sha512-WVyOYdK3w7RhK6UrA2MY8KPIbcZ88BGIoKmRhcOXdIUC8CLL1UIECgdRthFXOU+MBqDPFS+VeF+COk0CpRhE8Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.686.0", + "@aws-sdk/core": "3.686.0", + "@aws-sdk/credential-provider-node": "3.686.0", + "@aws-sdk/middleware-host-header": "3.686.0", + "@aws-sdk/middleware-logger": "3.686.0", + "@aws-sdk/middleware-recursion-detection": "3.686.0", + "@aws-sdk/middleware-user-agent": "3.686.0", + "@aws-sdk/region-config-resolver": "3.686.0", + "@aws-sdk/types": "3.686.0", + "@aws-sdk/util-endpoints": "3.686.0", + "@aws-sdk/util-user-agent-browser": "3.686.0", + "@aws-sdk/util-user-agent-node": "3.686.0", + "@smithy/config-resolver": "^3.0.10", + "@smithy/core": "^2.5.1", + "@smithy/fetch-http-handler": "^4.0.0", + "@smithy/hash-node": "^3.0.8", + "@smithy/invalid-dependency": "^3.0.8", + "@smithy/middleware-content-length": "^3.0.10", + "@smithy/middleware-endpoint": "^3.2.1", + "@smithy/middleware-retry": "^3.0.25", + "@smithy/middleware-serde": "^3.0.8", + "@smithy/middleware-stack": "^3.0.8", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/node-http-handler": "^3.2.5", + "@smithy/protocol-http": "^4.1.5", + "@smithy/smithy-client": "^3.4.2", + "@smithy/types": "^3.6.0", + "@smithy/url-parser": "^3.0.8", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.25", + "@smithy/util-defaults-mode-node": "^3.0.25", + "@smithy/util-endpoints": "^2.1.4", + "@smithy/util-middleware": "^3.0.8", + "@smithy/util-retry": "^3.0.8", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.686.0.tgz", + "integrity": "sha512-Xt3DV4DnAT3v2WURwzTxWQK34Ew+iiLzoUoguvLaZrVMFOqMMrwVjP+sizqIaHp1j7rGmFcN5I8saXnsDLuQLA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.686.0", + "@smithy/core": "^2.5.1", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.5", + "@smithy/signature-v4": "^4.2.0", + "@smithy/smithy-client": "^3.4.2", + "@smithy/types": "^3.6.0", + "@smithy/util-middleware": "^3.0.8", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.686.0.tgz", + "integrity": "sha512-osD7lPO8OREkgxPiTWmA1i6XEmOth1uW9HWWj/+A2YGCj1G/t2sHu931w4Qj9NWHYZtbTTXQYVRg+TErALV7nQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.686.0", + "@aws-sdk/types": "3.686.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.686.0.tgz", + "integrity": "sha512-xyGAD/f3vR/wssUiZrNFWQWXZvI4zRm2wpHhoHA1cC2fbRMNFYtFn365yw6dU7l00ZLcdFB1H119AYIUZS7xbw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.686.0", + "@aws-sdk/types": "3.686.0", + "@smithy/fetch-http-handler": "^4.0.0", + "@smithy/node-http-handler": "^3.2.5", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.5", + "@smithy/smithy-client": "^3.4.2", + "@smithy/types": "^3.6.0", + "@smithy/util-stream": "^3.2.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.686.0.tgz", + "integrity": "sha512-90yr47QsduNiuVizMaJ2GctXZfp/z6s9eSk8ryMxMEJ2zJtaQHmJXIxaNnXj5Kh7V+HhCK7rYu58eyhZvz2Seg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.686.0", + "@aws-sdk/credential-provider-env": "3.686.0", + "@aws-sdk/credential-provider-http": "3.686.0", + "@aws-sdk/credential-provider-process": "3.686.0", + "@aws-sdk/credential-provider-sso": "3.686.0", + "@aws-sdk/credential-provider-web-identity": "3.686.0", + "@aws-sdk/types": "3.686.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.686.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.686.0.tgz", + "integrity": "sha512-d5etJJD5rE3ALxrZag80EuFYI+tmJrS4E4dvFNRCosVFKvIC89VVpVY0W+OaA0J+D4FD3OzBwxan31BQAW3IyA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.686.0", + "@aws-sdk/credential-provider-http": "3.686.0", + "@aws-sdk/credential-provider-ini": "3.686.0", + "@aws-sdk/credential-provider-process": "3.686.0", + "@aws-sdk/credential-provider-sso": "3.686.0", + "@aws-sdk/credential-provider-web-identity": "3.686.0", + "@aws-sdk/types": "3.686.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.686.0.tgz", + "integrity": "sha512-sXqaAgyzMOc+dm4CnzAR5Q6S9OWVHyZjLfW6IQkmGjqeQXmZl24c4E82+w64C+CTkJrFLzH1VNOYp1Hy5gE6Qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.686.0", + "@aws-sdk/types": "3.686.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.686.0.tgz", + "integrity": "sha512-bGDFRcqpGUe2YBL5gmRZTLcxGwbtFd916JsdqmNgJwhhlOXPF6nqjGil5ZYruS3AMPy0BMntnG0Mvn/ZbusT/A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.686.0", + "@aws-sdk/core": "3.686.0", + "@aws-sdk/token-providers": "3.686.0", + "@aws-sdk/types": "3.686.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.686.0.tgz", + "integrity": "sha512-40UqCpPxyHCXDP7CGd9JIOZDgDZf+u1OyLaGBpjQJlz1HYuEsIWnnbTe29Yg3Ah/Zc3g4NBWcUdlGVotlnpnDg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.686.0", + "@aws-sdk/types": "3.686.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.686.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.686.0.tgz", + "integrity": "sha512-+Yc6rO02z+yhFbHmRZGvEw1vmzf/ifS9a4aBjJGeVVU+ZxaUvnk+IUZWrj4YQopUQ+bSujmMUzJLXSkbDq7yuw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.686.0", + "@smithy/protocol-http": "^4.1.5", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.686.0.tgz", + "integrity": "sha512-cX43ODfA2+SPdX7VRxu6gXk4t4bdVJ9pkktbfnkE5t27OlwNfvSGGhnHrQL8xTOFeyQ+3T+oowf26gf1OI+vIg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.686.0", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.686.0.tgz", + "integrity": "sha512-jF9hQ162xLgp9zZ/3w5RUNhmwVnXDBlABEUX8jCgzaFpaa742qR/KKtjjZQ6jMbQnP+8fOCSXFAVNMU+s6v81w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.686.0", + "@smithy/protocol-http": "^4.1.5", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.686.0.tgz", + "integrity": "sha512-/GRU68H5J66OD2a/RtX5s2ECtXTlMq6NneLlzcx0mIWnZ2VRMS2vFW2j2jrBEPJ5Y5us1/lK/fbun6gNo3qh7Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.686.0", + "@aws-sdk/types": "3.686.0", + "@aws-sdk/util-endpoints": "3.686.0", + "@smithy/core": "^2.5.1", + "@smithy/protocol-http": "^4.1.5", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.686.0.tgz", + "integrity": "sha512-6zXD3bSD8tcsMAVVwO1gO7rI1uy2fCD3czgawuPGPopeLiPpo6/3FoUWCQzk2nvEhj7p9Z4BbjwZGSlRkVrXTw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.686.0", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/types": "^3.6.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.686.0.tgz", + "integrity": "sha512-9oL4kTCSePFmyKPskibeiOXV6qavPZ63/kXM9Wh9V6dTSvBtLeNnMxqGvENGKJcTdIgtoqyqA6ET9u0PJ5IRIg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.686.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.686.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.686.0.tgz", + "integrity": "sha512-xFnrb3wxOoJcW2Xrh63ZgFo5buIu9DF7bOHnwoUxHdNpUXicUh0AHw85TjXxyxIAd0d1psY/DU7QHoNI3OswgQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.686.0.tgz", + "integrity": "sha512-7msZE2oYl+6QYeeRBjlDgxQUhq/XRky3cXE0FqLFs2muLS7XSuQEXkpOXB3R782ygAP6JX0kmBxPTLurRTikZg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.686.0", + "@smithy/types": "^3.6.0", + "@smithy/util-endpoints": "^2.1.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.679.0.tgz", + "integrity": "sha512-zKTd48/ZWrCplkXpYDABI74rQlbR0DNHs8nH95htfSLj9/mWRSwaGptoxwcihaq/77vi/fl2X3y0a1Bo8bt7RA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.686.0.tgz", + "integrity": "sha512-YiQXeGYZegF1b7B2GOR61orhgv79qmI0z7+Agm3NXLO6hGfVV3kFUJbXnjtH1BgWo5hbZYW7HQ2omGb3dnb6Lg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.686.0", + "@smithy/types": "^3.6.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.686.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.686.0.tgz", + "integrity": "sha512-XXUhZPeacJt5BmWc0qNXA4/yyQGXPmFcTOFe5aqXuZbhtTCNVJ0fPQHFip37iGSHCg8eAFykiBn9W8hD4swolQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.686.0", + "@aws-sdk/types": "3.686.0", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "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.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "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.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "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/core": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", + "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "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.0", + "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.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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.14.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz", + "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", + "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "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.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "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==", + "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/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.6.tgz", + "integrity": "sha512-0XuhuHQlEqbNQZp7QxxrFTdVWdwxch4vjxYgfInF91hZFkPxf9QDrdQka0KfxFMPqLNzSw0b95uGTrLliQUavQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.10.tgz", + "integrity": "sha512-Uh0Sz9gdUuz538nvkPiyv1DZRX9+D15EKDtnQP5rYVAzM/dnYk3P8cg73jcxyOitPgT3mE3OVj7ky7sibzHWkw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.9", + "@smithy/types": "^3.6.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.1.tgz", + "integrity": "sha512-DujtuDA7BGEKExJ05W5OdxCoyekcKT3Rhg1ZGeiUWaz2BJIWXjZmsG/DIP4W48GHno7AQwRsaCb8NcBgH3QZpg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.8", + "@smithy/protocol-http": "^4.1.5", + "@smithy/types": "^3.6.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.8", + "@smithy/util-stream": "^3.2.1", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.5.tgz", + "integrity": "sha512-4FTQGAsuwqTzVMmiRVTn0RR9GrbRfkP0wfu/tXWVHd2LgNpTY0uglQpIScXK4NaEyXbB3JmZt8gfVqO50lP8wg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.9", + "@smithy/property-provider": "^3.1.8", + "@smithy/types": "^3.6.0", + "@smithy/url-parser": "^3.0.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.0.0.tgz", + "integrity": "sha512-MLb1f5tbBO2X6K4lMEKJvxeLooyg7guq48C2zKr4qM7F2Gpkz4dc+hdSgu77pCJ76jVqFBjZczHYAs6dp15N+g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.5", + "@smithy/querystring-builder": "^3.0.8", + "@smithy/types": "^3.6.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-node": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.8.tgz", + "integrity": "sha512-tlNQYbfpWXHimHqrvgo14DrMAgUBua/cNoz9fMYcDmYej7MAmUcjav/QKQbFc3NrcPxeJ7QClER4tWZmfwoPng==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.8.tgz", + "integrity": "sha512-7Qynk6NWtTQhnGTTZwks++nJhQ1O54Mzi7fz4PqZOiYXb4Z1Flpb2yRvdALoggTS8xjtohWUM+RygOtB30YL3Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.10.tgz", + "integrity": "sha512-T4dIdCs1d/+/qMpwhJ1DzOhxCZjZHbHazEPJWdB4GDi2HjIZllVzeBEcdJUN0fomV8DURsgOyrbEUzg3vzTaOg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.5", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.1.tgz", + "integrity": "sha512-wWO3xYmFm6WRW8VsEJ5oU6h7aosFXfszlz3Dj176pTij6o21oZnzkCLzShfmRaaCHDkBXWBdO0c4sQAvLFP6zA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.1", + "@smithy/middleware-serde": "^3.0.8", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.9", + "@smithy/types": "^3.6.0", + "@smithy/url-parser": "^3.0.8", + "@smithy/util-middleware": "^3.0.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "3.0.25", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.25.tgz", + "integrity": "sha512-m1F70cPaMBML4HiTgCw5I+jFNtjgz5z5UdGnUbG37vw6kh4UvizFYjqJGHvicfgKMkDL6mXwyPp5mhZg02g5sg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.5", + "@smithy/service-error-classification": "^3.0.8", + "@smithy/smithy-client": "^3.4.2", + "@smithy/types": "^3.6.0", + "@smithy/util-middleware": "^3.0.8", + "@smithy/util-retry": "^3.0.8", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.8.tgz", + "integrity": "sha512-Xg2jK9Wc/1g/MBMP/EUn2DLspN8LNt+GMe7cgF+Ty3vl+Zvu+VeZU5nmhveU+H8pxyTsjrAkci8NqY6OuvZnjA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.8.tgz", + "integrity": "sha512-d7ZuwvYgp1+3682Nx0MD3D/HtkmZd49N3JUndYWQXfRZrYEnCWYc8BHcNmVsPAp9gKvlurdg/mubE6b/rPS9MA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.9.tgz", + "integrity": "sha512-qRHoah49QJ71eemjuS/WhUXB+mpNtwHRWQr77J/m40ewBVVwvo52kYAmb7iuaECgGTTcYxHS4Wmewfwy++ueew==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.8", + "@smithy/shared-ini-file-loader": "^3.1.9", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.5.tgz", + "integrity": "sha512-PkOwPNeKdvX/jCpn0A8n9/TyoxjGZB8WVoJmm9YzsnAgggTj4CrjpRHlTQw7dlLZ320n1mY1y+nTRUDViKi/3w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.6", + "@smithy/protocol-http": "^4.1.5", + "@smithy/querystring-builder": "^3.0.8", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.8.tgz", + "integrity": "sha512-ukNUyo6rHmusG64lmkjFeXemwYuKge1BJ8CtpVKmrxQxc6rhUX0vebcptFA9MmrGsnLhwnnqeH83VTU9hwOpjA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.5.tgz", + "integrity": "sha512-hsjtwpIemmCkm3ZV5fd/T0bPIugW1gJXwZ/hpuVubt2hEUApIoUTrf6qIdh9MAWlw0vjMrA1ztJLAwtNaZogvg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.8.tgz", + "integrity": "sha512-btYxGVqFUARbUrN6VhL9c3dnSviIwBYD9Rz1jHuN1hgh28Fpv2xjU1HeCeDJX68xctz7r4l1PBnFhGg1WBBPuA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.8.tgz", + "integrity": "sha512-BtEk3FG7Ks64GAbt+JnKqwuobJNX8VmFLBsKIwWr1D60T426fGrV2L3YS5siOcUhhp6/Y6yhBw1PSPxA5p7qGg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.8.tgz", + "integrity": "sha512-uEC/kCCFto83bz5ZzapcrgGqHOh/0r69sZ2ZuHlgoD5kYgXJEThCoTuw/y1Ub3cE7aaKdznb+jD9xRPIfIwD7g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.9.tgz", + "integrity": "sha512-/+OsJRNtoRbtsX0UpSgWVxFZLsJHo/4sTr+kBg/J78sr7iC+tHeOvOJrS5hCpVQ6sWBbhWLp1UNiuMyZhE6pmA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.1.tgz", + "integrity": "sha512-NsV1jF4EvmO5wqmaSzlnTVetemBS3FZHdyc5CExbDljcyJCEEkJr8ANu2JvtNbVg/9MvKAWV44kTrGS+Pi4INg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.5", + "@smithy/types": "^3.6.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.8", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.2.tgz", + "integrity": "sha512-dxw1BDxJiY9/zI3cBqfVrInij6ShjpV4fmGHesGZZUiP9OSE/EVfdwdRz0PgvkEvrZHpsj2htRaHJfftE8giBA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.1", + "@smithy/middleware-endpoint": "^3.2.1", + "@smithy/middleware-stack": "^3.0.8", + "@smithy/protocol-http": "^4.1.5", + "@smithy/types": "^3.6.0", + "@smithy/util-stream": "^3.2.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.6.0.tgz", + "integrity": "sha512-8VXK/KzOHefoC65yRgCn5vG1cysPJjHnOVt9d0ybFQSmJgQj152vMn4EkYhGuaOmnnZvCPav/KnYyE6/KsNZ2w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.8.tgz", + "integrity": "sha512-4FdOhwpTW7jtSFWm7SpfLGKIBC9ZaTKG5nBF0wK24aoQKQyDIKUw3+KFWCQ9maMzrgTJIuOvOnsV2lLGW5XjTg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.8", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.25", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.25.tgz", + "integrity": "sha512-fRw7zymjIDt6XxIsLwfJfYUfbGoO9CmCJk6rjJ/X5cd20+d2Is7xjU5Kt/AiDt6hX8DAf5dztmfP5O82gR9emA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.8", + "@smithy/smithy-client": "^3.4.2", + "@smithy/types": "^3.6.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.25", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.25.tgz", + "integrity": "sha512-H3BSZdBDiVZGzt8TG51Pd2FvFO0PAx/A0mJ0EH8a13KJ6iUCdYnw/Dk/MdC1kTd0eUuUGisDFaxXVXo4HHFL1g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^3.0.10", + "@smithy/credential-provider-imds": "^3.2.5", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/property-provider": "^3.1.8", + "@smithy/smithy-client": "^3.4.2", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.4.tgz", + "integrity": "sha512-kPt8j4emm7rdMWQyL0F89o92q10gvCUa6sBkBtDJ7nV2+P7wpXczzOfoDJ49CKXe5CCqb8dc1W+ZdLlrKzSAnQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.9", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.8.tgz", + "integrity": "sha512-p7iYAPaQjoeM+AKABpYWeDdtwQNxasr4aXQEA/OmbOaug9V0odRVDy3Wx4ci8soljE/JXQo+abV0qZpW8NX0yA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.8.tgz", + "integrity": "sha512-TCEhLnY581YJ+g1x0hapPz13JFqzmh/pMWL2KEFASC51qCfw3+Y47MrTmea4bUE5vsdxQ4F6/KFbUeSz22Q1ow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.8", + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.2.1.tgz", + "integrity": "sha512-R3ufuzJRxSJbE58K9AEnL/uSZyVdHzud9wLS8tIbXclxKzoe09CRohj2xV8wpx5tj7ZbiJaKYcutMm1eYgz/0A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^4.0.0", + "@smithy/node-http-handler": "^3.2.5", + "@smithy/types": "^3.6.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.145", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.145.tgz", + "integrity": "sha512-dtByW6WiFk5W5Jfgz1VM+YPA21xMXTuSFoLYIDY0L44jDLLflVPtZkYuu3/YxpGcvjzKFBZLU+GyKjR0HOYtyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/axios": { + "version": "0.9.36", + "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.9.36.tgz", + "integrity": "sha512-NLOpedx9o+rxo/X5ChbdiX6mS1atE4WHmEEIcR9NLenRVa5HoVjAvjafwU3FPTqnZEstpoqCaW7fagqSoTDNeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.14.0.tgz", + "integrity": "sha512-tqp8H7UWFaZj0yNO6bycd5YjMwxa6wIHOLZvWPkidwbgLCsBMetQoGj7DPuAlWa2yGO3H48xmPwjhsSPPCGU5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.14.0", + "@typescript-eslint/type-utils": "8.14.0", + "@typescript-eslint/utils": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.14.0.tgz", + "integrity": "sha512-2p82Yn9juUJq0XynBXtFCyrBDb6/dJombnz6vbo6mgQEtWHfvHbQuEa9kAOVIt1c9YFwi7H6WxtPj1kg+80+RA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "8.14.0", + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/typescript-estree": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.14.0.tgz", + "integrity": "sha512-aBbBrnW9ARIDn92Zbo7rguLnqQ/pOrUguVpbUwzOhkFg2npFDwTgPGqFqE0H5feXcOoJOfX3SxlJaKEVtq54dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.14.0.tgz", + "integrity": "sha512-Xcz9qOtZuGusVOH5Uk07NGs39wrKkf3AxlkK79RBK6aJC1l03CobXjJbwBPSidetAOV+5rEVuiT1VSBUOAsanQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.14.0", + "@typescript-eslint/utils": "8.14.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.14.0.tgz", + "integrity": "sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.14.0.tgz", + "integrity": "sha512-OPXPLYKGZi9XS/49rdaCbR5j/S14HazviBlUQFvSKz3npr3NikF+mrgK7CFVur6XEt95DZp/cmke9d5i3vtVnQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.14.0.tgz", + "integrity": "sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.14.0", + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/typescript-estree": "8.14.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.14.0.tgz", + "integrity": "sha512-vG0XZo8AdTH9OE6VFRwAZldNc7qtJ/6NLGWak+BtENuEUXGZgFpihILPiBvKXvJ2nFu27XNGC6rKiwuaoMbYzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.14.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.14.0.tgz", + "integrity": "sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.7.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.14.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.0", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-define-config": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-define-config/-/eslint-define-config-2.1.0.tgz", + "integrity": "sha512-QUp6pM9pjKEVannNAbSJNeRuYwW3LshejfyBBpjeMGaJjaDUpVps4C6KVR8R7dWZnD3i0synmrE36znjTkJvdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/Shinigami92" + }, + { + "type": "paypal", + "url": "https://www.paypal.com/donate/?hosted_button_id=L7GY729FBKTZY" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0.0", + "npm": ">=9.0.0", + "pnpm": ">=8.6.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", + "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/aws/package.json b/aws/package.json new file mode 100644 index 00000000..945c8bfc --- /dev/null +++ b/aws/package.json @@ -0,0 +1,27 @@ +{ + "name": "aws", + "version": "1.0.0", + "description": "Contains lambda functions that calls BCGOV API endpoints.", + "main": "index.js", + "scripts": { + "build": "node esbuild.config.js", + "lint": "eslint ." + }, + "author": "JASPER team", + "license": "ISC", + "devDependencies": { + "@types/aws-lambda": "^8.10.145", + "@types/axios": "^0.9.36", + "@types/node": "^22.9.0", + "@typescript-eslint/eslint-plugin": "^8.14.0", + "@typescript-eslint/parser": "^8.14.0", + "esbuild": "^0.24.0", + "eslint": "^9.14.0", + "eslint-define-config": "^2.1.0" + }, + "dependencies": { + "@aws-sdk/client-secrets-manager": "^3.686.0", + "axios": "^1.7.7", + "glob": "^11.0.0" + } +} diff --git a/aws/services/httpService.ts b/aws/services/httpService.ts new file mode 100644 index 00000000..cdb9cec7 --- /dev/null +++ b/aws/services/httpService.ts @@ -0,0 +1,80 @@ +import axios, { AxiosInstance, AxiosResponse } from "axios" +import * as https from "https" +import { getSecret } from "../helpers/getSecret" + +class HttpService { + private axios: AxiosInstance + + constructor() {} + + async init(baseURL: string) { + const httpsAgent = await this.initHttpsAgent() + + this.axios = axios.create({ + baseURL, + timeout: 5000, + httpsAgent + }) + } + + async initHttpsAgent(): Promise { + const mtlsCertJson = await getSecret(process.env.MTLS_SECRET_NAME) + + // Get and parse mTLS Cert + const { key, ca, cert } = JSON.parse(mtlsCertJson) + const certUtf8 = Buffer.from(cert, "base64").toString("utf-8") + const keyUtf8 = Buffer.from(key, "base64").toString("utf-8") + const caUtf8 = ca ? Buffer.from(ca, "base64").toString("utf-8") : undefined + + // Create the HTTPS Agent with the decoded cert and key + return new https.Agent({ + cert: certUtf8, + key: keyUtf8, + ca: caUtf8 ? caUtf8 : undefined, + rejectUnauthorized: true + }) + } + + async get(url: string, params?: Record): Promise { + try { + const response: AxiosResponse = await this.axios.get(url, { + params + }) + return response.data + } catch (error) { + this.handleError(error) + } + } + + async post(url: string, data?: Record): Promise { + try { + const response: AxiosResponse = await this.axios.post(url, data) + return response.data + } catch (error) { + this.handleError(error) + } + } + + async put(url: string, data?: Record): Promise { + try { + const response: AxiosResponse = await this.axios.put(url, data) + return response.data + } catch (error) { + this.handleError(error) + } + } + + private handleError(error: unknown): never { + if (axios.isAxiosError(error)) { + console.error("Axios error:", error.message) + throw new Error( + `HTTP Error: ${error.response?.status || "Unknown status"}` + ) + } else { + console.error("Unexpected error:", error) + throw new Error("Unexpected error occurred") + } + } +} + +export default HttpService diff --git a/docker/aws/Dockerfile.release b/docker/aws/Dockerfile.release new file mode 100644 index 00000000..91668aa2 --- /dev/null +++ b/docker/aws/Dockerfile.release @@ -0,0 +1,28 @@ +# Use base Node image for building +ARG NODE_VERSION=20 + +FROM node:${NODE_VERSION} AS build + +# Copy package.json to install dependencies once +WORKDIR /app +COPY /package.json . + +RUN npm install + +# Copy the rest of the code +COPY . . + +ARG TARGET_FUNCTION +# Use esbuild to bundle the specific function code +RUN npx esbuild lambdas/${TARGET_FUNCTION}/index.ts --bundle --minify --sourcemap --platform=node --target=es2020 --outfile=/app/dist/index.js + +# Final runtime image with AWS Lambda base +ARG NODE_VERSION +FROM public.ecr.aws/lambda/nodejs:${NODE_VERSION} + +# # Copy only the bundled code for the target function +WORKDIR /var/task +COPY --from=build /app/dist/index.js ./ + +# Set the command for the Lambda handler +CMD ["index.handler"] diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 3e51565f..889c6bb0 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -67,6 +67,8 @@ services: - SplunkCollectorUrl=${SplunkCollectorUrl} - SplunkCollectorId=${SplunkCollectorId} - SplunkToken=${SplunkToken} + - AWS_API_GATEWAY_URL=${AWS_API_GATEWAY_URL} + - AWS_API_GATEWAY_API_KEY=${AWS_API_GATEWAY_API_KEY} ports: - 5000:5000 volumes: diff --git a/docker/dummy-image/Dockerfile.release b/docker/dummy-image/Dockerfile.release new file mode 100644 index 00000000..b4a12cfa --- /dev/null +++ b/docker/dummy-image/Dockerfile.release @@ -0,0 +1,5 @@ +FROM alpine:latest + +RUN echo "This is a dummy image for initial deployment" > /dummy.txt + +CMD ["echo", "Dummy image deployed successfully"] diff --git a/docker/openshift/sync-secrets.sh b/docker/openshift/sync-secrets.sh index dc7e7077..5f930432 100644 --- a/docker/openshift/sync-secrets.sh +++ b/docker/openshift/sync-secrets.sh @@ -14,6 +14,7 @@ secret_keys="\ location_services_client \ lookup_services_client \ misc \ + mtls_cert \ request \ splunk \ user_services_client" diff --git a/infrastructure/cloud/environments/dev/variables.tf b/infrastructure/cloud/environments/dev/variables.tf index 007c64be..5bcaef0d 100644 --- a/infrastructure/cloud/environments/dev/variables.tf +++ b/infrastructure/cloud/environments/dev/variables.tf @@ -67,4 +67,3 @@ variable "cert_domain_name" { description = "The BCGov provisioned certificate domain name" type = string } - diff --git a/infrastructure/cloud/environments/dev/webapp.tf b/infrastructure/cloud/environments/dev/webapp.tf index 273a8285..07f50c2a 100644 --- a/infrastructure/cloud/environments/dev/webapp.tf +++ b/infrastructure/cloud/environments/dev/webapp.tf @@ -1,68 +1,217 @@ -module "security" { - source = "../../modules/security" - environment = var.environment - app_name = var.app_name - kms_key_name = var.kms_key_name - ecs_web_td_log_group_arn = module.monitoring.ecs_web_td_log_group_arn - ecs_api_td_log_group_arn = module.monitoring.ecs_api_td_log_group_arn - ecr_repository_arn = module.container.ecr_repository_arn - openshift_iam_user = var.openshift_iam_user - iam_user_table_name = var.iam_user_table_name - cert_domain_name = var.cert_domain_name -} - -module "storage" { - source = "../../modules/storage" +# This the rest of JASPER's infra resources. +# Make sure that the "initial" stack has been deployed first. + +# +# Existing Resources +# +data "aws_caller_identity" "current" {} + +# KMS Key +data "aws_kms_key" "kms_key" { + key_id = "alias/${var.kms_key_name}-${var.environment}" +} + +# VPC +data "aws_vpc" "vpc" { + id = var.vpc_id +} + +# Security Groups +data "aws_security_group" "web_sg" { + name = "Web_sg" +} + +data "aws_security_group" "app_sg" { + name = "App_sg" +} + +data "aws_security_group" "data_sg" { + name = "Data_sg" +} + +# App ECR Repo +data "aws_ecr_repository" "app_ecr_repo" { + name = "${var.app_name}-app-repo-${var.environment}" +} + +# Lambda ECR Repo +data "aws_ecr_repository" "lambda_ecr_repo" { + name = "${var.app_name}-lambda-repo-${var.environment}" +} + +# +# Modules +# + +# Create Secrets placeholder for Secrets Manager +module "secrets_manager" { + source = "../../modules/SecretsManager" + environment = var.environment + app_name = var.app_name + region = var.region + kms_key_arn = data.aws_kms_key.kms_key.arn +} + +# Create RDS Database +module "rds" { + source = "../../modules/RDS" + environment = var.environment + app_name = var.app_name + db_username = module.secrets_manager.db_username + db_password = module.secrets_manager.db_password + data_sg_id = data.aws_security_group.data_sg.id + vpc_id = data.aws_vpc.vpc.id + kms_key_arn = data.aws_kms_key.kms_key.arn + rds_db_ca_cert = var.rds_db_ca_cert +} + +# Create IAM Roles/Policies +module "iam" { + source = "../../modules/IAM" environment = var.environment app_name = var.app_name - kms_key_name = module.security.kms_key_alias - test_s3_bucket_name = var.test_s3_bucket_name - data_sg_id = module.networking.data_sg_id - db_username = module.security.db_username - db_password = module.security.db_password - vpc_id = var.vpc_id - kms_key_arn = module.security.kms_key_arn - rds_db_ca_cert = var.rds_db_ca_cert - depends_on = [module.security] -} - -module "networking" { - source = "../../modules/networking" + kms_key_arn = data.aws_kms_key.kms_key.arn + app_ecr_repo_arn = data.aws_ecr_repository.app_ecr_repo.arn + openshift_iam_user = var.openshift_iam_user + iam_user_table_name = var.iam_user_table_name + secrets_arn_list = module.secrets_manager.secrets_arn_list +} + +# Parse Subnets +module "subnets" { + source = "../../modules/Subnets" + web_subnet_names = var.web_subnet_names + app_subnet_names = var.app_subnet_names + data_subnet_names = var.data_subnet_names + vpc_id = data.aws_vpc.vpc.id +} + +# Create Target Groups +module "tg_web" { + source = "../../modules/TargetGroup" + environment = var.environment + app_name = var.app_name + name = "web" + port = 8080 + health_check_path = "/" + vpc_id = data.aws_vpc.vpc.id + protocol = "HTTPS" +} + +module "tg_api" { + source = "../../modules/TargetGroup" + environment = var.environment + app_name = var.app_name + name = "api" + port = 5000 + health_check_path = "/api/test/headers" + vpc_id = data.aws_vpc.vpc.id + protocol = "HTTP" +} + +# Setup ALB Listeners +module "alb" { + source = "../../modules/ALB" + environment = var.environment + app_name = var.app_name + lb_name = var.lb_name + cert_domain_name = var.cert_domain_name + tg_web_arn = module.tg_web.tg_arn + tg_api_arn = module.tg_api.tg_arn +} + +# Create Lambda Functions +module "lambda" { + source = "../../modules/Lambda" environment = var.environment app_name = var.app_name - region = var.region - vpc_id = var.vpc_id - web_subnet_names = var.web_subnet_names - app_subnet_names = var.app_subnet_names - data_subnet_names = var.data_subnet_names - lb_name = var.lb_name - default_lb_cert_arn = module.security.default_lb_cert_arn -} - -module "container" { - source = "../../modules/container" - environment = var.environment - app_name = var.app_name - region = var.region - ecs_execution_role_arn = module.security.ecs_execution_role_arn - web_subnet_ids = module.networking.web_subnets_ids - app_subnet_ids = module.networking.app_subnets_ids - web_sg_id = module.networking.web_sg_id - app_sg_id = module.networking.app_sg_id - web_tg_arn = module.networking.web_tg_arn - api_tg_arn = module.networking.api_tg_arn - ecs_web_td_log_group_name = module.monitoring.ecs_web_td_log_group_name - ecs_api_td_log_group_name = module.monitoring.ecs_api_td_log_group_name - kms_key_id = module.security.kms_key_id - default_lb_dns_name = module.networking.default_lb_dns_name - api_secrets = module.security.api_secrets - web_secrets = module.security.web_secrets - depends_on = [module.monitoring] -} - -module "monitoring" { - source = "../../modules/monitoring" + lambda_role_arn = module.iam.lambda_role_arn + apigw_execution_arn = module.apigw.apigw_execution_arn + lambda_ecr_repo_url = data.aws_ecr_repository.lambda_ecr_repo.repository_url + mtls_secret_name = module.secrets_manager.mtls_secret_name +} + +# Create API Gateway +module "apigw" { + source = "../../modules/APIGateway" + environment = var.environment + app_name = var.app_name + region = var.region + account_id = data.aws_caller_identity.current.account_id + lambda_functions = module.lambda.lambda_functions + ecs_execution_role_arn = module.iam.ecs_execution_role_arn +} + +# Create ECS Cluster +module "ecs_cluster" { + source = "../../modules/ECS/Cluster" environment = var.environment app_name = var.app_name - kms_key_arn = module.security.kms_key_arn + name = "app" +} + +# Create Web ECS Task Definition +module "ecs_web_td" { + source = "../../modules/ECS/TaskDefinition" + environment = var.environment + app_name = var.app_name + name = "web" + region = var.region + ecs_execution_role_arn = module.iam.ecs_execution_role_arn + ecr_repository_url = data.aws_ecr_repository.app_ecr_repo.repository_url + port = 8080 + secret_env_variables = module.secrets_manager.web_secrets + kms_key_arn = data.aws_kms_key.kms_key.arn +} + +# Create API ECS Task Definition +module "ecs_api_td" { + source = "../../modules/ECS/TaskDefinition" + environment = var.environment + app_name = var.app_name + name = "api" + region = var.region + ecs_execution_role_arn = module.iam.ecs_execution_role_arn + ecr_repository_url = data.aws_ecr_repository.app_ecr_repo.repository_url + port = 5000 + env_variables = [ + { + name = "CORS_DOMAIN" + value = module.alb.default_lb_dns_name + }, + { + name = "AWS_API_GATEWAY_URL" + value = module.apigw.apigw_invoke_url + } + ] + secret_env_variables = module.secrets_manager.api_secrets + kms_key_arn = data.aws_kms_key.kms_key.arn +} + +# Create Web ECS Service +module "ecs_web_service" { + source = "../../modules/ECS/Service" + environment = var.environment + app_name = var.app_name + name = "web" + ecs_cluster_id = module.ecs_cluster.ecs_cluster_id + ecs_td_arn = module.ecs_web_td.ecs_td_arn + tg_arn = module.tg_web.tg_arn + sg_id = data.aws_security_group.app_sg.id + subnet_ids = module.subnets.web_subnets_ids + port = module.ecs_web_td.port +} + +# Create Api ECS Service +module "ecs_api_service" { + source = "../../modules/ECS/Service" + environment = var.environment + app_name = var.app_name + name = "api" + ecs_cluster_id = module.ecs_cluster.ecs_cluster_id + ecs_td_arn = module.ecs_api_td.ecs_td_arn + tg_arn = module.tg_api.tg_arn + sg_id = data.aws_security_group.app_sg.id + subnet_ids = module.subnets.app_subnets_ids + port = module.ecs_api_td.port } diff --git a/infrastructure/cloud/environments/initial/backend.tfvars b/infrastructure/cloud/environments/initial/backend.tfvars new file mode 100644 index 00000000..1c0b5d64 --- /dev/null +++ b/infrastructure/cloud/environments/initial/backend.tfvars @@ -0,0 +1,4 @@ +bucket = "terraform-remote-state-b5e4f5-dev" +dynamodb_table = "terraform-remote-state-lock-b5e4f5" +key = "initial.terraform.tfstate" +region = "ca-central-1" diff --git a/infrastructure/cloud/environments/initial/dev.tfvars b/infrastructure/cloud/environments/initial/dev.tfvars new file mode 100644 index 00000000..1a02c1a4 --- /dev/null +++ b/infrastructure/cloud/environments/initial/dev.tfvars @@ -0,0 +1,10 @@ +region = "ca-central-1" +test_s3_bucket_name = "jasper-test-s3-bucket-dev" +web_subnet_names = ["Web_Dev_aza_net", "Web_Dev_azb_net"] +app_subnet_names = ["App_Dev_aza_net", "App_Dev_azb_net"] +data_subnet_names = ["Data_Dev_aza_net", "Data_Dev_azb_net"] +openshift_iam_user = "openshiftuserdev" +iam_user_table_name = "BCGOV_IAM_USER_TABLE" +lb_name = "default" +rds_db_ca_cert = "rds-ca-rsa2048-g1" +cert_domain_name = "*.example.ca" diff --git a/infrastructure/cloud/environments/initial/main.tf b/infrastructure/cloud/environments/initial/main.tf new file mode 100644 index 00000000..b6f662dd --- /dev/null +++ b/infrastructure/cloud/environments/initial/main.tf @@ -0,0 +1,33 @@ +# This "initial" stack is deployed first to avoid circular dependency to other resources +data "aws_caller_identity" "current" {} + +# Custom KMS Key +module "kms" { + source = "../../modules/KMS" + environment = var.environment + app_name = var.app_name + region = var.region + kms_key_name = var.kms_key_name + openshift_iam_user = var.openshift_iam_user + account_id = data.aws_caller_identity.current.account_id +} + +# UI and API ECR repository +module "app_ecr" { + source = "../../modules/ECR" + environment = var.environment + app_name = var.app_name + region = var.region + kms_key_id = module.kms.kms_key_id + repo_name = "app" +} + +# Lambda functions ECR repository +module "lambda_ecr" { + source = "../../modules/ECR" + environment = var.environment + app_name = var.app_name + region = var.region + kms_key_id = module.kms.kms_key_id + repo_name = "lambda" +} diff --git a/infrastructure/cloud/environments/initial/providers.tf b/infrastructure/cloud/environments/initial/providers.tf new file mode 100644 index 00000000..4cafa436 --- /dev/null +++ b/infrastructure/cloud/environments/initial/providers.tf @@ -0,0 +1,21 @@ +terraform { + required_version = "~> 1.9.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + + tls = { + source = "hashicorp/tls" + version = "4.0.5" + } + } + + backend "s3" { + } +} + +provider "aws" { + region = var.region +} diff --git a/infrastructure/cloud/modules/networking/variables.tf b/infrastructure/cloud/environments/initial/variables.tf similarity index 55% rename from infrastructure/cloud/modules/networking/variables.tf rename to infrastructure/cloud/environments/initial/variables.tf index 6638249f..007c64be 100644 --- a/infrastructure/cloud/modules/networking/variables.tf +++ b/infrastructure/cloud/environments/initial/variables.tf @@ -1,3 +1,18 @@ +variable "test_s3_bucket_name" { + description = "The name of the S3 bucket to create for testing" + type = string +} + +variable "region" { + description = "The AWS region" + type = string +} + +variable "kms_key_name" { + description = "Name of KMS key" + type = string +} + variable "app_name" { description = "The name of the application" type = string @@ -8,11 +23,6 @@ variable "environment" { type = string } -variable "region" { - description = "The AWS region" - type = string -} - variable "vpc_id" { description = "The provisioned VPC ID" type = string @@ -33,12 +43,28 @@ variable "data_subnet_names" { type = list(string) } +variable "openshift_iam_user" { + description = "Openshift IAM Username" + type = string +} + +variable "iam_user_table_name" { + description = "The BCGOV DynamoDb IAM user table" + type = string +} + variable "lb_name" { description = "The BCGOV provisioned Load Balancer name" type = string } -variable "default_lb_cert_arn" { - description = "The default Load Balancer certificate ARN" +variable "rds_db_ca_cert" { + description = "The Certifiate Authority identifier used in RDS" + type = string +} + +variable "cert_domain_name" { + description = "The BCGov provisioned certificate domain name" type = string } + diff --git a/infrastructure/cloud/modules/networking/alb.tf b/infrastructure/cloud/modules/ALB/main.tf similarity index 78% rename from infrastructure/cloud/modules/networking/alb.tf rename to infrastructure/cloud/modules/ALB/main.tf index 4457cdf9..0bbf111c 100644 --- a/infrastructure/cloud/modules/networking/alb.tf +++ b/infrastructure/cloud/modules/ALB/main.tf @@ -1,6 +1,10 @@ -# -# Default Load Balancer -# +# Load Balancer Certificate +data "aws_acm_certificate" "default_lb_cert" { + domain = var.cert_domain_name + most_recent = true + statuses = ["ISSUED"] +} + data "aws_lb" "default_lb" { name = var.lb_name } @@ -20,7 +24,7 @@ resource "aws_lb_listener" "http_listener" { port = "443" protocol = "HTTPS" query = "#{query}" - status_code = "HTTP_301" # Use HTTP_302 for temporary redirects + status_code = "HTTP_301" } } } @@ -30,7 +34,7 @@ resource "aws_lb_listener" "https_listener" { load_balancer_arn = data.aws_lb.default_lb.arn port = 443 protocol = "HTTPS" - certificate_arn = var.default_lb_cert_arn + certificate_arn = data.aws_acm_certificate.default_lb_cert.arn default_action { type = "fixed-response" @@ -42,7 +46,7 @@ resource "aws_lb_listener" "https_listener" { } } -# HTTPS Listener Rules +# Web Listener Rule resource "aws_lb_listener_rule" "web_lr" { listener_arn = aws_lb_listener.https_listener.arn priority = 200 @@ -55,7 +59,7 @@ resource "aws_lb_listener_rule" "web_lr" { action { type = "forward" - target_group_arn = aws_lb_target_group.web_target_group.arn + target_group_arn = var.tg_web_arn } tags = { @@ -63,6 +67,7 @@ resource "aws_lb_listener_rule" "web_lr" { } } +# API Listener Rule resource "aws_lb_listener_rule" "api_path_lr" { listener_arn = aws_lb_listener.https_listener.arn priority = 100 @@ -75,7 +80,7 @@ resource "aws_lb_listener_rule" "api_path_lr" { action { type = "forward" - target_group_arn = aws_lb_target_group.api_target_group.arn + target_group_arn = var.tg_api_arn } tags = { diff --git a/infrastructure/cloud/modules/ALB/outputs.tf b/infrastructure/cloud/modules/ALB/outputs.tf new file mode 100644 index 00000000..d9e6ceb3 --- /dev/null +++ b/infrastructure/cloud/modules/ALB/outputs.tf @@ -0,0 +1,3 @@ +output "default_lb_dns_name" { + value = data.aws_lb.default_lb.dns_name +} diff --git a/infrastructure/cloud/modules/ALB/variables.tf b/infrastructure/cloud/modules/ALB/variables.tf new file mode 100644 index 00000000..c7444e05 --- /dev/null +++ b/infrastructure/cloud/modules/ALB/variables.tf @@ -0,0 +1,31 @@ +variable "app_name" { + description = "The name of the application" + type = string +} + +variable "environment" { + description = "The AWS environment to deploy to" + type = string +} + +variable "lb_name" { + description = "The BCGOV provisioned Load Balancer name" + type = string +} + +variable "cert_domain_name" { + description = "The BCGov provisioned certificate domain name" + type = string +} + +variable "tg_web_arn" { + description = "The Web Target Group ARN" + type = string +} + +variable "tg_api_arn" { + description = "The API Target Group ARN" + type = string +} + + diff --git a/infrastructure/cloud/modules/APIGateway/main.tf b/infrastructure/cloud/modules/APIGateway/main.tf new file mode 100644 index 00000000..e2cb0262 --- /dev/null +++ b/infrastructure/cloud/modules/APIGateway/main.tf @@ -0,0 +1,169 @@ +resource "aws_api_gateway_rest_api" "apigw" { + name = "${var.app_name}-api-gateway-${var.environment}" + +} + +resource "aws_api_gateway_deployment" "apigw_deployment" { + depends_on = [ + aws_api_gateway_integration.get_locations_integration, + aws_api_gateway_integration.get_locations_rooms_integration, + aws_api_gateway_integration.get_files_civil_integration, + aws_api_gateway_integration.get_files_criminal_integration, + ] + rest_api_id = aws_api_gateway_rest_api.apigw.id + stage_name = var.environment +} + +resource "aws_api_gateway_rest_api_policy" "apigw_rest_api_policy" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Principal = var.ecs_execution_role_arn + Action = "execute-api:Invoke" + Resource = "arn:aws:execute-api:${var.region}:${var.account_id}:${aws_api_gateway_rest_api.apigw.id}/*" + } + ] + }) +} + +resource "aws_api_gateway_usage_plan" "apigw_usage_plan" { + name = "${var.app_name}-apigw-usage-plan-${var.environment}" + + api_stages { + api_id = aws_api_gateway_rest_api.apigw.id + stage = aws_api_gateway_deployment.apigw_deployment.stage_name + } +} + +resource "aws_api_gateway_api_key" "apigw_api_key" { + name = "${var.app_name}-apigw-api-key-${var.environment}" +} + +resource "aws_api_gateway_usage_plan_key" "apigw_usage_plan_key" { + key_id = aws_api_gateway_api_key.apigw_api_key.id + key_type = "API_KEY" + usage_plan_id = aws_api_gateway_usage_plan.apigw_usage_plan.id +} + +# +# /locations Resource +# +resource "aws_api_gateway_resource" "locations_resource" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + parent_id = aws_api_gateway_rest_api.apigw.root_resource_id + path_part = "locations" +} + +# GET /locations +resource "aws_api_gateway_method" "get_locations_method" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + resource_id = aws_api_gateway_resource.locations_resource.id + http_method = var.lambda_functions["get-locations"].http_method + authorization = "AWS_IAM" + api_key_required = true + + request_parameters = { + "method.request.header.x-origin-verify" = true + } +} + +resource "aws_api_gateway_integration" "get_locations_integration" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + resource_id = aws_api_gateway_resource.locations_resource.id + http_method = aws_api_gateway_method.get_locations_method.http_method + type = "AWS_PROXY" + integration_http_method = "POST" + uri = var.lambda_functions["get-locations"].invoke_arn +} + +# /locations/rooms Resource +resource "aws_api_gateway_resource" "rooms_resource" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + parent_id = aws_api_gateway_resource.locations_resource.id + path_part = "rooms" +} + +# GET /locations/rooms +resource "aws_api_gateway_method" "get_locations_rooms_method" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + resource_id = aws_api_gateway_resource.rooms_resource.id + http_method = var.lambda_functions["get-rooms"].http_method + authorization = "AWS_IAM" + api_key_required = true + + request_parameters = { + "method.request.header.x-origin-verify" = true + } +} + +resource "aws_api_gateway_integration" "get_locations_rooms_integration" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + resource_id = aws_api_gateway_resource.rooms_resource.id + http_method = aws_api_gateway_method.get_locations_rooms_method.http_method + type = "AWS_PROXY" + integration_http_method = "POST" + uri = var.lambda_functions["get-rooms"].invoke_arn +} + +# +# /files Resource +# +resource "aws_api_gateway_resource" "files_resource" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + parent_id = aws_api_gateway_rest_api.apigw.root_resource_id + path_part = "files" +} + +# /files/civil Resource +resource "aws_api_gateway_resource" "civil_resource" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + parent_id = aws_api_gateway_resource.files_resource.id + path_part = "civil" +} + +# GET /files/civil +resource "aws_api_gateway_method" "get_files_civil_method" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + resource_id = aws_api_gateway_resource.civil_resource.id + http_method = var.lambda_functions["search-civil-files"].http_method + authorization = "AWS_IAM" + api_key_required = true +} + +resource "aws_api_gateway_integration" "get_files_civil_integration" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + resource_id = aws_api_gateway_resource.civil_resource.id + http_method = aws_api_gateway_method.get_files_civil_method.http_method + type = "AWS_PROXY" + integration_http_method = "POST" + uri = var.lambda_functions["search-civil-files"].invoke_arn +} + +# /files/criminal Resource +resource "aws_api_gateway_resource" "criminal_resource" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + parent_id = aws_api_gateway_resource.files_resource.id + path_part = "criminal" +} + +# GET /files/criminal +resource "aws_api_gateway_method" "get_files_criminal_method" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + resource_id = aws_api_gateway_resource.criminal_resource.id + http_method = var.lambda_functions["search-criminal-files"].http_method + authorization = "AWS_IAM" + api_key_required = true +} + +resource "aws_api_gateway_integration" "get_files_criminal_integration" { + rest_api_id = aws_api_gateway_rest_api.apigw.id + resource_id = aws_api_gateway_resource.criminal_resource.id + http_method = aws_api_gateway_method.get_files_criminal_method.http_method + type = "AWS_PROXY" + integration_http_method = "POST" + uri = var.lambda_functions["search-criminal-files"].invoke_arn +} diff --git a/infrastructure/cloud/modules/APIGateway/outputs.tf b/infrastructure/cloud/modules/APIGateway/outputs.tf new file mode 100644 index 00000000..9d2ceb8a --- /dev/null +++ b/infrastructure/cloud/modules/APIGateway/outputs.tf @@ -0,0 +1,11 @@ +output "apigw_id" { + value = aws_api_gateway_rest_api.apigw.id +} + +output "apigw_invoke_url" { + value = aws_api_gateway_deployment.apigw_deployment.invoke_url +} + +output "apigw_execution_arn" { + value = aws_api_gateway_rest_api.apigw.execution_arn +} diff --git a/infrastructure/cloud/modules/APIGateway/variables.tf b/infrastructure/cloud/modules/APIGateway/variables.tf new file mode 100644 index 00000000..fb7e023b --- /dev/null +++ b/infrastructure/cloud/modules/APIGateway/variables.tf @@ -0,0 +1,33 @@ +variable "app_name" { + description = "The name of the application" + type = string +} + +variable "environment" { + description = "The AWS environment to deploy to" + type = string +} + +variable "region" { + description = "The AWS region" + type = string +} + +variable "account_id" { + description = "The current AWS Account Id" + type = string +} + +variable "lambda_functions" { + description = "Lambda functions config" + type = map(object({ + http_method = string + resource_path = string + invoke_arn = string + })) +} + +variable "ecs_execution_role_arn" { + description = "The ECS Task Definition Execution role ARN" + type = string +} diff --git a/infrastructure/cloud/modules/Cloudwatch/LogGroup/main.tf b/infrastructure/cloud/modules/Cloudwatch/LogGroup/main.tf new file mode 100644 index 00000000..004e5cb9 --- /dev/null +++ b/infrastructure/cloud/modules/Cloudwatch/LogGroup/main.tf @@ -0,0 +1,6 @@ +resource "aws_cloudwatch_log_group" "log_group" { + name = "/aws/${var.resource_name}/${var.app_name}-${var.resource_name}-${var.name}-log-group-${var.environment}" + retention_in_days = 90 + kms_key_id = var.kms_key_arn +} + diff --git a/infrastructure/cloud/modules/Cloudwatch/LogGroup/outputs.tf b/infrastructure/cloud/modules/Cloudwatch/LogGroup/outputs.tf new file mode 100644 index 00000000..fbee1a01 --- /dev/null +++ b/infrastructure/cloud/modules/Cloudwatch/LogGroup/outputs.tf @@ -0,0 +1,3 @@ +output "log_group_name" { + value = aws_cloudwatch_log_group.log_group.name +} diff --git a/infrastructure/cloud/modules/Cloudwatch/LogGroup/variables.tf b/infrastructure/cloud/modules/Cloudwatch/LogGroup/variables.tf new file mode 100644 index 00000000..6040d78d --- /dev/null +++ b/infrastructure/cloud/modules/Cloudwatch/LogGroup/variables.tf @@ -0,0 +1,24 @@ +variable "environment" { + description = "The environment to deploy the application to" + type = string +} + +variable "app_name" { + description = "The name of the application" + type = string +} + +variable "kms_key_arn" { + description = "KMS Key ARN" + type = string +} + +variable "resource_name" { + description = "Resource name of the Log Group" + type = string +} + +variable "name" { + description = "Log Group Name" + type = string +} diff --git a/infrastructure/cloud/modules/container/ecr.tf b/infrastructure/cloud/modules/ECR/main.tf similarity index 70% rename from infrastructure/cloud/modules/container/ecr.tf rename to infrastructure/cloud/modules/ECR/main.tf index e22327b7..f7a8c174 100644 --- a/infrastructure/cloud/modules/container/ecr.tf +++ b/infrastructure/cloud/modules/ECR/main.tf @@ -1,5 +1,5 @@ resource "aws_ecr_repository" "ecr_repository" { - name = "${var.app_name}-ecr-repo-${var.environment}" + name = "${var.app_name}-${var.repo_name}-repo-${var.environment}" force_delete = true image_tag_mutability = "IMMUTABLE" @@ -19,6 +19,6 @@ resource "aws_ecr_repository" "ecr_repository" { } tags = { - name = "${var.app_name}-ecr-repo-${var.environment}" + name = "${var.app_name}-${var.repo_name}-repo-${var.environment}" } } diff --git a/infrastructure/cloud/modules/compute/.gitkeep b/infrastructure/cloud/modules/ECR/outputs.tf similarity index 100% rename from infrastructure/cloud/modules/compute/.gitkeep rename to infrastructure/cloud/modules/ECR/outputs.tf diff --git a/infrastructure/cloud/modules/ECR/variables.tf b/infrastructure/cloud/modules/ECR/variables.tf new file mode 100644 index 00000000..9d350403 --- /dev/null +++ b/infrastructure/cloud/modules/ECR/variables.tf @@ -0,0 +1,24 @@ +variable "environment" { + description = "The environment to deploy the application to" + type = string +} + +variable "app_name" { + description = "The name of the application" + type = string +} + +variable "region" { + description = "The AWS region" + type = string +} + +variable "repo_name" { + description = "The ECR Repository name" + type = string +} + +variable "kms_key_id" { + description = "The KMS Key ID" + type = string +} diff --git a/infrastructure/cloud/modules/ECS/Cluster/main.tf b/infrastructure/cloud/modules/ECS/Cluster/main.tf new file mode 100644 index 00000000..055fa0a0 --- /dev/null +++ b/infrastructure/cloud/modules/ECS/Cluster/main.tf @@ -0,0 +1,12 @@ +resource "aws_ecs_cluster" "ecs_cluster" { + name = "${var.app_name}-${var.name}-cluster-${var.environment}" + + setting { + name = "containerInsights" + value = "enabled" + } + + tags = { + name = "${var.app_name}-${var.name}-cluster-${var.environment}" + } +} diff --git a/infrastructure/cloud/modules/ECS/Cluster/outputs.tf b/infrastructure/cloud/modules/ECS/Cluster/outputs.tf new file mode 100644 index 00000000..d9a71f0b --- /dev/null +++ b/infrastructure/cloud/modules/ECS/Cluster/outputs.tf @@ -0,0 +1,3 @@ +output "ecs_cluster_id" { + value = aws_ecs_cluster.ecs_cluster.id +} diff --git a/infrastructure/cloud/modules/monitoring/variables.tf b/infrastructure/cloud/modules/ECS/Cluster/variables.tf similarity index 72% rename from infrastructure/cloud/modules/monitoring/variables.tf rename to infrastructure/cloud/modules/ECS/Cluster/variables.tf index 797fefad..e149226a 100644 --- a/infrastructure/cloud/modules/monitoring/variables.tf +++ b/infrastructure/cloud/modules/ECS/Cluster/variables.tf @@ -8,8 +8,6 @@ variable "app_name" { type = string } -variable "kms_key_arn" { - description = "KMS Key ARN" - type = string +variable "name" { + description = "The name of ECS cluster" } - diff --git a/infrastructure/cloud/modules/ECS/Service/main.tf b/infrastructure/cloud/modules/ECS/Service/main.tf new file mode 100644 index 00000000..946ec9f0 --- /dev/null +++ b/infrastructure/cloud/modules/ECS/Service/main.tf @@ -0,0 +1,23 @@ +resource "aws_ecs_service" "ecs_service" { + name = "${var.app_name}-${var.name}-ecs-service-${var.environment}" + cluster = var.ecs_cluster_id + task_definition = var.ecs_td_arn + launch_type = "FARGATE" + desired_count = 1 + + network_configuration { + subnets = var.subnet_ids + security_groups = [var.sg_id] + assign_public_ip = true + } + + load_balancer { + target_group_arn = var.tg_arn + container_name = "${var.app_name}-${var.name}-container-${var.environment}" + container_port = var.port + } + + lifecycle { + prevent_destroy = true + } +} diff --git a/infrastructure/cloud/modules/ECS/Service/outputs.tf b/infrastructure/cloud/modules/ECS/Service/outputs.tf new file mode 100644 index 00000000..e69de29b diff --git a/infrastructure/cloud/modules/ECS/Service/variables.tf b/infrastructure/cloud/modules/ECS/Service/variables.tf new file mode 100644 index 00000000..6a157bca --- /dev/null +++ b/infrastructure/cloud/modules/ECS/Service/variables.tf @@ -0,0 +1,43 @@ +variable "environment" { + description = "The environment to deploy the application to" + type = string +} + +variable "app_name" { + description = "The name of the application" + type = string +} + +variable "name" { + description = "The name of ECS cluster" +} + +variable "subnet_ids" { + description = "The Subnet IDs" + type = list(string) +} + +variable "sg_id" { + description = "The BCGOV provisioned Security Group" + type = string +} + +variable "tg_arn" { + description = "The Target Group ARN" + type = string +} + +variable "port" { + description = "The Container port number" + type = number +} + +variable "ecs_cluster_id" { + description = "The ECS Cluster Id" + type = string +} + +variable "ecs_td_arn" { + description = "The ECS Task Definition ARN" + type = string +} diff --git a/infrastructure/cloud/modules/ECS/TaskDefinition/main.tf b/infrastructure/cloud/modules/ECS/TaskDefinition/main.tf new file mode 100644 index 00000000..4e2da487 --- /dev/null +++ b/infrastructure/cloud/modules/ECS/TaskDefinition/main.tf @@ -0,0 +1,50 @@ +resource "aws_ecs_task_definition" "ecs_td" { + family = "${var.app_name}-${var.name}-td-${var.environment}" + network_mode = "awsvpc" + requires_compatibilities = ["FARGATE"] + cpu = var.cpu + memory = var.memory_size + execution_role_arn = var.ecs_execution_role_arn + task_role_arn = var.ecs_execution_role_arn + + # lifecycle { + # # Since the dummy-image will be replaced when the GHA pipeline runs, + # # the whole container_definition edits has been ignored. + # ignore_changes = [container_definitions] + # } + + container_definitions = jsonencode([ + { + name = "${var.app_name}-${var.name}-container-${var.environment}" + image = "${var.ecr_repository_url}:${var.image_name}" # This is a placeholder image and will be replaced every deployment of GHA. + essential = true + portMappings = [ + { + containerPort = var.port + hostPort = var.port + protocol = "tcp" + } + ] + logConfiguration = { + logDriver = "awslogs" + options = { + "awslogs-group" = "/ecs/${var.name}-td" + "awslogs-region" = var.region + "awslogs-stream-prefix" = "ecs" + } + } + environment = var.env_variables != null ? [ + for env in var.env_variables : { + name = env.name + value = env.value + } + ] : [] + secrets = [ + for secret in var.secret_env_variables : { + name = secret[0] + valueFrom = secret[1] + } + ] + } + ]) +} diff --git a/infrastructure/cloud/modules/ECS/TaskDefinition/outputs.tf b/infrastructure/cloud/modules/ECS/TaskDefinition/outputs.tf new file mode 100644 index 00000000..50737d20 --- /dev/null +++ b/infrastructure/cloud/modules/ECS/TaskDefinition/outputs.tf @@ -0,0 +1,7 @@ +output "ecs_td_arn" { + value = aws_ecs_task_definition.ecs_td.arn +} + +output "port" { + value = var.port +} diff --git a/infrastructure/cloud/modules/ECS/TaskDefinition/variables.tf b/infrastructure/cloud/modules/ECS/TaskDefinition/variables.tf new file mode 100644 index 00000000..0645b12d --- /dev/null +++ b/infrastructure/cloud/modules/ECS/TaskDefinition/variables.tf @@ -0,0 +1,70 @@ +variable "environment" { + description = "The environment to deploy the application to" + type = string +} + +variable "app_name" { + description = "The name of the application" + type = string +} + +variable "region" { + description = "The AWS region" + type = string +} + +variable "name" { + description = "The name of ECS cluster" +} + +variable "cpu" { + description = "The ECS Task Definition CPU" + type = number + default = 256 +} + +variable "memory_size" { + description = "The ECS Task Definition Memory Size" + type = number + default = 512 +} + +variable "ecs_execution_role_arn" { + description = "The ECS Task Definition Execution role ARN" + type = string +} + +variable "ecr_repository_url" { + description = "The ECR Repository URL where the image will be referenced" + type = string +} + +variable "port" { + description = "The Container port number" + type = number +} + +variable "env_variables" { + description = "The environment variables for the container" + type = list(object({ + name = string + value = string + })) + default = null +} + +variable "secret_env_variables" { + description = "The sensitive environment variables for the container" + type = list(list(string)) +} + +variable "kms_key_arn" { + description = "KMS Key ARN" + type = string +} + +variable "image_name" { + description = "Image name that will be attached to the Task Definition" + type = string + default = "dummy-image" +} diff --git a/infrastructure/cloud/modules/security/iam.tf b/infrastructure/cloud/modules/IAM/main.tf similarity index 71% rename from infrastructure/cloud/modules/security/iam.tf rename to infrastructure/cloud/modules/IAM/main.tf index e1343e5e..19e2fc6a 100644 --- a/infrastructure/cloud/modules/security/iam.tf +++ b/infrastructure/cloud/modules/IAM/main.tf @@ -48,7 +48,7 @@ resource "aws_iam_role_policy" "ecs_execution_policy" { ], Effect = "Allow", Resource = [ - var.ecr_repository_arn + var.app_ecr_repo_arn ] }, { @@ -59,8 +59,7 @@ resource "aws_iam_role_policy" "ecs_execution_policy" { ], Effect = "Allow", Resource = [ - "${var.ecs_web_td_log_group_arn}:*", - "${var.ecs_api_td_log_group_arn}:*" + "arn:aws:logs:*:*:log-group:/aws/ecs/*:*" ] }, { @@ -69,18 +68,7 @@ resource "aws_iam_role_policy" "ecs_execution_policy" { ], Effect = "Allow", Resource = [ - aws_secretsmanager_secret.aspnet_core_secret.arn, - aws_secretsmanager_secret.file_services_client_secret.arn, - aws_secretsmanager_secret.location_services_client_secret.arn, - aws_secretsmanager_secret.lookup_services_client_secret.arn, - aws_secretsmanager_secret.user_services_client_secret.arn, - aws_secretsmanager_secret.keycloak_secret.arn, - aws_secretsmanager_secret.request_secret.arn, - aws_secretsmanager_secret.splunk_secret.arn, - aws_secretsmanager_secret.database_secret.arn, - aws_secretsmanager_secret.aspnet_core_secret.arn, - aws_secretsmanager_secret.misc_secret.arn, - aws_secretsmanager_secret.auth_secret.arn + "arn:aws:secretsmanager:*:*:secret:external/*" ] }, { @@ -88,7 +76,7 @@ resource "aws_iam_role_policy" "ecs_execution_policy" { "kms:Decrypt" ], Effect = "Allow", - Resource = aws_kms_key.kms_key.arn + Resource = var.kms_key_arn }, { "Effect" : "Allow", @@ -217,8 +205,8 @@ resource "aws_iam_policy" "openshift_role_policy" { ], "Effect" : "Allow", "Resource" : [ - "arn:aws:ssm:*:*:parameter/iam_users/*", - "arn:aws:kms:*:*:key/*" + "arn:aws:ssm:*:*:parameter/iam_users/${var.openshift_iam_user}_keys", + var.kms_key_arn ] }, { @@ -227,20 +215,8 @@ resource "aws_iam_policy" "openshift_role_policy" { "secretsmanager:DescribeSecret", "secretsmanager:PutSecretValue", ] - Effect = "Allow" - Resource = [ - aws_secretsmanager_secret.aspnet_core_secret.arn, - aws_secretsmanager_secret.auth_secret.arn, - aws_secretsmanager_secret.database_secret.arn, - aws_secretsmanager_secret.file_services_client_secret.arn, - aws_secretsmanager_secret.keycloak_secret.arn, - aws_secretsmanager_secret.location_services_client_secret.arn, - aws_secretsmanager_secret.lookup_services_client_secret.arn, - aws_secretsmanager_secret.misc_secret.arn, - aws_secretsmanager_secret.request_secret.arn, - aws_secretsmanager_secret.splunk_secret.arn, - aws_secretsmanager_secret.user_services_client_secret.arn - ] + Effect = "Allow" + Resource = var.secrets_arn_list } ] }) @@ -250,3 +226,92 @@ resource "aws_iam_role_policy_attachment" "openshift_role_policy_attachment" { role = aws_iam_role.openshift_role.name policy_arn = aws_iam_policy.openshift_role_policy.arn } + +# +# Lambda +# +resource "aws_iam_role" "lambda_role" { + name = "${var.app_name}-lambda-role-${var.environment}" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Principal = { + Service = "lambda.amazonaws.com" + } + Action = "sts:AssumeRole" + } + ] + }) +} + +resource "aws_iam_policy" "lambda_role_policy" { + name = "${var.app_name}-lambda-role-policy-${var.environment}" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + "Action" : [ + "kms:Encrypt", + "kms:Decrypt" + ], + "Effect" : "Allow", + "Resource" : [ + var.kms_key_arn + ] + }, + { + Action = [ + "secretsmanager:GetSecretValue", + "secretsmanager:DescribeSecret", + "secretsmanager:PutSecretValue", + ] + Effect = "Allow" + Resource = var.secrets_arn_list + }, + { + Effect = "Allow" + Action = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ] + Resource = "arn:aws:logs:*:*:log-group:/aws/lambda/${var.app_name}-*-lambda-${var.environment}:*" + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "lambda_role_policy_attachment" { + role = aws_iam_role.lambda_role.name + policy_arn = aws_iam_policy.lambda_role_policy.arn +} + +# +# API Gateway +# +resource "aws_iam_role" "apigw_logging_role" { + name = "${var.app_name}-apigw-logging-role-${var.environment}" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Principal = { + Service = "apigateway.amazonaws.com" + } + Action = "sts:AssumeRole" + } + ] + }) +} + +# Attach the AmazonAPIGatewayPushToCloudWatchLogs policy +resource "aws_iam_role_policy_attachment" "apigw_logging_role_policy" { + role = aws_iam_role.apigw_logging_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" +} diff --git a/infrastructure/cloud/modules/IAM/outputs.tf b/infrastructure/cloud/modules/IAM/outputs.tf new file mode 100644 index 00000000..e9e6f2cf --- /dev/null +++ b/infrastructure/cloud/modules/IAM/outputs.tf @@ -0,0 +1,11 @@ +output "ecs_execution_role_arn" { + value = aws_iam_role.ecs_execution_role.arn +} + +output "lambda_role_arn" { + value = aws_iam_role.lambda_role.arn +} + +output "apigw_logging_role_arn" { + value = aws_iam_role.apigw_logging_role.arn +} diff --git a/infrastructure/cloud/modules/IAM/variables.tf b/infrastructure/cloud/modules/IAM/variables.tf new file mode 100644 index 00000000..87ab8c13 --- /dev/null +++ b/infrastructure/cloud/modules/IAM/variables.tf @@ -0,0 +1,34 @@ +variable "app_name" { + description = "The name of the application" + type = string +} + +variable "environment" { + description = "The AWS environment to deploy to" + type = string +} + +variable "kms_key_arn" { + description = "The KMS Key ARN" + type = string +} + +variable "app_ecr_repo_arn" { + description = "The App (UI/API) ECR Repository ARN" + type = string +} + +variable "openshift_iam_user" { + description = "Openshift IAM Username" + type = string +} + +variable "iam_user_table_name" { + description = "The BCGOV IAM User DynamoDb table name" + type = string +} + +variable "secrets_arn_list" { + description = "List of Secrets ARN" + type = list(string) +} diff --git a/infrastructure/cloud/modules/security/kms.tf b/infrastructure/cloud/modules/KMS/main.tf similarity index 85% rename from infrastructure/cloud/modules/security/kms.tf rename to infrastructure/cloud/modules/KMS/main.tf index ed9cbab0..e6ed8100 100644 --- a/infrastructure/cloud/modules/security/kms.tf +++ b/infrastructure/cloud/modules/KMS/main.tf @@ -1,4 +1,3 @@ -data "aws_caller_identity" "current" {} # kms key for encryption resource "aws_kms_key" "kms_key" { @@ -31,8 +30,8 @@ resource "aws_kms_key_policy" "kms_key_policy" { Effect = "Allow" Principal = { AWS = [ - "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root", - "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/${var.openshift_iam_user}", + "arn:aws:iam::${var.account_id}:root", + "arn:aws:iam::${var.account_id}:user/${var.openshift_iam_user}" ] } Action = "kms:*" diff --git a/infrastructure/cloud/modules/KMS/outputs.tf b/infrastructure/cloud/modules/KMS/outputs.tf new file mode 100644 index 00000000..b2727b8e --- /dev/null +++ b/infrastructure/cloud/modules/KMS/outputs.tf @@ -0,0 +1,3 @@ +output "kms_key_id" { + value = aws_kms_key.kms_key.id +} diff --git a/infrastructure/cloud/modules/KMS/variables.tf b/infrastructure/cloud/modules/KMS/variables.tf new file mode 100644 index 00000000..180fbb3a --- /dev/null +++ b/infrastructure/cloud/modules/KMS/variables.tf @@ -0,0 +1,29 @@ +variable "environment" { + description = "The environment to deploy the application to" + type = string +} + +variable "app_name" { + description = "The name of the application" + type = string +} + +variable "region" { + description = "The AWS region" + type = string +} + +variable "kms_key_name" { + description = "Name of KMS key" + type = string +} + +variable "openshift_iam_user" { + description = "Openshift IAM Username" + type = string +} + +variable "account_id" { + description = "The current AWS Account Id" + type = string +} diff --git a/infrastructure/cloud/modules/Lambda/main.tf b/infrastructure/cloud/modules/Lambda/main.tf new file mode 100644 index 00000000..e7dde05e --- /dev/null +++ b/infrastructure/cloud/modules/Lambda/main.tf @@ -0,0 +1,50 @@ +locals { + lambda_functions = { + for k, v in var.functions : k => { + name = k + memory_size = lookup(v, "memory_size", 2048) + timeout = lookup(v, "timeout", 30) + http_method = v.http_method + resource_path = v.resource_path + env_variables = v.env_variables + } + } + + default_env_variables = { + MTLS_SECRET_NAME = var.mtls_secret_name + } +} + +resource "aws_lambda_function" "lambda" { + for_each = local.lambda_functions + + function_name = "${var.app_name}-${each.key}-lambda-${var.environment}" + role = var.lambda_role_arn + timeout = each.value.timeout + memory_size = each.value.memory_size + package_type = "Image" + image_uri = "${var.lambda_ecr_repo_url}:dummy-image" # This is a placeholder image and will be replaced every deployment of GHA. + + environment { + variables = merge(local.default_env_variables, each.value.env_variables) + } + + lifecycle { + create_before_destroy = true + ignore_changes = [image_uri] + } + + tracing_config { + mode = "Active" + } +} + +resource "aws_lambda_permission" "lambda_permissions" { + for_each = local.lambda_functions + + statement_id = "AllowAPIGatewayInvoke-${each.key}" + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.lambda[each.key].arn + principal = "apigateway.amazonaws.com" + source_arn = "${var.apigw_execution_arn}/*/${each.value.http_method}${each.value.resource_path}" +} diff --git a/infrastructure/cloud/modules/Lambda/outputs.tf b/infrastructure/cloud/modules/Lambda/outputs.tf new file mode 100644 index 00000000..acf0503c --- /dev/null +++ b/infrastructure/cloud/modules/Lambda/outputs.tf @@ -0,0 +1,10 @@ +output "lambda_functions" { + value = { + for name, lambda in aws_lambda_function.lambda : name => { + name = lambda.function_name + http_method = var.functions[name].http_method + resource_path = var.functions[name].resource_path + invoke_arn = lambda.invoke_arn + } + } +} diff --git a/infrastructure/cloud/modules/Lambda/variables.tf b/infrastructure/cloud/modules/Lambda/variables.tf new file mode 100644 index 00000000..6d9b443f --- /dev/null +++ b/infrastructure/cloud/modules/Lambda/variables.tf @@ -0,0 +1,59 @@ +variable "app_name" { + description = "The name of the application" + type = string +} + +variable "environment" { + description = "The AWS environment to deploy to" + type = string +} + +variable "lambda_role_arn" { + description = "The Lambda IAM Role ARN" + type = string +} + +variable "functions" { + description = "Lambda functions config" + type = map(object({ + http_method = string + resource_path = string + env_variables = optional(map(string), {}) + timeout = optional(number, 300) + memory_size = optional(number, 2048) + })) + default = { + # Keys should match the folder name in lambda code + "get-locations" = { + http_method = "GET" + resource_path = "/locations" + }, + "get-rooms" = { + http_method = "GET" + resource_path = "/locations/rooms" + }, + "search-criminal-files" = { + http_method = "GET" + resource_path = "/files/criminal" + }, + "search-civil-files" = { + http_method = "GET" + resource_path = "/files/civil" + } + } +} + +variable "apigw_execution_arn" { + description = "The API Gateway Execution ARN" + type = string +} + +variable "lambda_ecr_repo_url" { + description = "The Lambda ECR Repository URL" + type = string +} + +variable "mtls_secret_name" { + description = "The secret name of mTLS Cert in Secrets Manager" + type = string +} diff --git a/infrastructure/cloud/modules/storage/rds.tf b/infrastructure/cloud/modules/RDS/main.tf similarity index 89% rename from infrastructure/cloud/modules/storage/rds.tf rename to infrastructure/cloud/modules/RDS/main.tf index 9a990874..f404ffa2 100644 --- a/infrastructure/cloud/modules/storage/rds.tf +++ b/infrastructure/cloud/modules/RDS/main.tf @@ -1,3 +1,7 @@ +data "aws_security_group" "data_sg" { + name = "Data_sg" +} + resource "aws_db_instance" "postgres_db_instance" { allocated_storage = 20 storage_type = "gp2" @@ -8,7 +12,7 @@ resource "aws_db_instance" "postgres_db_instance" { username = var.db_username password = var.db_password parameter_group_name = "default.postgres16" - vpc_security_group_ids = [var.data_sg_id] + vpc_security_group_ids = [data.aws_security_group.data_sg.id] db_subnet_group_name = "default-${var.vpc_id}" storage_encrypted = true kms_key_id = var.kms_key_arn diff --git a/infrastructure/cloud/modules/RDS/outputs.tf b/infrastructure/cloud/modules/RDS/outputs.tf new file mode 100644 index 00000000..e69de29b diff --git a/infrastructure/cloud/modules/storage/variables.tf b/infrastructure/cloud/modules/RDS/variables.tf similarity index 80% rename from infrastructure/cloud/modules/storage/variables.tf rename to infrastructure/cloud/modules/RDS/variables.tf index 0d464bda..019a99fd 100644 --- a/infrastructure/cloud/modules/storage/variables.tf +++ b/infrastructure/cloud/modules/RDS/variables.tf @@ -1,14 +1,3 @@ - -variable "test_s3_bucket_name" { - type = string - description = "The name of the S3 bucket to create for testing" -} - -variable "kms_key_name" { - description = "Name of KMS key" - type = string -} - variable "app_name" { description = "The name of the application" type = string diff --git a/infrastructure/cloud/modules/storage/s3buckets.tf b/infrastructure/cloud/modules/S3/main.tf similarity index 100% rename from infrastructure/cloud/modules/storage/s3buckets.tf rename to infrastructure/cloud/modules/S3/main.tf diff --git a/infrastructure/cloud/modules/security/secretsmanager.tf b/infrastructure/cloud/modules/SecretsManager/main.tf similarity index 84% rename from infrastructure/cloud/modules/security/secretsmanager.tf rename to infrastructure/cloud/modules/SecretsManager/main.tf index 78e99e46..bacd8a49 100644 --- a/infrastructure/cloud/modules/security/secretsmanager.tf +++ b/infrastructure/cloud/modules/SecretsManager/main.tf @@ -1,6 +1,6 @@ resource "aws_secretsmanager_secret" "file_services_client_secret" { name = "external/${var.app_name}-file-services-client-secret-${var.environment}" - kms_key_id = aws_kms_key.kms_key.arn + kms_key_id = var.kms_key_arn } resource "aws_secretsmanager_secret_version" "file_services_client_secret_value" { @@ -14,7 +14,7 @@ resource "aws_secretsmanager_secret_version" "file_services_client_secret_value" resource "aws_secretsmanager_secret" "location_services_client_secret" { name = "external/${var.app_name}-location-services-client-secret-${var.environment}" - kms_key_id = aws_kms_key.kms_key.arn + kms_key_id = var.kms_key_arn } resource "aws_secretsmanager_secret_version" "location_services_client_secret_value" { @@ -28,7 +28,7 @@ resource "aws_secretsmanager_secret_version" "location_services_client_secret_va resource "aws_secretsmanager_secret" "lookup_services_client_secret" { name = "external/${var.app_name}-lookup-services-client-secret-${var.environment}" - kms_key_id = aws_kms_key.kms_key.arn + kms_key_id = var.kms_key_arn } resource "aws_secretsmanager_secret_version" "lookup_services_client_secret_value" { @@ -42,7 +42,7 @@ resource "aws_secretsmanager_secret_version" "lookup_services_client_secret_valu resource "aws_secretsmanager_secret" "user_services_client_secret" { name = "external/${var.app_name}-user-services-client-secret-${var.environment}" - kms_key_id = aws_kms_key.kms_key.arn + kms_key_id = var.kms_key_arn } resource "aws_secretsmanager_secret_version" "user_services_client_secret_value" { @@ -56,7 +56,7 @@ resource "aws_secretsmanager_secret_version" "user_services_client_secret_value" resource "aws_secretsmanager_secret" "keycloak_secret" { name = "external/${var.app_name}-keycloak-secret-${var.environment}" - kms_key_id = aws_kms_key.kms_key.arn + kms_key_id = var.kms_key_arn } resource "aws_secretsmanager_secret_version" "keycloak_secret_value" { @@ -73,7 +73,7 @@ resource "aws_secretsmanager_secret_version" "keycloak_secret_value" { resource "aws_secretsmanager_secret" "request_secret" { name = "external/${var.app_name}-request-secret-${var.environment}" - kms_key_id = aws_kms_key.kms_key.arn + kms_key_id = var.kms_key_arn } resource "aws_secretsmanager_secret_version" "request_secret_value" { @@ -88,7 +88,7 @@ resource "aws_secretsmanager_secret_version" "request_secret_value" { resource "aws_secretsmanager_secret" "splunk_secret" { name = "external/${var.app_name}-splunk-secret-${var.environment}" - kms_key_id = aws_kms_key.kms_key.arn + kms_key_id = var.kms_key_arn } resource "aws_secretsmanager_secret_version" "splunk_secret_value" { @@ -102,7 +102,7 @@ resource "aws_secretsmanager_secret_version" "splunk_secret_value" { resource "aws_secretsmanager_secret" "database_secret" { name = "external/${var.app_name}-database-secret-${var.environment}" - kms_key_id = aws_kms_key.kms_key.arn + kms_key_id = var.kms_key_arn } resource "aws_secretsmanager_secret_version" "database_secret_value" { @@ -122,7 +122,7 @@ data "aws_secretsmanager_secret_version" "current_db_secret_value" { resource "aws_secretsmanager_secret" "aspnet_core_secret" { name = "external/${var.app_name}-aspnet-core-secret-${var.environment}" - kms_key_id = aws_kms_key.kms_key.arn + kms_key_id = var.kms_key_arn } resource "aws_secretsmanager_secret_version" "aspnet_core_secret_value" { @@ -135,7 +135,7 @@ resource "aws_secretsmanager_secret_version" "aspnet_core_secret_value" { resource "aws_secretsmanager_secret" "misc_secret" { name = "external/${var.app_name}-misc-secret-${var.environment}" - kms_key_id = aws_kms_key.kms_key.arn + kms_key_id = var.kms_key_arn } resource "aws_secretsmanager_secret_version" "misc_secret_value" { @@ -148,13 +148,14 @@ resource "aws_secretsmanager_secret_version" "misc_secret_value" { realIpFrom = "", apiUrl = "" siteMinderLogoutUrl = "", - includeSiteMinderHeaders = "" + includeSiteMinderHeaders = "", + mtlsCert = "" }) } resource "aws_secretsmanager_secret" "auth_secret" { name = "external/${var.app_name}-auth-secret-${var.environment}" - kms_key_id = aws_kms_key.kms_key.arn + kms_key_id = var.kms_key_arn } resource "aws_secretsmanager_secret_version" "auth_secret_value" { @@ -162,6 +163,21 @@ resource "aws_secretsmanager_secret_version" "auth_secret_value" { secret_string = jsonencode({ userId = "", userPassword = "", - allowSiteMinderUserType = "" + allowSiteMinderUserType = "", + apigwApiKey = "" + }) +} + +resource "aws_secretsmanager_secret" "mtls_cert_secret" { + name = "external/${var.app_name}-mtls-cert-secret-${var.environment}" + kms_key_id = var.kms_key_arn +} + +resource "aws_secretsmanager_secret_version" "mtls_cert_secret_value" { + secret_id = aws_secretsmanager_secret.mtls_cert_secret.id + secret_string = jsonencode({ + cert = "", + key = "", + ca = "" }) } diff --git a/infrastructure/cloud/modules/security/outputs.tf b/infrastructure/cloud/modules/SecretsManager/output.tf similarity index 83% rename from infrastructure/cloud/modules/security/outputs.tf rename to infrastructure/cloud/modules/SecretsManager/output.tf index add54138..7c3b8803 100644 --- a/infrastructure/cloud/modules/security/outputs.tf +++ b/infrastructure/cloud/modules/SecretsManager/output.tf @@ -1,24 +1,25 @@ - -output "kms_key_alias" { - value = aws_kms_alias.kms_alias.name -} - -output "ecs_execution_role_arn" { - value = aws_iam_role.ecs_execution_role.arn -} - -output "kms_key_id" { - value = aws_kms_key.kms_key.key_id -} - -output "kms_key_arn" { - value = aws_kms_key.kms_key.arn +output "secrets_arn_list" { + value = [ + aws_secretsmanager_secret.aspnet_core_secret.arn, + aws_secretsmanager_secret.auth_secret.arn, + aws_secretsmanager_secret.database_secret.arn, + aws_secretsmanager_secret.file_services_client_secret.arn, + aws_secretsmanager_secret.keycloak_secret.arn, + aws_secretsmanager_secret.location_services_client_secret.arn, + aws_secretsmanager_secret.lookup_services_client_secret.arn, + aws_secretsmanager_secret.misc_secret.arn, + aws_secretsmanager_secret.mtls_cert_secret.arn, + aws_secretsmanager_secret.request_secret.arn, + aws_secretsmanager_secret.splunk_secret.arn, + aws_secretsmanager_secret.user_services_client_secret.arn + ] } output "api_secrets" { value = [ ["ASPNETCORE_URLS", "${aws_secretsmanager_secret.aspnet_core_secret.arn}:urls::"], ["ASPNETCORE_ENVIRONMENT", "${aws_secretsmanager_secret.aspnet_core_secret.arn}:environment::"], + ["AWS_API_GATEWAY_API_KEY", "${aws_secretsmanager_secret.auth_secret.arn}:apigwApiKey::"], ["Auth__UserId", "${aws_secretsmanager_secret.auth_secret.arn}:userId::"], ["Auth__UserPassword", "${aws_secretsmanager_secret.auth_secret.arn}:userPassword::"], ["Auth__AllowSiteMinderUserType", "${aws_secretsmanager_secret.auth_secret.arn}:allowSiteMinderUserType::"], @@ -74,6 +75,6 @@ output "db_password" { sensitive = true } -output "default_lb_cert_arn" { - value = data.aws_acm_certificate.default_lb_cert.arn +output "mtls_secret_name" { + value = aws_secretsmanager_secret.mtls_cert_secret.name } diff --git a/infrastructure/cloud/modules/SecretsManager/variables.tf b/infrastructure/cloud/modules/SecretsManager/variables.tf new file mode 100644 index 00000000..b234206c --- /dev/null +++ b/infrastructure/cloud/modules/SecretsManager/variables.tf @@ -0,0 +1,19 @@ +variable "environment" { + description = "The environment to deploy the application to" + type = string +} + +variable "app_name" { + description = "The name of the application" + type = string +} + +variable "region" { + description = "The AWS region" + type = string +} + +variable "kms_key_arn" { + description = "The KMS Key ARN" + type = string +} diff --git a/infrastructure/cloud/modules/networking/subnets.tf b/infrastructure/cloud/modules/Subnets/main.tf similarity index 64% rename from infrastructure/cloud/modules/networking/subnets.tf rename to infrastructure/cloud/modules/Subnets/main.tf index 237d4a27..71b05998 100644 --- a/infrastructure/cloud/modules/networking/subnets.tf +++ b/infrastructure/cloud/modules/Subnets/main.tf @@ -1,7 +1,7 @@ data "aws_subnets" "all_subnets" { filter { name = "vpc-id" - values = [data.aws_vpc.vpc.id] + values = [var.vpc_id] } } @@ -15,7 +15,10 @@ locals { for tag_value in var.web_subnet_names : tag_value => [ for subnet in data.aws_subnet.subnets : - subnet.id if substr(subnet.tags["Name"], 0, length(tag_value)) == tag_value + { + id = subnet.id + cidr_block = subnet.cidr_block + } if substr(subnet.tags["Name"], 0, length(tag_value)) == tag_value ] } @@ -27,7 +30,10 @@ locals { for tag_value in var.app_subnet_names : tag_value => [ for subnet in data.aws_subnet.subnets : - subnet.id if substr(subnet.tags["Name"], 0, length(tag_value)) == tag_value + { + id = subnet.id + cidr_block = subnet.cidr_block + } if substr(subnet.tags["Name"], 0, length(tag_value)) == tag_value ] } @@ -39,7 +45,10 @@ locals { for tag_value in var.data_subnet_names : tag_value => [ for subnet in data.aws_subnet.subnets : - subnet.id if substr(subnet.tags["Name"], 0, length(tag_value)) == tag_value + { + id = subnet.id + cidr_block = subnet.cidr_block + } if substr(subnet.tags["Name"], 0, length(tag_value)) == tag_value ] } diff --git a/infrastructure/cloud/modules/Subnets/outputs.tf b/infrastructure/cloud/modules/Subnets/outputs.tf new file mode 100644 index 00000000..50894849 --- /dev/null +++ b/infrastructure/cloud/modules/Subnets/outputs.tf @@ -0,0 +1,11 @@ +output "web_subnets_ids" { + value = [for subnet in local.web_subnets : subnet.id] +} + +output "app_subnets_ids" { + value = [for subnet in local.app_subnets : subnet.id] +} + +output "data_subnets_ids" { + value = [for subnet in local.data_subnets : subnet.id] +} diff --git a/infrastructure/cloud/modules/Subnets/variables.tf b/infrastructure/cloud/modules/Subnets/variables.tf new file mode 100644 index 00000000..1e83a401 --- /dev/null +++ b/infrastructure/cloud/modules/Subnets/variables.tf @@ -0,0 +1,19 @@ +variable "web_subnet_names" { + description = "List of Subnets for Web" + type = list(string) +} + +variable "app_subnet_names" { + description = "List of Subnets for App" + type = list(string) +} + +variable "data_subnet_names" { + description = "List of Subnets for Data" + type = list(string) +} + +variable "vpc_id" { + description = "The provisioned VPC ID" + type = string +} diff --git a/infrastructure/cloud/modules/TargetGroup/main.tf b/infrastructure/cloud/modules/TargetGroup/main.tf new file mode 100644 index 00000000..b466c346 --- /dev/null +++ b/infrastructure/cloud/modules/TargetGroup/main.tf @@ -0,0 +1,20 @@ +resource "aws_lb_target_group" "target_group" { + name = "${var.app_name}-${var.name}-tg-${var.environment}" + port = var.port + protocol = var.protocol + vpc_id = var.vpc_id + target_type = "ip" + deregistration_delay = 5 + + health_check { + protocol = var.protocol + path = var.health_check_path + port = var.port + interval = 30 + timeout = 5 + enabled = true + healthy_threshold = 3 + unhealthy_threshold = 3 + matcher = "200" + } +} diff --git a/infrastructure/cloud/modules/TargetGroup/output.tf b/infrastructure/cloud/modules/TargetGroup/output.tf new file mode 100644 index 00000000..2f288873 --- /dev/null +++ b/infrastructure/cloud/modules/TargetGroup/output.tf @@ -0,0 +1,3 @@ +output "tg_arn" { + value = aws_lb_target_group.target_group.arn +} diff --git a/infrastructure/cloud/modules/TargetGroup/variables.tf b/infrastructure/cloud/modules/TargetGroup/variables.tf new file mode 100644 index 00000000..1c84cb1d --- /dev/null +++ b/infrastructure/cloud/modules/TargetGroup/variables.tf @@ -0,0 +1,34 @@ +variable "app_name" { + description = "The name of the application" + type = string +} + +variable "environment" { + description = "The AWS environment to deploy to" + type = string +} + +variable "name" { + description = "The name of Target Group" + type = string +} + +variable "port" { + description = "The port number" + type = number +} + +variable "health_check_path" { + description = "The health check path" + type = string +} + +variable "vpc_id" { + description = "The VPC Id" + type = string +} + +variable "protocol" { + description = "The protocol that will be used by the Target Group and Health Check" + type = string +} diff --git a/infrastructure/cloud/modules/container/ecs.tf b/infrastructure/cloud/modules/container/ecs.tf deleted file mode 100644 index 99f15669..00000000 --- a/infrastructure/cloud/modules/container/ecs.tf +++ /dev/null @@ -1,155 +0,0 @@ -resource "aws_ecs_cluster" "ecs_cluster" { - name = "${var.app_name}-ecs-cluster-${var.environment}" - - setting { - name = "containerInsights" - value = "enabled" - } - - tags = { - name = "${var.app_name}-ecs-cluster-${var.environment}" - } -} - -# Web -resource "aws_ecs_task_definition" "ecs_web_task_definition" { - family = "${var.app_name}-web-task-definition-${var.environment}" - network_mode = "awsvpc" - requires_compatibilities = ["FARGATE"] - cpu = 256 - memory = 512 - execution_role_arn = var.ecs_execution_role_arn - task_role_arn = var.ecs_execution_role_arn - - lifecycle { - ignore_changes = [container_definitions] - } - - container_definitions = jsonencode([ - { - name = "${var.app_name}-web-container-${var.environment}" - image = "${aws_ecr_repository.ecr_repository.repository_url}:jasper-web" - essential = true - portMappings = [ - { - containerPort = 8080 - hostPort = 8080 - protocol = "tcp" - } - ] - logConfiguration = { - logDriver = "awslogs" - options = { - "awslogs-group" = var.ecs_web_td_log_group_name - "awslogs-region" = var.region - "awslogs-stream-prefix" = "ecs" - } - } - secrets = [ - for secret in var.web_secrets : { - name = secret[0] - valueFrom = secret[1] - } - ] - } - ]) -} - -resource "aws_ecs_service" "ecs_web_service" { - name = "${var.app_name}-ecs-web-service-${var.environment}" - cluster = aws_ecs_cluster.ecs_cluster.id - task_definition = aws_ecs_task_definition.ecs_web_task_definition.arn - launch_type = "FARGATE" - desired_count = 1 - enable_execute_command = true - - network_configuration { - subnets = var.web_subnet_ids - security_groups = [var.app_sg_id] - assign_public_ip = true - } - - load_balancer { - target_group_arn = var.web_tg_arn - container_name = "${var.app_name}-web-container-${var.environment}" - container_port = 8080 - } - - lifecycle { - prevent_destroy = true - } -} - -# API -resource "aws_ecs_task_definition" "ecs_api_task_definition" { - family = "${var.app_name}-api-task-definition-${var.environment}" - network_mode = "awsvpc" - requires_compatibilities = ["FARGATE"] - cpu = 256 - memory = 512 - execution_role_arn = var.ecs_execution_role_arn - task_role_arn = var.ecs_execution_role_arn - - lifecycle { - ignore_changes = [container_definitions] - } - - container_definitions = jsonencode([ - { - name = "${var.app_name}-api-container-${var.environment}" - image = "${aws_ecr_repository.ecr_repository.repository_url}:jasper-api" - essential = true - portMappings = [ - { - containerPort = 5000 - hostPort = 5000 - protocol = "tcp" - } - ] - environment = [ - { - name = "CORS_DOMAIN" - value = var.default_lb_dns_name - } - ] - secrets = [ - for secret in var.api_secrets : { - name = secret[0] - valueFrom = secret[1] - } - ] - logConfiguration = { - logDriver = "awslogs" - options = { - "awslogs-group" = var.ecs_api_td_log_group_name - "awslogs-region" = var.region - "awslogs-stream-prefix" = "ecs" - } - } - } - ]) -} - -resource "aws_ecs_service" "ecs_api_service" { - name = "${var.app_name}-ecs-api-service-${var.environment}" - cluster = aws_ecs_cluster.ecs_cluster.id - task_definition = aws_ecs_task_definition.ecs_api_task_definition.arn - launch_type = "FARGATE" - desired_count = 1 - - network_configuration { - subnets = var.app_subnet_ids - security_groups = [var.app_sg_id] - assign_public_ip = true - } - - load_balancer { - target_group_arn = var.api_tg_arn - container_name = "${var.app_name}-api-container-${var.environment}" - container_port = 5000 - } - - lifecycle { - prevent_destroy = true - } -} diff --git a/infrastructure/cloud/modules/container/outputs.tf b/infrastructure/cloud/modules/container/outputs.tf deleted file mode 100644 index 349fdf78..00000000 --- a/infrastructure/cloud/modules/container/outputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -output "ecr_url" { - value = try(aws_ecr_repository.ecr_repository.repository_url, "") -} - -output "ecr_repository_arn" { - value = aws_ecr_repository.ecr_repository.arn -} diff --git a/infrastructure/cloud/modules/container/variables.tf b/infrastructure/cloud/modules/container/variables.tf deleted file mode 100644 index 7b623789..00000000 --- a/infrastructure/cloud/modules/container/variables.tf +++ /dev/null @@ -1,79 +0,0 @@ -variable "environment" { - description = "The environment to deploy the application to" - type = string -} - -variable "app_name" { - description = "The name of the application" - type = string -} - -variable "region" { - description = "The AWS region" - type = string -} - -variable "ecs_execution_role_arn" { - description = "ECS Execution Role ARN" - type = string -} - -variable "web_subnet_ids" { - description = "The Web Subnet IDs" - type = list(string) -} - -variable "app_subnet_ids" { - description = "The App Subnet IDs" - type = list(string) -} - -variable "web_sg_id" { - description = "The BCGOV provisioned Web Security Group" - type = string -} - -variable "app_sg_id" { - description = "The BCGOV provisioned App Security Group" - type = string -} - -variable "web_tg_arn" { - description = "The Web Target Group ARN" - type = string -} - -variable "api_tg_arn" { - description = "The API Target Group ARN" - type = string -} - -variable "ecs_web_td_log_group_name" { - description = "ECS Web Task Definition Log Group Name in CloudWatch" - type = string -} - -variable "ecs_api_td_log_group_name" { - description = "ECS API Task Definition Log Group Name in CloudWatch" - type = string -} - -variable "kms_key_id" { - description = "The KMS Key ID" - type = string -} - -variable "default_lb_dns_name" { - description = "The BCGov Load Balancer DNS Name" - type = string -} - -variable "api_secrets" { - description = "List if env variable secrets used in API" - type = list(list(string)) -} - -variable "web_secrets" { - description = "List if env variable secrets used in Web" - type = list(list(string)) -} diff --git a/infrastructure/cloud/modules/monitoring/logs.tf b/infrastructure/cloud/modules/monitoring/logs.tf deleted file mode 100644 index 61c74f30..00000000 --- a/infrastructure/cloud/modules/monitoring/logs.tf +++ /dev/null @@ -1,13 +0,0 @@ -resource "aws_cloudwatch_log_group" "ecs_web_td_log_group" { - name = "/aws/ecs/${var.app_name}-ecs-web-td-log-group-${var.environment}" - retention_in_days = 90 - - kms_key_id = var.kms_key_arn -} - -resource "aws_cloudwatch_log_group" "ecs_api_td_log_group" { - name = "/aws/ecs/${var.app_name}-ecs-api-td-log-group-${var.environment}" - retention_in_days = 90 - - kms_key_id = var.kms_key_arn -} diff --git a/infrastructure/cloud/modules/monitoring/outputs.tf b/infrastructure/cloud/modules/monitoring/outputs.tf deleted file mode 100644 index 7b277a6b..00000000 --- a/infrastructure/cloud/modules/monitoring/outputs.tf +++ /dev/null @@ -1,15 +0,0 @@ -output "ecs_web_td_log_group_name" { - value = aws_cloudwatch_log_group.ecs_web_td_log_group.name -} - -output "ecs_web_td_log_group_arn" { - value = aws_cloudwatch_log_group.ecs_web_td_log_group.arn -} - -output "ecs_api_td_log_group_name" { - value = aws_cloudwatch_log_group.ecs_api_td_log_group.name -} - -output "ecs_api_td_log_group_arn" { - value = aws_cloudwatch_log_group.ecs_api_td_log_group.arn -} diff --git a/infrastructure/cloud/modules/networking/outputs.tf b/infrastructure/cloud/modules/networking/outputs.tf deleted file mode 100644 index 313ef830..00000000 --- a/infrastructure/cloud/modules/networking/outputs.tf +++ /dev/null @@ -1,34 +0,0 @@ -output "web_tg_arn" { - value = aws_lb_target_group.web_target_group.arn -} - -output "api_tg_arn" { - value = aws_lb_target_group.api_target_group.arn -} - -output "web_sg_id" { - value = data.aws_security_group.web_sg.id -} - -output "app_sg_id" { - value = data.aws_security_group.app_sg.id -} - -output "web_subnets_ids" { - value = local.web_subnets -} - -output "app_subnets_ids" { - value = local.app_subnets -} -output "data_subnets_ids" { - value = local.data_subnets -} - -output "default_lb_dns_name" { - value = data.aws_lb.default_lb.dns_name -} - -output "data_sg_id" { - value = data.aws_security_group.data_sg.id -} diff --git a/infrastructure/cloud/modules/networking/securitygroup.tf b/infrastructure/cloud/modules/networking/securitygroup.tf deleted file mode 100644 index 0dc0f892..00000000 --- a/infrastructure/cloud/modules/networking/securitygroup.tf +++ /dev/null @@ -1,161 +0,0 @@ -# -# BCGOV provisioned security groups -# -data "aws_security_group" "web_sg" { - name = "Web_sg" -} - -data "aws_security_group" "app_sg" { - name = "App_sg" -} - -data "aws_security_group" "data_sg" { - name = "Data_sg" -} - -# # -# # Load Balancer Security Group -# # -# resource "aws_security_group" "lb_sg" { -# name = "${var.app_name}-lb-sg-${var.environment}" -# vpc_id = data.aws_vpc.vpc.id -# description = "Security Group for the Application Load Balancer" - -# tags = { -# Name = "${var.app_name}_lb_sg_${var.environment}" -# } -# } - -# # Load Balancer Ingress Rules. This will change once we get the public load balancer details from cloud team. -# resource "aws_vpc_security_group_ingress_rule" "lb_sg_ingress_http_allow_80" { -# security_group_id = aws_security_group.lb_sg.id -# description = "Allow inbound HTTP traffic on port 80" -# ip_protocol = "tcp" -# from_port = 80 -# to_port = 80 -# cidr_ipv4 = "0.0.0.0/0" -# } - -# resource "aws_vpc_security_group_ingress_rule" "lb_sg_ingress_http_allow_8080" { -# security_group_id = aws_security_group.lb_sg.id -# description = "Allow inbound HTTP traffic on port 8080" -# ip_protocol = "tcp" -# from_port = 8080 -# to_port = 8080 -# cidr_ipv4 = "0.0.0.0/0" -# } - -# # Load Balancer Egress Rules -# resource "aws_vpc_security_group_egress_rule" "lb_sg_egress_allow_to_ecs_sg" { -# security_group_id = aws_security_group.lb_sg.id -# referenced_security_group_id = aws_security_group.ecs_sg.id -# description = "Allow all outbound traffic to ECS SG from Load Balancer SG" -# ip_protocol = "-1" -# } - -# # -# # ECS Security Group -# # -# resource "aws_security_group" "ecs_sg" { -# name = "${var.app_name}-ecs-sg-${var.environment}" -# vpc_id = data.aws_vpc.vpc.id -# description = "Security Group for ECS services" - -# tags = { -# Name = "${var.app_name}_ecs_sg_${var.environment}" -# } -# } - -# # ECS Ingress Rules -# # Remove ecs_sg_ingress_allow_icmp and ecs_sg_ingress_allow_ssh once the JASPER -# # is publicly accessible. These ingress rules is for tesing SG-SG connectivity using -# # EC2 Instance and EC2 Instance Connect Endpoint -# resource "aws_vpc_security_group_ingress_rule" "ecs_sg_ingress_allow_from_web_sg" { -# security_group_id = aws_security_group.ecs_sg.id -# referenced_security_group_id = data.aws_security_group.web_sg.id -# description = "Allow all inbound traffic from Web SG" -# ip_protocol = -1 -# } - -# resource "aws_vpc_security_group_ingress_rule" "ecs_sg_ingress_allow_from_lambda_sg" { -# security_group_id = aws_security_group.ecs_sg.id -# referenced_security_group_id = aws_security_group.lambda_sg.id -# description = "Allow all inbound traffic from Lambda SG" -# ip_protocol = -1 -# } - -# resource "aws_vpc_security_group_ingress_rule" "ecs_sg_ingress_allow_icmp" { -# security_group_id = aws_security_group.ecs_sg.id -# description = "Allow inbound ICMP traffic to ECS SG to allow pinging the Lambda SG" -# ip_protocol = "icmp" -# from_port = -1 -# to_port = -1 -# cidr_ipv4 = "0.0.0.0/0" -# } - -# resource "aws_vpc_security_group_ingress_rule" "ecs_sg_ingress_allow_ssh" { -# security_group_id = aws_security_group.ecs_sg.id -# description = "Allow inbound SSH traffic to ECS SG" -# ip_protocol = "tcp" -# from_port = 22 -# to_port = 22 -# cidr_ipv4 = data.aws_vpc.vpc.cidr_block -# } - -# # ECS Egress Rules -# resource "aws_vpc_security_group_egress_rule" "ecs_sg_egress_allow_to_anywhere" { -# security_group_id = aws_security_group.ecs_sg.id -# description = "Unrestricted" -# ip_protocol = "-1" -# cidr_ipv4 = "0.0.0.0/0" -# } - -# # -# # Lambda Security Group -# # -# resource "aws_security_group" "lambda_sg" { -# name = "${var.app_name}-lambda-sg-${var.environment}" -# vpc_id = data.aws_vpc.vpc.id -# description = "Security Group for Lambda functions" - -# tags = { -# Name = "${var.app_name}_lambda_sg_${var.environment}" -# } -# } - -# # Lambda Ingress Rules -# # Remove lambda_sg_ingress_allow_icmp and lambda_sg_ingress_allow_ssh once the JASPER -# # is publicly accessible. These ingress rules is for tesing SG-SG connectivity using -# # EC2 Instance and EC2 Instance Connect Endpoint -# resource "aws_vpc_security_group_ingress_rule" "lambda_sg_ingress_allow_from_ecs_sg" { -# security_group_id = aws_security_group.lambda_sg.id -# referenced_security_group_id = aws_security_group.ecs_sg.id -# description = "Allow all inbound traffic from ECS SG" -# ip_protocol = -1 -# } - -# resource "aws_vpc_security_group_ingress_rule" "lambda_sg_ingress_allow_icmp" { -# security_group_id = aws_security_group.lambda_sg.id -# description = "Allow inbound ICMP traffic to Lambda SG to allow pinging the ECS SG" -# ip_protocol = "icmp" -# from_port = -1 -# to_port = -1 -# cidr_ipv4 = "0.0.0.0/0" -# } - -# resource "aws_vpc_security_group_ingress_rule" "lambda_sg_ingress_allow_ssh" { -# security_group_id = aws_security_group.lambda_sg.id -# description = "Allow inbound SSH traffic to Lambda SG" -# ip_protocol = "tcp" -# from_port = 22 -# to_port = 22 -# cidr_ipv4 = data.aws_vpc.vpc.cidr_block -# } - -# # Lambda Egress Rules -# resource "aws_vpc_security_group_egress_rule" "lambda_sg_egress_allow_to_anywhere" { -# security_group_id = aws_security_group.lambda_sg.id -# description = "Unrestricted" -# ip_protocol = "-1" -# cidr_ipv4 = "0.0.0.0/0" -# } diff --git a/infrastructure/cloud/modules/networking/targetgroups.tf b/infrastructure/cloud/modules/networking/targetgroups.tf deleted file mode 100644 index cf6faff2..00000000 --- a/infrastructure/cloud/modules/networking/targetgroups.tf +++ /dev/null @@ -1,42 +0,0 @@ -# Web -resource "aws_lb_target_group" "web_target_group" { - name = "${var.app_name}-web-tg-${var.environment}" - port = 8080 - protocol = "HTTPS" - vpc_id = data.aws_vpc.vpc.id - target_type = "ip" - deregistration_delay = 5 - - health_check { - protocol = "HTTPS" - path = "/" - port = 8080 - interval = 30 - timeout = 5 - enabled = true - healthy_threshold = 3 - unhealthy_threshold = 3 - matcher = "200" - } -} - -# API -resource "aws_lb_target_group" "api_target_group" { - name = "${var.app_name}-api-tg-${var.environment}" - port = 5000 - protocol = "HTTP" - vpc_id = data.aws_vpc.vpc.id - target_type = "ip" - deregistration_delay = 5 - - health_check { - path = "/api/test/headers" - port = 5000 - interval = 30 - timeout = 5 - enabled = true - healthy_threshold = 3 - unhealthy_threshold = 3 - matcher = "200" - } -} diff --git a/infrastructure/cloud/modules/networking/vpc.tf b/infrastructure/cloud/modules/networking/vpc.tf deleted file mode 100644 index 34f36776..00000000 --- a/infrastructure/cloud/modules/networking/vpc.tf +++ /dev/null @@ -1,3 +0,0 @@ -data "aws_vpc" "vpc" { - id = var.vpc_id -} diff --git a/infrastructure/cloud/modules/security/acm.tf b/infrastructure/cloud/modules/security/acm.tf deleted file mode 100644 index e8369d8d..00000000 --- a/infrastructure/cloud/modules/security/acm.tf +++ /dev/null @@ -1,5 +0,0 @@ -data "aws_acm_certificate" "default_lb_cert" { - domain = var.cert_domain_name - most_recent = true - statuses = ["ISSUED"] -} diff --git a/infrastructure/cloud/modules/security/variables.tf b/infrastructure/cloud/modules/security/variables.tf deleted file mode 100644 index 651ba3d2..00000000 --- a/infrastructure/cloud/modules/security/variables.tf +++ /dev/null @@ -1,44 +0,0 @@ -variable "kms_key_name" { - description = "Name of KMS key" - type = string -} - -variable "app_name" { - description = "The name of the application" - type = string -} - -variable "environment" { - description = "The AWS environment to deploy to" - type = string -} - -variable "ecs_web_td_log_group_arn" { - description = "The ECS Web Task Definition Log Group ARN" - type = string -} - -variable "ecs_api_td_log_group_arn" { - description = "The ECS API Task Definition Log Group ARN" - type = string -} - -variable "ecr_repository_arn" { - description = "The ECR Repository ARN" - type = string -} - -variable "openshift_iam_user" { - description = "Openshift IAM Username" - type = string -} - -variable "iam_user_table_name" { - description = "The BCGOV IAM User DynamoDb table name" - type = string -} - -variable "cert_domain_name" { - description = "The BCGov provisioned certificate domain name" - type = string -}