From c754137cd0cffafdef1377035399c0e6159312bf Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 4 Aug 2025 09:46:50 -0700 Subject: [PATCH 001/217] feat: adding entrypoint --- .trunk/.gitignore | 9 +++++++ .trunk/configs/.hadolint.yaml | 4 +++ .trunk/configs/.markdownlint.yaml | 2 ++ .trunk/configs/.shellcheckrc | 7 +++++ .trunk/configs/.yamllint.yaml | 7 +++++ .trunk/configs/svgo.config.mjs | 14 ++++++++++ .trunk/trunk.yaml | 42 ++++++++++++++++++++++++++++++ build/docker/Dockerfile.backend | 8 +++--- build/docker/entrypoint-migrate.sh | 5 ++++ build/docker/entrypoint-run.sh | 2 ++ 10 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 .trunk/.gitignore create mode 100644 .trunk/configs/.hadolint.yaml create mode 100644 .trunk/configs/.markdownlint.yaml create mode 100644 .trunk/configs/.shellcheckrc create mode 100644 .trunk/configs/.yamllint.yaml create mode 100644 .trunk/configs/svgo.config.mjs create mode 100644 .trunk/trunk.yaml create mode 100644 build/docker/entrypoint-migrate.sh create mode 100644 build/docker/entrypoint-run.sh diff --git a/.trunk/.gitignore b/.trunk/.gitignore new file mode 100644 index 00000000000..15966d087eb --- /dev/null +++ b/.trunk/.gitignore @@ -0,0 +1,9 @@ +*out +*logs +*actions +*notifications +*tools +plugins +user_trunk.yaml +user.yaml +tmp diff --git a/.trunk/configs/.hadolint.yaml b/.trunk/configs/.hadolint.yaml new file mode 100644 index 00000000000..98bf0cd2ee9 --- /dev/null +++ b/.trunk/configs/.hadolint.yaml @@ -0,0 +1,4 @@ +# Following source doesn't work in most setups +ignored: + - SC1090 + - SC1091 diff --git a/.trunk/configs/.markdownlint.yaml b/.trunk/configs/.markdownlint.yaml new file mode 100644 index 00000000000..b40ee9d7a7a --- /dev/null +++ b/.trunk/configs/.markdownlint.yaml @@ -0,0 +1,2 @@ +# Prettier friendly markdownlint config (all formatting rules disabled) +extends: markdownlint/style/prettier diff --git a/.trunk/configs/.shellcheckrc b/.trunk/configs/.shellcheckrc new file mode 100644 index 00000000000..8c7b1ada8a3 --- /dev/null +++ b/.trunk/configs/.shellcheckrc @@ -0,0 +1,7 @@ +enable=all +source-path=SCRIPTDIR +disable=SC2154 + +# If you're having issues with shellcheck following source, disable the errors via: +# disable=SC1090 +# disable=SC1091 diff --git a/.trunk/configs/.yamllint.yaml b/.trunk/configs/.yamllint.yaml new file mode 100644 index 00000000000..184e251f8de --- /dev/null +++ b/.trunk/configs/.yamllint.yaml @@ -0,0 +1,7 @@ +rules: + quoted-strings: + required: only-when-needed + extra-allowed: ["{|}"] + key-duplicates: {} + octal-values: + forbid-implicit-octal: true diff --git a/.trunk/configs/svgo.config.mjs b/.trunk/configs/svgo.config.mjs new file mode 100644 index 00000000000..f403e04c120 --- /dev/null +++ b/.trunk/configs/svgo.config.mjs @@ -0,0 +1,14 @@ +export default { + plugins: [ + { + name: "preset-default", + params: { + overrides: { + removeViewBox: false, // https://github.com/svg/svgo/issues/1128 + sortAttrs: true, + removeOffCanvasPaths: true, + }, + }, + }, + ], +} diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml new file mode 100644 index 00000000000..ed9ec1ed064 --- /dev/null +++ b/.trunk/trunk.yaml @@ -0,0 +1,42 @@ +# This file controls the behavior of Trunk: https://docs.trunk.io/cli +# To learn more about the format of this file, see https://docs.trunk.io/reference/trunk-yaml +version: 0.1 +cli: + version: 1.24.0 +# Trunk provides extensibility via plugins. (https://docs.trunk.io/plugins) +plugins: + sources: + - id: trunk + ref: v1.7.1 + uri: https://github.com/trunk-io/plugins +# Many linters and tools depend on runtimes - configure them here. (https://docs.trunk.io/runtimes) +runtimes: + enabled: + - go@1.21.0 + - node@22.16.0 + - python@3.10.8 +# This is the section where you manage your linters. (https://docs.trunk.io/check/configuration) +lint: + enabled: + - actionlint@1.7.7 + - checkov@3.2.457 + - eslint@8.57.0 + - git-diff-check + - hadolint@2.12.1-beta + - markdownlint@0.45.0 + - osv-scanner@2.0.3 + - oxipng@9.1.5 + - prettier@3.6.2 + - shellcheck@0.10.0 + - shfmt@3.6.0 + - svgo@4.0.0 + - taplo@0.9.3 + - trufflehog@3.90.2 + - yamllint@1.37.1 +actions: + disabled: + - trunk-announce + - trunk-check-pre-push + - trunk-fmt-pre-commit + enabled: + - trunk-upgrade-available diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index 11e84497135..9fa09b3111a 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -130,8 +130,9 @@ RUN chmod 777 ./src/temp USER node RUN yarn prisma generate +COPY --chown=node:node /usr/local/bin ./build/docker/entrypoint-run.sh -CMD yarn start:prod +ENTRYPOINT /usr/local/bin/entrypoint-run.sh ##################### ##### NEW STAGE ##### @@ -162,7 +163,4 @@ COPY --from=source --chown=node:node /app/api/scripts ./scripts ## This CMD should be run as the node user USER node -# We need to skip all migrations that are just for local development (changes already exist in deployed environment) -CMD if [ "$SKIP_MIGRATIONS" = TRUE ]; then yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ - else yarn db:migration:run; \ - fi +ENTRYPOINT /usr/local/bin/entrypoint-migrate.sh diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh new file mode 100644 index 00000000000..241a38af1fd --- /dev/null +++ b/build/docker/entrypoint-migrate.sh @@ -0,0 +1,5 @@ +export DATABASE_URL="postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" +# We need to skip all migrations that are just for local development (changes already exist in deployed environment) +CMD if [ "$SKIP_MIGRATIONS" = TRUE ]; then yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ + else yarn db:migration:run; \ + fi \ No newline at end of file diff --git a/build/docker/entrypoint-run.sh b/build/docker/entrypoint-run.sh new file mode 100644 index 00000000000..84db390a11b --- /dev/null +++ b/build/docker/entrypoint-run.sh @@ -0,0 +1,2 @@ +export DATABASE_URL="postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" +yarn start:prod \ No newline at end of file From 3e43ae20e1882d3be08e9f58a62e6aa1992b618a Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 4 Aug 2025 09:54:40 -0700 Subject: [PATCH 002/217] fix: fixing copy command --- build/docker/Dockerfile.backend | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index 9fa09b3111a..b406f0c6c25 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -130,9 +130,9 @@ RUN chmod 777 ./src/temp USER node RUN yarn prisma generate -COPY --chown=node:node /usr/local/bin ./build/docker/entrypoint-run.sh +COPY --chown=node:node ./build/docker/entrypoint-run.sh /app/ -ENTRYPOINT /usr/local/bin/entrypoint-run.sh +ENTRYPOINT /app/entrypoint-run.sh ##################### ##### NEW STAGE ##### @@ -162,5 +162,5 @@ COPY --from=source --chown=node:node /app/api/scripts ./scripts ## This CMD should be run as the node user USER node - -ENTRYPOINT /usr/local/bin/entrypoint-migrate.sh +COPY --chown=node:node ./build/docker/entrypoint-migrate.sh /app/ +ENTRYPOINT /app/entrypoint-migrate.sh From 9f15ba4c21d104534b98d593333453eb076a48a6 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 4 Aug 2025 15:23:40 -0700 Subject: [PATCH 003/217] feat: modifying for cdk build --- ci/buildspec/migrate_stop_backend.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index ea82d398cf8..747467cc4ba 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -6,12 +6,12 @@ version: 0.2 env: shell: bash secrets-manager: - PGUSER: "${DB_CREDS_ARN}:user" + PGUSER: "${DB_CREDS_ARN}:username" PGPASSWORD: "${DB_CREDS_ARN}:password" PGHOST: "${DB_CREDS_ARN}:host" PGDATABASE: "${DB_CREDS_ARN}:db_name" PGPORT: "${DB_CREDS_ARN}:port" - DATABASE_URL: "${DB_CREDS_ARN}:uri" + phases: pre_build: commands: @@ -61,6 +61,3 @@ phases: --env PARTNERS_PORTAL_URL="${PARTNERS_PORTAL_URL:-http://localhost:3001/not-used}" --env SKIP_MIGRATIONS=FALSE "${MIGRATION_IMAGE}" sh -c "yarn ${MIGRATION_CMD}" - # Start the backend back up with the previous number of desired containers - - aws ecs update-service --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_BACKEND_SERVICE}" --desired-count $BACKEND_COUNT - - if [[ ! -z "${ECS_PRIVATE_BACKEND_SERVICE}" ]]; then aws ecs update-service --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_PRIVATE_BACKEND_SERVICE}" --desired-count $BACKEND_COUNT; fi From 7216f9d0f294c0f121d420b8bd96d5b87b3bd459 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 5 Aug 2025 10:18:38 -0700 Subject: [PATCH 004/217] fix: static database name --- ci/buildspec/migrate_stop_backend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 747467cc4ba..66b1290c7b0 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -9,7 +9,7 @@ env: PGUSER: "${DB_CREDS_ARN}:username" PGPASSWORD: "${DB_CREDS_ARN}:password" PGHOST: "${DB_CREDS_ARN}:host" - PGDATABASE: "${DB_CREDS_ARN}:db_name" + PGDATABASE: "doorway" PGPORT: "${DB_CREDS_ARN}:port" phases: From ab30418c0b45cba447ea998ab69803631952fc08 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 5 Aug 2025 11:11:23 -0700 Subject: [PATCH 005/217] fix: fixing database url --- ci/buildspec/migrate_stop_backend.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 66b1290c7b0..261d77812ff 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -9,7 +9,6 @@ env: PGUSER: "${DB_CREDS_ARN}:username" PGPASSWORD: "${DB_CREDS_ARN}:password" PGHOST: "${DB_CREDS_ARN}:host" - PGDATABASE: "doorway" PGPORT: "${DB_CREDS_ARN}:port" phases: @@ -46,8 +45,7 @@ phases: --env PGPASSWORD="${PGPASSWORD}" --env PGHOST="${PGHOST}" --env PGDATABASE=template1 - --env PGPORT="${PGPORT}" - --env DATABASE_URL="${DATABASE_URL}" + --env PGPORT="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/doorway" --env CLOUDINARY_CLOUD_NAME="${CLOUDINARY_CLOUD_NAME}" --env LISTINGS_QUERY="${LISTINGS_QUERY}" --env FILE_SERVICE="${FILE_SERVICE:-cloudinary}" From 1421c43932fd5b0f3db52a94c4ab5c0cbfcff4dd Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 6 Aug 2025 12:59:40 -0700 Subject: [PATCH 006/217] fix: adding dbname --- ci/buildspec/migrate_stop_backend.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 261d77812ff..a1f69b0dd31 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -10,6 +10,7 @@ env: PGPASSWORD: "${DB_CREDS_ARN}:password" PGHOST: "${DB_CREDS_ARN}:host" PGPORT: "${DB_CREDS_ARN}:port" + PGDATABASE: "${DB_CREDS_ARN}:dbname" phases: pre_build: @@ -45,7 +46,7 @@ phases: --env PGPASSWORD="${PGPASSWORD}" --env PGHOST="${PGHOST}" --env PGDATABASE=template1 - --env PGPORT="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/doorway" + --env DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" --env CLOUDINARY_CLOUD_NAME="${CLOUDINARY_CLOUD_NAME}" --env LISTINGS_QUERY="${LISTINGS_QUERY}" --env FILE_SERVICE="${FILE_SERVICE:-cloudinary}" From c4042078828f0d65158d5f21445b1d3b68feeb61 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 6 Aug 2025 13:00:33 -0700 Subject: [PATCH 007/217] fix: pgdatabase --- ci/buildspec/migrate_stop_backend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index a1f69b0dd31..4a1184952fb 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -45,7 +45,7 @@ phases: --env PGUSER="${PGUSER}" --env PGPASSWORD="${PGPASSWORD}" --env PGHOST="${PGHOST}" - --env PGDATABASE=template1 + --env PGDATABASE=${PGDATABASE} --env DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" --env CLOUDINARY_CLOUD_NAME="${CLOUDINARY_CLOUD_NAME}" --env LISTINGS_QUERY="${LISTINGS_QUERY}" From 2fb2d2b76e7a7f9a777d88bff404a4a59f801545 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 7 Aug 2025 15:22:06 -0700 Subject: [PATCH 008/217] fix: chowning --- build/docker/Dockerfile.backend | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index b406f0c6c25..90cab727ff1 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -131,7 +131,7 @@ USER node RUN yarn prisma generate COPY --chown=node:node ./build/docker/entrypoint-run.sh /app/ - +RUN chown 755 /app/entrypoint-run.sh ENTRYPOINT /app/entrypoint-run.sh ##################### @@ -163,4 +163,5 @@ COPY --from=source --chown=node:node /app/api/scripts ./scripts ## This CMD should be run as the node user USER node COPY --chown=node:node ./build/docker/entrypoint-migrate.sh /app/ +RUN chown 755 /app/entrypoint-migrate.sh ENTRYPOINT /app/entrypoint-migrate.sh From 1c6635105deed57ec1149f7fb1cdd9b70758ab0f Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 7 Aug 2025 15:27:12 -0700 Subject: [PATCH 009/217] fix: chown --- build/docker/Dockerfile.backend | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index 90cab727ff1..04657690584 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -131,7 +131,7 @@ USER node RUN yarn prisma generate COPY --chown=node:node ./build/docker/entrypoint-run.sh /app/ -RUN chown 755 /app/entrypoint-run.sh +RUN chmod 755 /app/entrypoint-run.sh ENTRYPOINT /app/entrypoint-run.sh ##################### @@ -163,5 +163,5 @@ COPY --from=source --chown=node:node /app/api/scripts ./scripts ## This CMD should be run as the node user USER node COPY --chown=node:node ./build/docker/entrypoint-migrate.sh /app/ -RUN chown 755 /app/entrypoint-migrate.sh +RUN chmod 755 /app/entrypoint-migrate.sh ENTRYPOINT /app/entrypoint-migrate.sh From 48fcbf5c1baee748c0fdbb78e4b0f632edf00f1d Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 7 Aug 2025 15:39:42 -0700 Subject: [PATCH 010/217] fix: fixing entrypoints --- build/docker/entrypoint-migrate.sh | 9 ++++++--- build/docker/entrypoint-run.sh | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh index 241a38af1fd..b96315c6db6 100644 --- a/build/docker/entrypoint-migrate.sh +++ b/build/docker/entrypoint-migrate.sh @@ -1,5 +1,8 @@ +#!/bin/sh export DATABASE_URL="postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" # We need to skip all migrations that are just for local development (changes already exist in deployed environment) -CMD if [ "$SKIP_MIGRATIONS" = TRUE ]; then yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ - else yarn db:migration:run; \ - fi \ No newline at end of file +if [ "$SKIP_MIGRATIONS" = TRUE ]; then + yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run +else + yarn db:migration:run +fi diff --git a/build/docker/entrypoint-run.sh b/build/docker/entrypoint-run.sh index 84db390a11b..5ee03b55933 100644 --- a/build/docker/entrypoint-run.sh +++ b/build/docker/entrypoint-run.sh @@ -1,2 +1,3 @@ +#!/bin/sh export DATABASE_URL="postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" -yarn start:prod \ No newline at end of file +yarn start:prod From f21478f549ef1794e9660a2f399b9ec6dc843bd0 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 8 Aug 2025 15:44:15 -0700 Subject: [PATCH 011/217] fix: changing migration command --- build/docker/entrypoint-migrate.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh index b96315c6db6..c29df6b9890 100644 --- a/build/docker/entrypoint-migrate.sh +++ b/build/docker/entrypoint-migrate.sh @@ -1,8 +1,3 @@ #!/bin/sh export DATABASE_URL="postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" -# We need to skip all migrations that are just for local development (changes already exist in deployed environment) -if [ "$SKIP_MIGRATIONS" = TRUE ]; then - yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run -else - yarn db:migration:run -fi +yarn db:reseed:ci From d7e43a18427253532dcdca6835f04179f0468572 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 15 Aug 2025 11:55:45 -0700 Subject: [PATCH 012/217] feat: cdk pipeline --- cdk/.gitignore | 8 + cdk/.npmignore | 6 + cdk/README.md | 14 + cdk/bin/doorway-cdk.ts | 11 + cdk/cdk.context.json | 3 + cdk/cdk.json | 99 + cdk/jest.config.js | 8 + .../doorway-build-pipeline-stack.ts | 111 + .../build-pipeline/doorway-docker-build.ts | 98 + cdk/lib/doorway-ecs-services-stack.ts | 222 + cdk/lib/doorway-stack-props.ts | 5 + cdk/lib/doorway_api_service-stack.ts | 339 ++ cdk/package-lock.json | 4422 +++++++++++++++++ cdk/package.json | 26 + cdk/test/cdk.test.ts | 16 + cdk/tsconfig.json | 24 + 16 files changed, 5412 insertions(+) create mode 100644 cdk/.gitignore create mode 100644 cdk/.npmignore create mode 100644 cdk/README.md create mode 100644 cdk/bin/doorway-cdk.ts create mode 100644 cdk/cdk.context.json create mode 100644 cdk/cdk.json create mode 100644 cdk/jest.config.js create mode 100644 cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts create mode 100644 cdk/lib/build-pipeline/doorway-docker-build.ts create mode 100644 cdk/lib/doorway-ecs-services-stack.ts create mode 100644 cdk/lib/doorway-stack-props.ts create mode 100644 cdk/lib/doorway_api_service-stack.ts create mode 100644 cdk/package-lock.json create mode 100644 cdk/package.json create mode 100644 cdk/test/cdk.test.ts create mode 100644 cdk/tsconfig.json diff --git a/cdk/.gitignore b/cdk/.gitignore new file mode 100644 index 00000000000..f60797b6a91 --- /dev/null +++ b/cdk/.gitignore @@ -0,0 +1,8 @@ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/cdk/.npmignore b/cdk/.npmignore new file mode 100644 index 00000000000..c1d6d45dcf3 --- /dev/null +++ b/cdk/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/cdk/README.md b/cdk/README.md new file mode 100644 index 00000000000..80c3df35e1b --- /dev/null +++ b/cdk/README.md @@ -0,0 +1,14 @@ +# Welcome to your CDK TypeScript project + +This is a blank project for CDK development with TypeScript. + +The `cdk.json` file tells the CDK Toolkit how to execute your app. + +## Useful commands + +- `npm run build` compile typescript to js +- `npm run watch` watch for changes and compile +- `npm run test` perform the jest unit tests +- `npx cdk deploy` deploy this stack to your default AWS account/region +- `npx cdk diff` compare deployed stack with current state +- `npx cdk synth` emits the synthesized CloudFormation template diff --git a/cdk/bin/doorway-cdk.ts b/cdk/bin/doorway-cdk.ts new file mode 100644 index 00000000000..487bef28110 --- /dev/null +++ b/cdk/bin/doorway-cdk.ts @@ -0,0 +1,11 @@ +#!/usr/bin/env node +import * as cdk from "aws-cdk-lib" + +import { DoorwayBuildPipelineStack } from "../lib/build-pipeline/doorway-build-pipeline-stack" + +const app = new cdk.App() + +new DoorwayBuildPipelineStack(app, "DoorwayBuildPipelineStack", { + githubSecret: "mtc/githubSecret", + dockerHubSecret: "mtc/dockerHub", +}) diff --git a/cdk/cdk.context.json b/cdk/cdk.context.json new file mode 100644 index 00000000000..b114dbfe135 --- /dev/null +++ b/cdk/cdk.context.json @@ -0,0 +1,3 @@ +{ + "acknowledged-issue-numbers": [34892] +} diff --git a/cdk/cdk.json b/cdk/cdk.json new file mode 100644 index 00000000000..3b1412ffe4f --- /dev/null +++ b/cdk/cdk.json @@ -0,0 +1,99 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/doorway-cdk.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/aws-kms:applyImportedAliasPermissionsToPrincipal": true, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, + "@aws-cdk/aws-efs:denyAnonymousAccess": true, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true, + "@aws-cdk/aws-eks:nodegroupNameAttribute": true, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, + "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false, + "@aws-cdk/core:explicitStackTags": true, + "@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": false, + "@aws-cdk/aws-ecs:disableEcsImdsBlocking": true, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true, + "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true, + "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true, + "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true, + "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true, + "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true, + "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true, + "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true, + "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true, + "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true, + "@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true, + "@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": true, + "@aws-cdk/core:enableAdditionalMetadataCollection": true, + "@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": false, + "@aws-cdk/aws-s3:setUniqueReplicationRoleName": true, + "@aws-cdk/aws-events:requireEventBusPolicySid": true, + "@aws-cdk/core:aspectPrioritiesMutating": true, + "@aws-cdk/aws-dynamodb:retainTableReplica": true, + "@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": true, + "@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": true, + "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true, + "@aws-cdk/aws-s3:publicAccessBlockedByDefault": true, + "@aws-cdk/aws-lambda:useCdkManagedLogGroup": true + } +} \ No newline at end of file diff --git a/cdk/jest.config.js b/cdk/jest.config.js new file mode 100644 index 00000000000..93b648455f1 --- /dev/null +++ b/cdk/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: "node", + roots: ["/test"], + testMatch: ["**/*.test.ts"], + transform: { + "^.+\\.tsx?$": "ts-jest", + }, +} diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts new file mode 100644 index 00000000000..8e9677a62fd --- /dev/null +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -0,0 +1,111 @@ +import { Stack, StackProps } from "aws-cdk-lib" +import { Artifact, Pipeline } from "aws-cdk-lib/aws-codepipeline" +import { GitHubSourceAction } from "aws-cdk-lib/aws-codepipeline-actions" +import { PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import { Secret } from "aws-cdk-lib/aws-secretsmanager" +import { Construct } from "constructs" + +import { DoorwayDockerBuild } from "./doorway-docker-build" + +export class DoorwayBuildPipelineStack extends Stack { + constructor(scope: Construct, id: string, props: PipelineProps) { + super(scope, id, props) + + // No need to create a repository for docker-hub if using pull-through cache + // The pull-through cache repository should already be configured in the AWS console + + const pipelineRole = new Role(this, "doorway-app-pipeline-role", { + assumedBy: new ServicePrincipal("codepipeline.amazonaws.com"), + }) + pipelineRole.addToPolicy( + new PolicyStatement({ + actions: [ + "cloudformation:*", + "ec2:*", + "ssm:*", + "codebuild:*", + "logs:*", + "iam:AssumeRole", + "iam:PassRole", + ], + resources: ["*"], + }) + ) + + const dockerSecret = Secret.fromSecretNameV2( + this, + "dockerSecret", + props.dockerHubSecret + ).secretArn + + const githubSecret = Secret.fromSecretNameV2( + this, + "githubSecret", + props.githubSecret + ).secretValue + const pipeline = new Pipeline(this, "doorway-app-pipeline", { + role: pipelineRole, + pipelineName: "doorway-app-pipeline", + }) + const sourceArtifact = new Artifact("SourceArtifact") + const doorwaySource = new GitHubSourceAction({ + actionName: "Source", + oauthToken: githubSecret, + owner: "metrotranscom", + repo: "doorway", + branch: "feat/tf_to_cdk", + output: sourceArtifact, + }) + const configSourceArtifact = new Artifact("ConfigSourceArtifact") + const doorwayConfigSource = new GitHubSourceAction({ + actionName: "ConfigSource", + oauthToken: githubSecret, + owner: "metrotranscom", + repo: "doorway-config", + branch: "feat/cdk-terraform", + output: configSourceArtifact, + }) + + pipeline.addStage({ + stageName: "Source", + actions: [doorwaySource, doorwayConfigSource], + }) + pipeline.addStage({ + stageName: "Build", + actions: [ + new DoorwayDockerBuild(this, `doorway-backend`, { + buildspec: "../ci/buildspec/build_backend.yml", + imageName: "backend", + source: sourceArtifact, + configSource: configSourceArtifact, + dockerHubSecret: dockerSecret, + }).action, + new DoorwayDockerBuild(this, "doorway-import-listings", { + buildspec: "../ci/buildspec/build_import_listings.yml", + imageName: "import-listings", + source: sourceArtifact, + configSource: configSourceArtifact, + dockerHubSecret: dockerSecret, + }).action, + new DoorwayDockerBuild(this, `doorway-partners`, { + buildspec: "../ci/buildspec/build_partners.yml", + imageName: "partners", + source: sourceArtifact, + configSource: configSourceArtifact, + dockerHubSecret: dockerSecret, + }).action, + new DoorwayDockerBuild(this, "doorway-public", { + buildspec: "../ci/buildspec/build_public.yml", + imageName: "public", + source: sourceArtifact, + configSource: configSourceArtifact, + dockerHubSecret: dockerSecret, + }).action, + ], + }) + } +} +export interface PipelineProps extends StackProps { + githubSecret: string + dockerHubSecret: string +} diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts new file mode 100644 index 00000000000..c6ad320aea7 --- /dev/null +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -0,0 +1,98 @@ +import { Stack } from "aws-cdk-lib" +import { BuildSpec, ComputeType, PipelineProject } from "aws-cdk-lib/aws-codebuild" +import { Artifact } from "aws-cdk-lib/aws-codepipeline" +import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" +import { Repository } from "aws-cdk-lib/aws-ecr" +import { PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import fs from "fs" +import * as YAML from "yaml" + +export interface DoorwayDockerBuildProps { + buildspec: string + imageName: string + source: Artifact + configSource?: Artifact + dockerHubSecret: string +} + +export class DoorwayDockerBuild { + public readonly action: CodeBuildAction + + constructor(stack: Stack, id: string, props: DoorwayDockerBuildProps) { + // Create ECR repository + const repo = new Repository(stack, `${id}-ECRRepository`, { + repositoryName: `doorway/${props.imageName}`, + }) + const role = new Role(stack, `${id}-doorway-app-build-role`, { + assumedBy: new ServicePrincipal("codebuild.amazonaws.com"), + managedPolicies: [ + { + managedPolicyArn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess", + }, + { + managedPolicyArn: "arn:aws:iam::aws:policy/AmazonS3FullAccess", + }, + ], + inlinePolicies: { + ECRPolicies: new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: [ + "ecr:BatchGetImage", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchCheckLayerAvailability", + ], + resources: [`arn:aws:ecr:${stack.region}:${stack.account}:repository/*`], + }), + new PolicyStatement({ + actions: ["secretsmanager:GetSecretValue"], + resources: [ + `arn:aws:secretsmanager:${stack.region}:${stack.account}:secret:mtc/dockerHub*`, + ], + }), + new PolicyStatement({ + actions: [ + "cloudformation:*", + "ec2:*", + "ssm:*", + "codebuild:*", + "logs:*", + "iam:AssumeRole", + "iam:PassRole", + ], + resources: ["*"], + }), + ], + }), + }, + }) + + // Grant permissions to the provided role + repo.grantPullPush(role) + + // Create the CodeBuild project + const project = new PipelineProject(stack, `${id}-Project`, { + buildSpec: BuildSpec.fromObject(YAML.parse(fs.readFileSync(props.buildspec, "utf8"))), + environment: { + computeType: ComputeType.LARGE, + }, + environmentVariables: { + ECR_REGION: { value: stack.region }, + ECR_ACCOUNT_ID: { value: stack.account }, + ECR_NAMESPACE: { value: "doorway" }, + IMAGE_NAME: { value: props.imageName }, + DOCKER_HUB_SECRET_ARN: { value: props.dockerHubSecret }, + }, + role: role, + }) + + // Create the CodeBuild action + this.action = new CodeBuildAction({ + actionName: `${id}-DockerBuild`, + input: props.source, + extraInputs: props.configSource ? [props.configSource] : undefined, + outputs: [new Artifact(`${id}-BuildOutput`)], + project, + }) + } +} diff --git a/cdk/lib/doorway-ecs-services-stack.ts b/cdk/lib/doorway-ecs-services-stack.ts new file mode 100644 index 00000000000..05b6fceccad --- /dev/null +++ b/cdk/lib/doorway-ecs-services-stack.ts @@ -0,0 +1,222 @@ +import { Fn, Stack } from "aws-cdk-lib" +import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager" +import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" +import { Repository } from "aws-cdk-lib/aws-ecr" +import { AwsLogDriver, Cluster, ContainerImage, Secret } from "aws-cdk-lib/aws-ecs" +import { ApplicationLoadBalancedFargateService } from "aws-cdk-lib/aws-ecs-patterns" +import { SslPolicy } from "aws-cdk-lib/aws-elasticloadbalancingv2" +import { ManagedPolicy, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import { LogGroup } from "aws-cdk-lib/aws-logs" +import { PublicHostedZone } from "aws-cdk-lib/aws-route53" +import * as secret from "aws-cdk-lib/aws-secretsmanager" +import { StringParameter } from "aws-cdk-lib/aws-ssm" +import { Construct } from "constructs" + +import { DoorwayStackProps } from "./doorway-stack-props" + +export class DoorwayEcsServicesStack extends Stack { + constructor(scope: Construct, id: string, props: DoorwayStackProps) { + super(scope, id, props) + // Create the execution role for the doorway API service + const executionRole = new Role(this, "executionRole", { + assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), + }) + executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy") + ) + executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess") + ) + executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("AmazonRDSReadOnlyAccess") + ) + executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly") + ) + const hostedZone = PublicHostedZone.fromHostedZoneAttributes( + this, + `doorway-public-hosted-zone-${props.environment}`, + { + hostedZoneId: StringParameter.valueForStringParameter(this, `/doorway/public-hosted-zone`), + zoneName: `housingbayarea.mtc.ca.gov`, + } + ) + const cert = new Certificate(this, "doorwayPublicCert", { + domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, + validation: { + method: CertificateValidation.fromDns().method, + props: { + hostedZone: hostedZone, + }, + }, + }) + + new ApplicationLoadBalancedFargateService( + this, + `doorway-public-portal-service-${props.environment}`, + { + cluster: Cluster.fromClusterAttributes(this, `doorway-ecs-cluster-${props.environment}`, { + clusterName: `doorway-ecs-cluster-${props.environment}`, + vpc: Vpc.fromVpcAttributes(this, "vpc", { + vpcId: Fn.importValue(`doorway-vpc-id-${props.environment}`), + availabilityZones: Fn.importValue(`doorway-azs-${props.environment}`).split(","), + publicSubnetIds: [ + Fn.importValue(`doorway-public-subnet-1-${props.environment}`), + Fn.importValue(`doorway-public-subnet-2-${props.environment}`), + ], + }), + securityGroups: [ + SecurityGroup.fromSecurityGroupId( + this, + `doorway-app-sg-${props.environment}`, + Fn.importValue(`doorway-app-sg-${props.environment}`) + ), + ], + }), + taskSubnets: { + subnets: [ + Subnet.fromSubnetId( + this, + `doorway-app-subnet-1-${props.environment}`, + Fn.importValue(`doorway-app-subnet-1-${props.environment}`) + ), + Subnet.fromSubnetId( + this, + `doorway-app-subnet-2-${props.environment}`, + Fn.importValue(`doorway-app-subnet-2-${props.environment}`) + ), + ], + }, + + assignPublicIp: true, + publicLoadBalancer: true, + taskImageOptions: { + image: ContainerImage.fromEcrRepository( + Repository.fromRepositoryName( + this, + `doorway-public-portal-repo-${props.environment}`, + `doorway/public` + ), + "run-f21478f5" + ), + enableLogging: true, + containerPort: 3000, + executionRole: executionRole, + taskRole: executionRole, + secrets: { + BLOOM_API_BASE: Secret.fromSsmParameter( + StringParameter.fromStringParameterName( + this, + "BLOOM_API_BASE", + `/doorway/${props.environment}/public-portal/BLOOM_API_BASE` + ) + ), + CACHE_REVALIDATE: Secret.fromSsmParameter( + StringParameter.fromStringParameterName( + this, + "CACHE_REVALIDATE", + `/doorway/${props.environment}/public-portal/CACHE_REVALIDATE` + ) + ), + GOOGLE_API_KEY: Secret.fromSecretsManager( + secret.Secret.fromSecretNameV2(this, "googleApiKey", "/doorway/GOOGLE_API_KEY") + ), + GOOGLE_MAPS_API_KEY: Secret.fromSecretsManager( + secret.Secret.fromSecretNameV2( + this, + "googleMapsApiKey", + "/doorway/GOOGLE_MAPS_API_KEY" + ) + ), + GOOGLE_MAPS_MAP_ID: Secret.fromSecretsManager( + secret.Secret.fromSecretNameV2(this, "googleMapsMapId", "/doorway/GOOGLE_MAPS_MAP_ID") + ), + GTM_KEY: Secret.fromSsmParameter( + StringParameter.fromStringParameterName( + this, + "GTM_KEY", + `/doorway/${props.environment}/public-portal/GTM_KEY` + ) + ), + IDLE_TIMEOUT: Secret.fromSsmParameter( + StringParameter.fromStringParameterName( + this, + "IDLE_TIMEOUT", + `/doorway/${props.environment}/public-portal/IDLE_TIMEOUT` + ) + ), + JURISDICTION_NAME: Secret.fromSsmParameter( + StringParameter.fromStringParameterName( + this, + "JURISDICTION_NAME", + `/doorway/${props.environment}/public-portal/JURISDICTION_NAME` + ) + ), + LANGUAGES: Secret.fromSsmParameter( + StringParameter.fromStringParameterName( + this, + "LANGUAGES", + `/doorway/${props.environment}/public-portal/LANGUAGES` + ) + ), + LISTINGS_QUERY: Secret.fromSsmParameter( + StringParameter.fromStringParameterName( + this, + "LISTINGS_QUERY", + `/doorway/${props.environment}/public-portal/LISTINGS_QUERY` + ) + ), + NEXTJS_PORT: Secret.fromSsmParameter( + StringParameter.fromStringParameterName( + this, + "NEXTJS_PORT", + `/doorway/${props.environment}/public-portal/NEXTJS_PORT` + ) + ), + NOTIFICATIONS_SIGN_UP_URL: Secret.fromSsmParameter( + StringParameter.fromStringParameterName( + this, + "NOTIFICATIONS_SIGN_UP_URL", + `/doorway/${props.environment}/public-portal/NOTIFICATIONS_SIGN_UP_URL` + ) + ), + SHOW_ALL_MAP_PINS: Secret.fromSsmParameter( + StringParameter.fromStringParameterName( + this, + "SHOW_ALL_MAP_PINS", + `/doorway/${props.environment}/public-portal/SHOW_ALL_MAP_PINS` + ) + ), + SHOW_ALL_PROFESSIONAL_PARTNERS: Secret.fromSsmParameter( + StringParameter.fromStringParameterName( + this, + "SHOW_PROFESSIONAL_PARTNERS", + `/doorway/${props.environment}/public-portal/SHOW_PROFESSIONAL_PARTNERS` + ) + ), + }, + environment: { + BACKEND_API_BASE: `http://backend.${props.environment}.housingbayarea.int`, + }, + logDriver: AwsLogDriver.awsLogs({ + streamPrefix: "doorway-public-portal", + logGroup: new LogGroup(this, `doorway-${props.environment}-tasks`, { + logGroupName: `/doorway/${props.environment}/ecs/doorway-public-portal`, + }), + }), + }, + + certificate: cert, + listenerPort: 443, + redirectHTTP: true, + sslPolicy: SslPolicy.RECOMMENDED, + serviceName: `doorway-public-portal-service-${props.environment}`, + loadBalancerName: `doorway-public-lb-${props.environment}`, + cpu: 1024, + memoryLimitMiB: 2048, + domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, + domainZone: hostedZone, + } + ) + } +} diff --git a/cdk/lib/doorway-stack-props.ts b/cdk/lib/doorway-stack-props.ts new file mode 100644 index 00000000000..b2334529c66 --- /dev/null +++ b/cdk/lib/doorway-stack-props.ts @@ -0,0 +1,5 @@ +import { StackProps } from "aws-cdk-lib" + +export interface DoorwayStackProps extends StackProps { + environment: string +} diff --git a/cdk/lib/doorway_api_service-stack.ts b/cdk/lib/doorway_api_service-stack.ts new file mode 100644 index 00000000000..06a30a70342 --- /dev/null +++ b/cdk/lib/doorway_api_service-stack.ts @@ -0,0 +1,339 @@ +import * as cdk from "aws-cdk-lib" +import { Fn } from "aws-cdk-lib" +import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2" +import { + Cluster, + Compatibility, + ContainerImage, + FargateService, + LogDrivers, + NetworkMode, + Protocol, + Secret, + TaskDefinition, +} from "aws-cdk-lib/aws-ecs" +//import * as ecs from "aws-cdk-lib/aws-ecs"; +import { ManagedPolicy, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import { LogGroup } from "aws-cdk-lib/aws-logs" +import { Bucket } from "aws-cdk-lib/aws-s3" +import * as secret from "aws-cdk-lib/aws-secretsmanager" +import { EmailIdentity } from "aws-cdk-lib/aws-ses" +import { StringParameter } from "aws-cdk-lib/aws-ssm" +import { Construct } from "constructs" + +import { DoorwayStackProps } from "./doorway-stack-props" + +export class DoorwayApiServiceStack extends cdk.Stack { + public constructor( + scope: Construct, + id: string, + props: DoorwayStackProps = { + environment: "dev2", + env: { + account: process.env.CDK_DEFAULT_ACCOUNT || "none", + region: process.env.CDK_DEFAULT_REGION || "none", + }, + } + ) { + super(scope, id, props) + + const vpcId = cdk.Fn.importValue(`doorway-vpc-id-${props.environment}`) + const azs = cdk.Fn.importValue(`doorway-azs-${props.environment}`).split(",") + const vpc = cdk.aws_ec2.Vpc.fromVpcAttributes(this, "vpc", { + vpcId: vpcId, + availabilityZones: azs, + }) + + // Get the subnets + const appSubnetIds: string[] = [] + appSubnetIds.push(cdk.Fn.importValue(`doorway-app-subnet-1-${props.environment}`)) + appSubnetIds.push(cdk.Fn.importValue(`doorway-app-subnet-2-${props.environment}`)) + const appSubnets: ISubnet[] = [] + appSubnetIds.forEach((id) => { + appSubnets.push(Subnet.fromSubnetId(this, id, id)) + }) + const appTierPrivateSGId = cdk.Fn.importValue(`doorway-app-sg-${props.environment}`) + const appTierPrivateSG = cdk.aws_ec2.SecurityGroup.fromSecurityGroupId( + this, + "appTierPrivateSG", + appTierPrivateSGId + ) + + const publicUploadsBucket = Bucket.fromBucketArn( + this, + `publicUploadsBucket`, + Fn.importValue(`doorway-public-uploads-${props.environment}`) + ) + const secureUploadsBucket = Bucket.fromBucketArn( + this, + `secureUploadsBucket`, + Fn.importValue(`doorway-secure-uploads-${props.environment}`) + ) + // Get the SES Email Information + const sesIdentity = EmailIdentity.fromEmailIdentityName( + this, + "sesIdentity", + "housingbayarea2.org" + ) + + // Create the execution role for the doorway API service + const executionRole = new Role(this, "executionRole", { + assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), + }) + executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy") + ) + executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess") + ) + executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("AmazonRDSReadOnlyAccess") + ) + executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly") + ) + publicUploadsBucket.grantReadWrite(executionRole) + publicUploadsBucket.grantPut(executionRole) + secureUploadsBucket.grantReadWrite(executionRole) + secureUploadsBucket.grantPut(executionRole) + sesIdentity.grantSendEmail(executionRole) + // Add a bunch of random SES grants that grantSendEmail doesn't set up + const policy = new PolicyStatement({ + actions: [ + "ses:SendEmail", + "ses:SendRawEmail", + "ses:SendTemplatedEmail", + "ses:SendRawTemplatedEmail", + "ses:SendBulkTemplatedEmail", + "ses:UseConfiguration", + "ses:SendBulkEmail", + ], + resources: [ + sesIdentity.emailIdentityArn, + `arn:aws:ses:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:configuration-set/dway-config-set`, + `arn:aws:ses:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:identity/*`, + ], + }) + executionRole.addToPolicy(policy) + const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) + const dbSecret = secret.Secret.fromSecretCompleteArn(this, "dbSecret", dbSecretArn) + dbSecret.grantRead(executionRole) + const logGroup = new LogGroup(this, `doorway-${props.environment}-tasks`, { + logGroupName: `doorway-${props.environment}-tasks`, + removalPolicy: cdk.RemovalPolicy.DESTROY, + retention: cdk.aws_logs.RetentionDays.ONE_WEEK, + }) + logGroup.grantWrite(executionRole) + + // Now we're down to business! Set up the fargate container task for the api + // Notice the load of environment variables we add to it below + const task = new TaskDefinition(this, "apiTask", { + compatibility: Compatibility.FARGATE, + cpu: "2048", + memoryMiB: "4096", + executionRole: executionRole, + taskRole: executionRole, + networkMode: NetworkMode.AWS_VPC, + }) + task.addContainer("internal-api", { + image: ContainerImage.fromRegistry( + `${cdk.Aws.ACCOUNT_ID}.dkr.ecr.${cdk.Aws.REGION}.amazonaws.com/doorway/backend:run-candidate` + ), + cpu: 1, + memoryLimitMiB: 1024, + essential: true, + logging: LogDrivers.awsLogs({ + streamPrefix: "internal-api", + logGroup: LogGroup.fromLogGroupName(this, "logGroup", `doorway-${props.environment}-tasks`), + }), + + secrets: { + GOOGLE_API_ID: Secret.fromSecretsManager( + secret.Secret.fromSecretNameV2(this, "googleApiId", "/doorway/GOOGLE_API_ID") + ), + GOOGLE_API_EMAIL: Secret.fromSecretsManager( + secret.Secret.fromSecretNameV2(this, "googleApiEmail", "/doorway/GOOGLE_API_EMAIL") + ), + GOOGLE_API_KEY: Secret.fromSecretsManager( + secret.Secret.fromSecretNameV2(this, "googleApiKey", "/doorway/GOOGLE_API_KEY") + ), + + PGUSER: Secret.fromSecretsManager(dbSecret, "username"), + PGPASSWORD: Secret.fromSecretsManager(dbSecret, "password"), + PGPORT: Secret.fromSecretsManager(dbSecret, "port"), + PGHOST: Secret.fromSecretsManager(dbSecret, "host"), + THROTTLE_LIMIT: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "THROTTLE_LIMIT", { + parameterName: `/doorway/${props.environment}/internal-api/THROTTLE_LIMIT`, + }) + ), + LOG_LEVEL: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "LOG_LEVEL", { + parameterName: `/doorway/${props.environment}/internal-api/LOG_LEVEL`, + }) + ), + + LISTING_PROCESSING_CRON_STRING: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "LISTING_PROCESSING_CRON_STRING", { + parameterName: `/doorway/${props.environment}/internal-api/LISTING_PROCESSING_CRON_STRING`, + }) + ), + LOTTERY_PROCESSING_CRON_STRING: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "LOTTERY_PROCESSING_CRON_STRING", { + parameterName: `/doorway/${props.environment}/internal-api/LOTTERY_PROCESSING_CRON_STRING`, + }) + ), + LOTTERY_PUBLISH_PROCESSING_CRON_STRING: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes( + this, + "LOTTERY_PUBLISH_PROCESSING_CRON_STRING", + { + parameterName: `/doorway/${props.environment}/internal-api/LOTTERY_PUBLISH_PROCESSING_CRON_STRING`, + } + ) + ), + AFS_PROCESSING_CRON_STRING: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "AFS_PROCESSING_CRON_STRING", { + parameterName: `/doorway/${props.environment}/internal-api/AFS_PROCESSING_CRON_STRING`, + }) + ), + TEMP_FILE_CLEAR_CRON_STRING: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "TEMP_FILE_CLEAR_CRON_STRING", { + parameterName: `/doorway/${props.environment}/internal-api/TEMP_FILE_CLEAR_CRON_STRING`, + }) + ), + LOTTERY_DAYS_TILL_EXPIRY: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "LOTTERY_DAYS_TILL_EXPIRY", { + parameterName: `/doorway/${props.environment}/internal-api/LOTTERY_DAYS_TILL_EXPIRY`, + }) + ), + MFA_CODE_LENGTH: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "MFA_CODE_LENGTH", { + parameterName: `/doorway/${props.environment}/internal-api/MFA_CODE_LENGTH`, + }) + ), + MFA_CODE_VALID: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "MFA_CODE_VALID", { + parameterName: `/doorway/${props.environment}/internal-api/MFA_CODE_VALID`, + }) + ), + + GOVDELIVERY_TOPIC: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "GOVDELIVERY_TOPIC", { + parameterName: `/doorway/${props.environment}/internal-api/GOVDELIVERY_TOPIC`, + }) + ), + + PARTNERS_PORTAL_URL: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "PARTNERS_PORTAL_URL", { + parameterName: `/doorway/${props.environment}/internal-api/PARTNERS_PORTAL_URL`, + }) + ), + PARTNERS_BASE_URL: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "PARTNERS_PORTAL_BASE_URL", { + parameterName: `/doorway/${props.environment}/internal-api/PARTNERS_BASE_URL`, + }) + ), + THROTTLE_TTL: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "THROTTLE_TTL", { + parameterName: `/doorway/${props.environment}/internal-api/THROTTLE_TTL`, + }) + ), + + ASSET_UPLOAD_MAX_SIZE: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "ASSET_UPLOAD_MAX_SIZE", { + parameterName: `/doorway/${props.environment}/internal-api/ASSET_UPLOAD_MAX_SIZE`, + }) + ), + AUTH_LOCK_LOGIN_COOLDOWN: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "AUTH_LOCK_LOGIN_COOLDOWN", { + parameterName: `/doorway/${props.environment}/internal-api/AUTH_LOCK_LOGIN_COOLDOWN`, + }) + ), + AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes( + this, + "AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS", + { + parameterName: `/doorway/${props.environment}/internal-api/AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS`, + } + ) + ), + CORS_ORIGINS: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "CORS_ORIGINS", { + parameterName: `/doorway/${props.environment}/internal-api/CORS_ORIGINS`, + }) + ), + DUPLICATES_CLOSE_DATE: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "DUPLICATES_CLOSE_DATE", { + parameterName: `/doorway/${props.environment}/internal-api/DUPLICATES_CLOSE_DATE`, + }) + ), + DUPLICATES_PROCESSING_CRON_STRING: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "DUPLICATES_PROCESSING_CRON_STRING", { + parameterName: `/doorway/${props.environment}/internal-api/DUPLICATES_PROCESSING_CRON_STRING`, + }) + ), + HTTPS_OFF: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "HTTPS_OFF", { + parameterName: `/doorway/${props.environment}/internal-api/HTTPS_OFF`, + }) + ), + SAME_SITE: Secret.fromSsmParameter( + StringParameter.fromStringParameterAttributes(this, "SAME_SITE", { + parameterName: `/doorway/${props.environment}/internal-api/SAME_SITE`, + }) + ), + }, + environment: { + APP_SECRET: "", + ASSET_FILE_SERVICE: "s3", + LISTINGS_PROCESSING_QUERY: "/listings", + PORT: "3100", + SHOW_LM_LINKS: "TRUE", + TIME_ZONE: "America/Los_Angeles", + SHOW_DUPLICATES: "FALSE", + NO_COLOR: "TRUE", + ASSET_FS_CONFIG_s3_PATH_PREFIX: "", + ASSET_FS_PRIVATE_CONFIG_s3_BUCKET: secureUploadsBucket.bucketName, + ASSET_FS_CONFIG_s3_BUCKET: publicUploadsBucket.bucketName, + ASSET_FS_CONFIG_s3_REGION: cdk.Aws.REGION, + ASSET_FS_CONFIG_s3_URL_FORMAT: "public", + PGDATABASE: "bloom", + }, + entryPoint: [], + portMappings: [ + { + containerPort: 3100, + protocol: Protocol.TCP, + hostPort: 3100, + }, + ], + }) + + // Create the service in ECS + const service = new FargateService(this, `doorway-${props.environment}-internal-api`, { + taskDefinition: task, + serviceName: `doorway-${props.environment}-internal-api`, + cluster: Cluster.fromClusterAttributes(this, "ecsCluster", { + clusterName: Fn.importValue(`doorway-ecs-cluster-${props.environment}`), + vpc: vpc, + }), + vpcSubnets: { + subnets: appSubnets, + }, + securityGroups: [appTierPrivateSG], + desiredCount: 3, + }) + + const scaling = service.autoScaleTaskCount({ + minCapacity: 2, + maxCapacity: 10, + }) + scaling.scaleOnCpuUtilization("CpuScaling", { + targetUtilizationPercent: 80, + scaleInCooldown: cdk.Duration.seconds(60), + scaleOutCooldown: cdk.Duration.seconds(60), + }) + } +} diff --git a/cdk/package-lock.json b/cdk/package-lock.json new file mode 100644 index 00000000000..230efff4c51 --- /dev/null +++ b/cdk/package-lock.json @@ -0,0 +1,4422 @@ +{ + "name": "cdk", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cdk", + "version": "0.1.0", + "dependencies": { + "aws-cdk-lib": "2.208.0", + "constructs": "^10.0.0" + }, + "bin": { + "cdk": "bin/cdk.js" + }, + "devDependencies": { + "@types/jest": "^29.5.14", + "@types/node": "22.7.9", + "aws-cdk": "2.1024.0", + "jest": "^29.7.0", + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", + "typescript": "~5.6.3" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@aws-cdk/asset-awscli-v1": { + "version": "2.2.242", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.242.tgz", + "integrity": "sha512-4c1bAy2ISzcdKXYS1k4HYZsNrgiwbiDzj36ybwFVxEWZXVAP0dimQTCaB9fxu7sWzEjw3d+eaw6Fon+QTfTIpQ==", + "license": "Apache-2.0" + }, + "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.1.0.tgz", + "integrity": "sha512-7bY3J8GCVxLupn/kNmpPc5VJz8grx+4RKfnnJiO1LG+uxkZfANZG3RMHhE+qQxxwkyQ9/MfPtTpf748UhR425A==", + "license": "Apache-2.0" + }, + "node_modules/@aws-cdk/cloud-assembly-schema": { + "version": "45.2.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-45.2.0.tgz", + "integrity": "sha512-5TTUkGHQ+nfuUGwKA8/Yraxb+JdNUh4np24qk/VHXmrCMq+M6HfmGWfhcg/QlHA2S5P3YIamfYHdQAB4uSNLAg==", + "bundleDependencies": [ + "jsonschema", + "semver" + ], + "license": "Apache-2.0", + "dependencies": { + "jsonschema": "~1.4.1", + "semver": "^7.7.2" + }, + "engines": { + "node": ">= 18.0.0" + } + }, + "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": { + "version": "1.4.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/semver": { + "version": "7.7.2", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", + "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "22.7.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.9.tgz", + "integrity": "sha512-jrTfRC7FM6nChvU7X2KqcrgquofrWLFDeYC1hKfwNWomVvrn7JIksqf344WN2X/y8xrgqBd2dJATZV4GbatBfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aws-cdk": { + "version": "2.1024.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1024.0.tgz", + "integrity": "sha512-hY0iVT2gPX/QOQXL7RSP2sqIRI/4BYU27vSmbhZxLEj//c3pkMkd9QpIHj7gOhyWC2gf6n5JuYPw27Dgw8FEdA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "cdk": "bin/cdk" + }, + "engines": { + "node": ">= 18.0.0" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/aws-cdk-lib": { + "version": "2.208.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.208.0.tgz", + "integrity": "sha512-lZW475enKz36A/hZvS7xUwfjeqLqxKgBmZ2cGA7BB5PAlQsjBfN70ZGDgGl0egug5fb/hsYy1Dy4OrclfNzxFg==", + "bundleDependencies": [ + "@balena/dockerignore", + "case", + "fs-extra", + "ignore", + "jsonschema", + "minimatch", + "punycode", + "semver", + "table", + "yaml", + "mime-types" + ], + "license": "Apache-2.0", + "dependencies": { + "@aws-cdk/asset-awscli-v1": "2.2.242", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", + "@aws-cdk/cloud-assembly-schema": "^45.2.0", + "@balena/dockerignore": "^1.0.2", + "case": "1.6.3", + "fs-extra": "^11.3.0", + "ignore": "^5.3.2", + "jsonschema": "^1.5.0", + "mime-types": "^2.1.35", + "minimatch": "^3.1.2", + "punycode": "^2.3.1", + "semver": "^7.7.2", + "table": "^6.9.0", + "yaml": "1.10.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "constructs": "^10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { + "version": "1.0.2", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/aws-cdk-lib/node_modules/ajv": { + "version": "8.17.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/astral-regex": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/brace-expansion": { + "version": "1.1.12", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/aws-cdk-lib/node_modules/case": { + "version": "1.6.3", + "inBundle": true, + "license": "(MIT OR GPL-3.0-or-later)", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/concat-map": { + "version": "0.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { + "version": "3.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-uri": { + "version": "3.0.6", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/aws-cdk-lib/node_modules/fs-extra": { + "version": "11.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/aws-cdk-lib/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/ignore": { + "version": "5.3.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/jsonfile": { + "version": "6.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/jsonschema": { + "version": "1.5.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { + "version": "4.4.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/mime-db": { + "version": "1.52.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/mime-types": { + "version": "2.1.35", + "inBundle": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/minimatch": { + "version": "3.1.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/punycode": { + "version": "2.3.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/aws-cdk-lib/node_modules/require-from-string": { + "version": "2.0.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/semver": { + "version": "7.7.2", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/slice-ansi": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "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/aws-cdk-lib/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/table": { + "version": "6.9.0", + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/universalify": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/yaml": { + "version": "1.10.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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/browserslist": { + "version": "4.25.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz", + "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001733", + "electron-to-chromium": "^1.5.199", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "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/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001735", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", + "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "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/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/constructs": { + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.4.2.tgz", + "integrity": "sha512-wsNxBlAott2qg8Zv87q3eYZYgheb9lchtBfjHzzLHtXbttwSrHPs1NNQbBrmbb1YZvYg2+Vh0Dor76w4mFxJkA==", + "license": "Apache-2.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "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.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.202", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.202.tgz", + "integrity": "sha512-NxbYjRmiHcHXV1Ws3fWUW+SLb62isauajk45LUJ/HgIOkUA7jLZu/X2Iif+X9FBNK8QkF9Zb4Q2mcwXCcY30mg==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "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": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "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/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "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/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "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/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "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/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "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-jest": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.1.tgz", + "integrity": "sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.2", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "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/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/cdk/package.json b/cdk/package.json new file mode 100644 index 00000000000..629d82ae846 --- /dev/null +++ b/cdk/package.json @@ -0,0 +1,26 @@ +{ + "name": "doorway-cdk", + "version": "0.1.0", + "bin": { + "cdk": "bin/doorway-cdk.ts" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk" + }, + "devDependencies": { + "@types/jest": "^29.5.14", + "@types/node": "22.7.9", + "jest": "^29.7.0", + "ts-jest": "^29.2.5", + "aws-cdk": "2.1024.0", + "ts-node": "^10.9.2", + "typescript": "~5.6.3" + }, + "dependencies": { + "aws-cdk-lib": "2.208.0", + "constructs": "^10.0.0" + } +} \ No newline at end of file diff --git a/cdk/test/cdk.test.ts b/cdk/test/cdk.test.ts new file mode 100644 index 00000000000..bd53ea16aae --- /dev/null +++ b/cdk/test/cdk.test.ts @@ -0,0 +1,16 @@ +// import * as cdk from 'aws-cdk-lib'; +// import { Template } from 'aws-cdk-lib/assertions'; +// import * as Cdk from '../lib/cdk-stack'; + +// example test. To run these tests, uncomment this file along with the +// example resource in lib/cdk-stack.ts +test("SQS Queue Created", () => { + // const app = new cdk.App(); + // // WHEN + // const stack = new Cdk.CdkStack(app, 'MyTestStack'); + // // THEN + // const template = Template.fromStack(stack); + // template.hasResourceProperties('AWS::SQS::Queue', { + // VisibilityTimeout: 300 + // }); +}) diff --git a/cdk/tsconfig.json b/cdk/tsconfig.json new file mode 100644 index 00000000000..b1eaa510e97 --- /dev/null +++ b/cdk/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["es2022"], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": ["./node_modules/@types"] + }, + "exclude": ["node_modules", "cdk.out"] +} From 60666393ad49679389bd4fad7befc3b80c4502f2 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 15 Aug 2025 15:09:41 -0700 Subject: [PATCH 013/217] feat: adding migration --- cdk/cdk.json | 11 +- cdk/{jest.config.js => jest.config.ts} | 0 .../doorway-build-pipeline-stack.ts | 12 + .../doorway-database-migrate.ts | 99 + cdk/package.json | 4 +- cdk/yarn.lock | 2425 +++++++++++++++++ ci/buildspec/migrate_stop_backend.yml | 1 - 7 files changed, 2541 insertions(+), 11 deletions(-) rename cdk/{jest.config.js => jest.config.ts} (100%) create mode 100644 cdk/lib/build-pipeline/doorway-database-migrate.ts create mode 100644 cdk/yarn.lock diff --git a/cdk/cdk.json b/cdk/cdk.json index 3b1412ffe4f..3dbd62bc3e8 100644 --- a/cdk/cdk.json +++ b/cdk/cdk.json @@ -1,9 +1,7 @@ { "app": "npx ts-node --prefer-ts-exts bin/doorway-cdk.ts", "watch": { - "include": [ - "**" - ], + "include": ["**"], "exclude": [ "README.md", "cdk*.json", @@ -19,10 +17,7 @@ "context": { "@aws-cdk/aws-lambda:recognizeLayerVersion": true, "@aws-cdk/core:checkSecretUsage": true, - "@aws-cdk/core:target-partitions": [ - "aws", - "aws-cn" - ], + "@aws-cdk/core:target-partitions": ["aws", "aws-cn"], "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, @@ -96,4 +91,4 @@ "@aws-cdk/aws-s3:publicAccessBlockedByDefault": true, "@aws-cdk/aws-lambda:useCdkManagedLogGroup": true } -} \ No newline at end of file +} diff --git a/cdk/jest.config.js b/cdk/jest.config.ts similarity index 100% rename from cdk/jest.config.js rename to cdk/jest.config.ts diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 8e9677a62fd..ffc83da893c 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -5,6 +5,7 @@ import { PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Secret } from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" +import { DoorwayDatabaseMigrate } from "./doorway-database-migrate" import { DoorwayDockerBuild } from "./doorway-docker-build" export class DoorwayBuildPipelineStack extends Stack { @@ -103,6 +104,17 @@ export class DoorwayBuildPipelineStack extends Stack { }).action, ], }) + pipeline.addStage({ + stageName: "Dev", + actions: [ + new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev", { + ecrNamespace: "doorway/backend", + environment: "dev2", + buildspec: "./ci/buildspec/migrate_stop_backend.yml", + source: sourceArtifact, + }).action, + ], + }) } } export interface PipelineProps extends StackProps { diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts new file mode 100644 index 00000000000..30c29c59127 --- /dev/null +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -0,0 +1,99 @@ +import { Aws, Fn, Stack } from "aws-cdk-lib" +import { BuildSpec, ComputeType, LinuxBuildImage, PipelineProject } from "aws-cdk-lib/aws-codebuild" +import { Artifact } from "aws-cdk-lib/aws-codepipeline" +import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" +import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" +import { PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" + +// Removed fs and YAML imports since we're using BuildSpec.fromSourceFilename + +export interface DoorwayDatabaseMigrateProps { + ecrNamespace?: string + databaseName?: string + environment: string + buildspec: string + source: Artifact +} + +export class DoorwayDatabaseMigrate { + public readonly action: CodeBuildAction + constructor(stack: Stack, id: string, props: DoorwayDatabaseMigrateProps) { + const vpcId = Fn.importValue(`doorway-vpc-id-${props.environment}`) + const subnetId = Fn.importValue(`doorway-app-subnet-1-${props.environment}`) + const securityGroupId = Fn.importValue(`doorway-app-sg-${props.environment}`) + const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) + const azs = Fn.importValue(`doorway-azs-${props.environment}`).split(", ") + const vpc = Vpc.fromVpcAttributes(stack, "vpc", { + vpcId: vpcId, + availabilityZones: azs, + }) + const subnet = Subnet.fromSubnetAttributes(stack, "subnet", { + subnetId: subnetId, + }) + const sg = SecurityGroup.fromSecurityGroupId(stack, "sg", securityGroupId) + + const buildRole = new Role(stack, "doorway-app-build-role", { + assumedBy: new ServicePrincipal("codebuild.amazonaws.com"), + managedPolicies: [ + { + managedPolicyArn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess", + }, + { + managedPolicyArn: "arn:aws:iam::aws:policy/AmazonS3FullAccess", + }, + ], + inlinePolicies: { + ECRPolicies: new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: ["ecr:*"], + resources: [`arn:aws:ecr:${Aws.REGION}:${Aws.ACCOUNT_ID}:repository/*`], + }), + new PolicyStatement({ + actions: ["secretsmanager:GetSecretValue"], + resources: [dbSecretArn], + }), + new PolicyStatement({ + actions: [ + "cloudformation:*", + "ec2:*", + "ssm:*", + "codebuild:*", + "logs:*", + "iam:AssumeRole", + "iam:PassRole", + ], + resources: ["*"], + }), + ], + }), + }, + }) + const project = new PipelineProject(stack, `doorway-dbMigrate-${props.environment}`, { + vpc: vpc, + subnetSelection: { subnets: [subnet] }, + securityGroups: [sg], + buildSpec: BuildSpec.fromSourceFilename(props.buildspec), + environment: { + buildImage: LinuxBuildImage.STANDARD_7_0, + computeType: ComputeType.LARGE, + privileged: true, // Required for Docker commands + }, + environmentVariables: { + DB_SECRET_ARN: { value: dbSecretArn }, + ECR_REGION: { value: Aws.REGION }, + ECR_ACCOUNT_ID: { value: Aws.ACCOUNT_ID }, + ECR_NAMESPACE: { value: props.ecrNamespace || "doorway" }, + PGDATABASE: { value: props.databaseName || "bloom" }, + }, + role: buildRole, + }) + // Create the CodeBuild action + this.action = new CodeBuildAction({ + actionName: `${id}-DBMigrate`, + input: props.source, + outputs: [new Artifact(`${id}-BuildOutput`)], + project, + }) + } +} diff --git a/cdk/package.json b/cdk/package.json index 629d82ae846..b8c18074a0e 100644 --- a/cdk/package.json +++ b/cdk/package.json @@ -13,9 +13,9 @@ "devDependencies": { "@types/jest": "^29.5.14", "@types/node": "22.7.9", + "aws-cdk": "2.1024.0", "jest": "^29.7.0", "ts-jest": "^29.2.5", - "aws-cdk": "2.1024.0", "ts-node": "^10.9.2", "typescript": "~5.6.3" }, @@ -23,4 +23,4 @@ "aws-cdk-lib": "2.208.0", "constructs": "^10.0.0" } -} \ No newline at end of file +} diff --git a/cdk/yarn.lock b/cdk/yarn.lock new file mode 100644 index 00000000000..8b1d4944733 --- /dev/null +++ b/cdk/yarn.lock @@ -0,0 +1,2425 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@aws-cdk/asset-awscli-v1@2.2.242": + version "2.2.242" + resolved "https://registry.yarnpkg.com/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.242.tgz#235cb25b6d1ad26975b0095c0d6ee84309adae5c" + integrity sha512-4c1bAy2ISzcdKXYS1k4HYZsNrgiwbiDzj36ybwFVxEWZXVAP0dimQTCaB9fxu7sWzEjw3d+eaw6Fon+QTfTIpQ== + +"@aws-cdk/asset-node-proxy-agent-v6@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.1.0.tgz#6d3c7860354d4856a7e75375f2f0ecab313b4989" + integrity sha512-7bY3J8GCVxLupn/kNmpPc5VJz8grx+4RKfnnJiO1LG+uxkZfANZG3RMHhE+qQxxwkyQ9/MfPtTpf748UhR425A== + +"@aws-cdk/cloud-assembly-schema@^45.2.0": + version "45.2.0" + resolved "https://registry.yarnpkg.com/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-45.2.0.tgz#bd4bf1e16189a2d424b9ab503db48ca9732325b1" + integrity sha512-5TTUkGHQ+nfuUGwKA8/Yraxb+JdNUh4np24qk/VHXmrCMq+M6HfmGWfhcg/QlHA2S5P3YIamfYHdQAB4uSNLAg== + dependencies: + jsonschema "~1.4.1" + semver "^7.7.2" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== + dependencies: + "@babel/helper-validator-identifier" "^7.27.1" + js-tokens "^4.0.0" + picocolors "^1.1.1" + +"@babel/compat-data@^7.27.2": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.0.tgz#9fc6fd58c2a6a15243cd13983224968392070790" + integrity sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw== + +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.3.tgz#aceddde69c5d1def69b839d09efa3e3ff59c97cb" + integrity sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.28.3" + "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-module-transforms" "^7.28.3" + "@babel/helpers" "^7.28.3" + "@babel/parser" "^7.28.3" + "@babel/template" "^7.27.2" + "@babel/traverse" "^7.28.3" + "@babel/types" "^7.28.2" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.28.3", "@babel/generator@^7.7.2": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.3.tgz#9626c1741c650cbac39121694a0f2d7451b8ef3e" + integrity sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw== + dependencies: + "@babel/parser" "^7.28.3" + "@babel/types" "^7.28.2" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" + jsesc "^3.0.2" + +"@babel/helper-compilation-targets@^7.27.2": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" + integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== + dependencies: + "@babel/compat-data" "^7.27.2" + "@babel/helper-validator-option" "^7.27.1" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-globals@^7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" + integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== + +"@babel/helper-module-imports@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" + integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== + dependencies: + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" + +"@babel/helper-module-transforms@^7.28.3": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" + integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw== + dependencies: + "@babel/helper-module-imports" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + "@babel/traverse" "^7.28.3" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" + integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== + +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + +"@babel/helper-validator-identifier@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== + +"@babel/helper-validator-option@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" + integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== + +"@babel/helpers@^7.28.3": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.3.tgz#b83156c0a2232c133d1b535dd5d3452119c7e441" + integrity sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw== + dependencies: + "@babel/template" "^7.27.2" + "@babel/types" "^7.28.2" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.27.2", "@babel/parser@^7.28.3": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.3.tgz#d2d25b814621bca5fe9d172bc93792547e7a2a71" + integrity sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA== + dependencies: + "@babel/types" "^7.28.2" + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-import-attributes@^7.24.7": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" + integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-syntax-import-meta@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" + integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" + integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/template@^7.27.2", "@babel/template@^7.3.3": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" + integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/parser" "^7.27.2" + "@babel/types" "^7.27.1" + +"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.3.tgz#6911a10795d2cce43ec6a28cffc440cca2593434" + integrity sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.28.3" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.28.3" + "@babel/template" "^7.27.2" + "@babel/types" "^7.28.2" + debug "^4.3.1" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.28.2", "@babel/types@^7.3.3": + version "7.28.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.2.tgz#da9db0856a9a88e0a13b019881d7513588cf712b" + integrity sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + +"@balena/dockerignore@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@balena/dockerignore/-/dockerignore-1.0.2.tgz#9ffe4726915251e8eb69f44ef3547e0da2c03e0d" + integrity sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q== + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== + dependencies: + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== + dependencies: + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== + dependencies: + expect "^29.7.0" + jest-snapshot "^29.7.0" + +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== + dependencies: + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" + +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" + +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^6.0.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" + +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== + dependencies: + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== + dependencies: + "@jest/test-result" "^29.7.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + slash "^3.0.0" + +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": + version "0.3.30" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz#4a76c4daeee5df09f5d3940e087442fb36ce2b99" + integrity sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sinonjs/commons@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/babel__core@^7.1.14": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9" + integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74" + integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== + dependencies: + "@babel/types" "^7.28.2" + +"@types/graceful-fs@^4.1.3": + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^29.5.14": + version "29.5.14" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" + integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/node@*": + version "24.3.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.3.0.tgz#89b09f45cb9a8ee69466f18ee5864e4c3eb84dec" + integrity sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow== + dependencies: + undici-types "~7.10.0" + +"@types/node@22.7.9": + version "22.7.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.9.tgz#2bf2797b5e84702d8262ea2cf843c3c3c880d0e9" + integrity sha512-jrTfRC7FM6nChvU7X2KqcrgquofrWLFDeYC1hKfwNWomVvrn7JIksqf344WN2X/y8xrgqBd2dJATZV4GbatBfg== + dependencies: + undici-types "~6.19.2" + +"@types/stack-utils@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== + +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.8": + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== + dependencies: + "@types/yargs-parser" "*" + +acorn-walk@^8.1.1: + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.4.1: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + +ajv@^8.0.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +anymatch@^3.0.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +aws-cdk-lib@2.208.0: + version "2.208.0" + resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.208.0.tgz#bec8ae4219b857b75d2841e4c6f023c42bdedd10" + integrity sha512-lZW475enKz36A/hZvS7xUwfjeqLqxKgBmZ2cGA7BB5PAlQsjBfN70ZGDgGl0egug5fb/hsYy1Dy4OrclfNzxFg== + dependencies: + "@aws-cdk/asset-awscli-v1" "2.2.242" + "@aws-cdk/asset-node-proxy-agent-v6" "^2.1.0" + "@aws-cdk/cloud-assembly-schema" "^45.2.0" + "@balena/dockerignore" "^1.0.2" + case "1.6.3" + fs-extra "^11.3.0" + ignore "^5.3.2" + jsonschema "^1.5.0" + mime-types "^2.1.35" + minimatch "^3.1.2" + punycode "^2.3.1" + semver "^7.7.2" + table "^6.9.0" + yaml "1.10.2" + +aws-cdk@2.1024.0: + version "2.1024.0" + resolved "https://registry.yarnpkg.com/aws-cdk/-/aws-cdk-2.1024.0.tgz#c4fac23fa577a1a6e2253e68fa7a8b07cb6816aa" + integrity sha512-hY0iVT2gPX/QOQXL7RSP2sqIRI/4BYU27vSmbhZxLEj//c3pkMkd9QpIHj7gOhyWC2gf6n5JuYPw27Dgw8FEdA== + optionalDependencies: + fsevents "2.3.2" + +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== + dependencies: + "@jest/transform" "^29.7.0" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.6.3" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + +babel-preset-current-node-syntax@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz#20730d6cdc7dda5d89401cab10ac6a32067acde6" + integrity sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-import-attributes" "^7.24.7" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== + dependencies: + babel-plugin-jest-hoist "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browserslist@^4.24.0: + version "4.25.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.25.2.tgz#90c1507143742d743544ae6e92bca3348adff667" + integrity sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA== + dependencies: + caniuse-lite "^1.0.30001733" + electron-to-chromium "^1.5.199" + node-releases "^2.0.19" + update-browserslist-db "^1.1.3" + +bs-logger@^0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001733: + version "1.0.30001735" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz#ba658fd3fd24a4106fd68d5ce472a2c251494dbe" + integrity sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w== + +case@1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9" + integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +cjs-module-lexer@^1.0.0: + version "1.4.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz#0f79731eb8cfe1ec72acd4066efac9d61991b00d" + integrity sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q== + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +collect-v8-coverage@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +constructs@^10.0.0: + version "10.4.2" + resolved "https://registry.yarnpkg.com/constructs/-/constructs-10.4.2.tgz#e875a78bef932cca12ea63965969873a25c1c132" + integrity sha512-wsNxBlAott2qg8Zv87q3eYZYgheb9lchtBfjHzzLHtXbttwSrHPs1NNQbBrmbb1YZvYg2+Vh0Dor76w4mFxJkA== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^7.0.3: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + +dedent@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.6.0.tgz#79d52d6389b1ffa67d2bcef59ba51847a9d503b2" + integrity sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +electron-to-chromium@^1.5.199: + version "1.5.202" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.202.tgz#e243716943a2cab144604a81b9ebaa87b4e8c1d4" + integrity sha512-NxbYjRmiHcHXV1Ws3fWUW+SLb62isauajk45LUJ/HgIOkUA7jLZu/X2Iif+X9FBNK8QkF9Zb4Q2mcwXCcY30mg== + +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expect@^29.0.0, expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-uri@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" + integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== + +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +fs-extra@^11.3.0: + version "11.3.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.1.tgz#ba7a1f97a85f94c6db2e52ff69570db3671d5a74" + integrity sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +fsevents@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +glob@^7.1.3, glob@^7.1.4: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +handlebars@^4.7.8: + version "4.7.8" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.2" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +ignore@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-local@^3.0.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-instrument@^5.0.4: + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-instrument@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== + dependencies: + "@babel/core" "^7.23.9" + "@babel/parser" "^7.23.9" + "@istanbuljs/schema" "^0.1.3" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + +istanbul-lib-report@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== + dependencies: + execa "^5.0.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^1.0.0" + is-generator-fn "^2.0.0" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + pretty-format "^29.7.0" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== + dependencies: + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + chalk "^4.0.0" + create-jest "^29.7.0" + exit "^0.1.2" + import-local "^3.0.2" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + yargs "^17.3.1" + +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== + dependencies: + detect-newline "^3.0.0" + +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" + +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== + dependencies: + "@jest/types" "^29.6.3" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== + dependencies: + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-util "^29.7.0" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== + +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== + dependencies: + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" + +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-pnp-resolver "^1.2.2" + jest-util "^29.7.0" + jest-validate "^29.7.0" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== + dependencies: + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.7.0" + graceful-fs "^4.2.9" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + natural-compare "^1.4.0" + pretty-format "^29.7.0" + semver "^7.5.3" + +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== + dependencies: + "@jest/types" "^29.6.3" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.6.3" + leven "^3.1.0" + pretty-format "^29.7.0" + +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== + dependencies: + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.7.0" + string-length "^4.0.1" + +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== + dependencies: + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" + import-local "^3.0.2" + jest-cli "^29.7.0" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonfile@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62" + integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonschema@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.5.0.tgz#f6aceb1ab9123563dd901d05f81f9d4883d3b7d8" + integrity sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw== + +jsonschema@~1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" + integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +make-error@^1.1.1, make-error@^1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +micromatch@^4.0.4: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.35: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.5: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pirates@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" + integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +prompts@^2.0.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +pure-rand@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== + +react-is@^18.0.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve.exports@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" + integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== + +resolve@^1.20.0: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.5.3, semver@^7.5.4, semver@^7.7.2: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +table@^6.9.0: + version "6.9.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.9.0.tgz#50040afa6264141c7566b3b81d4d82c47a8668f5" + integrity sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +ts-jest@^29.2.5: + version "29.4.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.1.tgz#42d33beb74657751d315efb9a871fe99e3b9b519" + integrity sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw== + dependencies: + bs-logger "^0.2.6" + fast-json-stable-stringify "^2.1.0" + handlebars "^4.7.8" + json5 "^2.2.3" + lodash.memoize "^4.1.2" + make-error "^1.3.6" + semver "^7.7.2" + type-fest "^4.41.0" + yargs-parser "^21.1.1" + +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^4.41.0: + version "4.41.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" + integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== + +typescript@~5.6.3: + version "5.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" + integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== + +uglify-js@^3.1.4: + version "3.19.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" + integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== + +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + +undici-types@~7.10.0: + version "7.10.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.10.0.tgz#4ac2e058ce56b462b056e629cc6a02393d3ff350" + integrity sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +update-browserslist-db@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" + integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +v8-to-istanbul@^9.0.1: + version "9.3.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" + integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^2.0.0" + +walker@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yaml@1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.3.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 4a1184952fb..3ea69fe7a94 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -10,7 +10,6 @@ env: PGPASSWORD: "${DB_CREDS_ARN}:password" PGHOST: "${DB_CREDS_ARN}:host" PGPORT: "${DB_CREDS_ARN}:port" - PGDATABASE: "${DB_CREDS_ARN}:dbname" phases: pre_build: From 2e016dbf32296e503b80087c96ddf3dc6b92c42c Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 15 Aug 2025 15:50:59 -0700 Subject: [PATCH 014/217] fix: trying with migrate.yml temporarily --- .../build-pipeline/doorway-build-job-base.ts | 118 ++++++++++++++++++ .../doorway-build-pipeline-stack.ts | 3 +- .../doorway-database-migrate.ts | 2 +- ci/buildspec/migrate.yml | 3 +- ci/buildspec/migrate_stop_backend.yml | 1 + 5 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 cdk/lib/build-pipeline/doorway-build-job-base.ts diff --git a/cdk/lib/build-pipeline/doorway-build-job-base.ts b/cdk/lib/build-pipeline/doorway-build-job-base.ts new file mode 100644 index 00000000000..ca633fe3326 --- /dev/null +++ b/cdk/lib/build-pipeline/doorway-build-job-base.ts @@ -0,0 +1,118 @@ +import { Aws, Fn, Stack } from "aws-cdk-lib" +import { + BuildSpec, + ComputeType, + LinuxBuildImage, + PipelineProject, + PipelineProjectProps, +} from "aws-cdk-lib/aws-codebuild" +import { Artifact } from "aws-cdk-lib/aws-codepipeline" +import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" +import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" +import { PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" + +// Removed fs and YAML imports since we're using BuildSpec.fromSourceFilename + +export interface DoorwayBuildJobProps { + ecrNamespace?: string + databaseName?: string + buildInVpc?: boolean + environment: string + buildspec: string + source: Artifact +} + +export class DoorwayBuildJobBase { + public readonly action: CodeBuildAction + constructor(stack: Stack, id: string, props: DoorwayBuildJobProps) { + const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) + + const buildRole = new Role(stack, "doorway-app-build-role", { + assumedBy: new ServicePrincipal("codebuild.amazonaws.com"), + managedPolicies: [ + { + managedPolicyArn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess", + }, + { + managedPolicyArn: "arn:aws:iam::aws:policy/AmazonS3FullAccess", + }, + ], + inlinePolicies: { + ECRPolicies: new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: ["ecr:*"], + resources: [`arn:aws:ecr:${Aws.REGION}:${Aws.ACCOUNT_ID}:repository/*`], + }), + new PolicyStatement({ + actions: ["secretsmanager:GetSecretValue"], + resources: [dbSecretArn], + }), + new PolicyStatement({ + actions: [ + "cloudformation:*", + "ec2:*", + "ssm:*", + "codebuild:*", + "logs:*", + "iam:AssumeRole", + "iam:PassRole", + ], + resources: ["*"], + }), + ], + }), + }, + }) + let projectProps: PipelineProjectProps = { + buildSpec: BuildSpec.fromSourceFilename(props.buildspec), + environment: { + buildImage: LinuxBuildImage.STANDARD_7_0, + computeType: ComputeType.LARGE, + privileged: true, // Required for Docker commands + }, + environmentVariables: { + DB_SECRET_ARN: { value: dbSecretArn }, + ECR_REGION: { value: Aws.REGION }, + ECR_ACCOUNT_ID: { value: Aws.ACCOUNT_ID }, + ECR_NAMESPACE: { value: props.ecrNamespace || "doorway" }, + PGDATABASE: { value: props.databaseName || "bloom" }, + }, + role: buildRole, + } + if (props.buildInVpc) { + const vpcId = Fn.importValue(`doorway-vpc-id-${props.environment}`) + const subnetId = Fn.importValue(`doorway-app-subnet-1-${props.environment}`) + const securityGroupId = Fn.importValue(`doorway-app-sg-${props.environment}`) + + const azs = Fn.importValue(`doorway-azs-${props.environment}`).split(", ") + const vpc = Vpc.fromVpcAttributes(stack, "vpc", { + vpcId: vpcId, + availabilityZones: azs, + }) + const subnet = Subnet.fromSubnetAttributes(stack, "subnet", { + subnetId: subnetId, + }) + const sg = SecurityGroup.fromSecurityGroupId(stack, "sg", securityGroupId) + projectProps = { + ...projectProps, + vpc: vpc, + subnetSelection: { subnets: [subnet] }, + securityGroups: [sg], + } + } + + const project = new PipelineProject( + stack, + `doorway-dbMigrate-${props.environment}`, + projectProps + ) + // Create the CodeBuild action + this.action = new CodeBuildAction({ + actionName: `${id}-DBMigrate`, + input: props.source, + outputs: [new Artifact(`${id}-BuildOutput`)], + project, + }) + } +} diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index ffc83da893c..0824f818993 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -108,9 +108,8 @@ export class DoorwayBuildPipelineStack extends Stack { stageName: "Dev", actions: [ new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev", { - ecrNamespace: "doorway/backend", environment: "dev2", - buildspec: "./ci/buildspec/migrate_stop_backend.yml", + buildspec: "./ci/buildspec/migrate.yml", source: sourceArtifact, }).action, ], diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts index 30c29c59127..b9caadce999 100644 --- a/cdk/lib/build-pipeline/doorway-database-migrate.ts +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -80,7 +80,7 @@ export class DoorwayDatabaseMigrate { privileged: true, // Required for Docker commands }, environmentVariables: { - DB_SECRET_ARN: { value: dbSecretArn }, + DB_CREDS_ARN: { value: dbSecretArn }, ECR_REGION: { value: Aws.REGION }, ECR_ACCOUNT_ID: { value: Aws.ACCOUNT_ID }, ECR_NAMESPACE: { value: props.ecrNamespace || "doorway" }, diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index 5c7ff7c1090..2c259c256b3 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -13,7 +13,6 @@ env: PGHOST: "${DB_CREDS_ARN}:host" PGDATABASE: "${DB_CREDS_ARN}:db_name" PGPORT: "${DB_CREDS_ARN}:port" - DATABASE_URL: "${DB_CREDS_ARN}:uri" phases: pre_build: @@ -40,7 +39,7 @@ phases: --env PGHOST="${PGHOST}" --env PGDATABASE=template1 --env PGPORT="${PGPORT}" - --env DATABASE_URL="${DATABASE_URL}" + --env DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" --env CLOUDINARY_CLOUD_NAME="${CLOUDINARY_CLOUD_NAME}" --env LISTINGS_QUERY="${LISTINGS_QUERY}" --env FILE_SERVICE="${FILE_SERVICE:-cloudinary}" diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 3ea69fe7a94..4a1184952fb 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -10,6 +10,7 @@ env: PGPASSWORD: "${DB_CREDS_ARN}:password" PGHOST: "${DB_CREDS_ARN}:host" PGPORT: "${DB_CREDS_ARN}:port" + PGDATABASE: "${DB_CREDS_ARN}:dbname" phases: pre_build: From 38702cba778333465e0c13b0e5094bea4d1d6b81 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 15 Aug 2025 16:03:16 -0700 Subject: [PATCH 015/217] fix: fixing migrate.yml --- ci/buildspec/migrate.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index 2c259c256b3..630a65e8088 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -8,11 +8,11 @@ version: 0.2 env: shell: bash secrets-manager: - PGUSER: "${DB_CREDS_ARN}:user" + PGUSER: "${DB_CREDS_ARN}:username" PGPASSWORD: "${DB_CREDS_ARN}:password" PGHOST: "${DB_CREDS_ARN}:host" - PGDATABASE: "${DB_CREDS_ARN}:db_name" PGPORT: "${DB_CREDS_ARN}:port" + PGDATABASE: "${DB_CREDS_ARN}:dbname" phases: pre_build: From ff1cfe81f16c781687a9bc9e381393be43a89be8 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 18 Aug 2025 09:50:38 -0700 Subject: [PATCH 016/217] fix: moving build role to the stack --- .../doorway-build-pipeline-stack.ts | 49 ++++++++++++++- .../build-pipeline/doorway-docker-build.ts | 63 ++++--------------- 2 files changed, 59 insertions(+), 53 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 0824f818993..4b44d82ae77 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -1,7 +1,7 @@ import { Stack, StackProps } from "aws-cdk-lib" import { Artifact, Pipeline } from "aws-cdk-lib/aws-codepipeline" import { GitHubSourceAction } from "aws-cdk-lib/aws-codepipeline-actions" -import { PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import { PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Secret } from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" @@ -32,6 +32,49 @@ export class DoorwayBuildPipelineStack extends Stack { resources: ["*"], }) ) + const buildRole = new Role(this, `${id}-doorway-app-build-role`, { + assumedBy: new ServicePrincipal("codebuild.amazonaws.com"), + managedPolicies: [ + { + managedPolicyArn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess", + }, + { + managedPolicyArn: "arn:aws:iam::aws:policy/AmazonS3FullAccess", + }, + ], + inlinePolicies: { + ECRPolicies: new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: [ + "ecr:BatchGetImage", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchCheckLayerAvailability", + ], + resources: [`arn:aws:ecr:${this.region}:${this.account}:repository/*`], + }), + new PolicyStatement({ + actions: ["secretsmanager:GetSecretValue"], + resources: [ + `arn:aws:secretsmanager:${this.region}:${this.account}:secret:mtc/dockerHub*`, + ], + }), + new PolicyStatement({ + actions: [ + "cloudformation:*", + "ec2:*", + "ssm:*", + "codebuild:*", + "logs:*", + "iam:AssumeRole", + "iam:PassRole", + ], + resources: ["*"], + }), + ], + }), + }, + }) const dockerSecret = Secret.fromSecretNameV2( this, @@ -80,6 +123,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, configSource: configSourceArtifact, dockerHubSecret: dockerSecret, + buildRole: buildRole, }).action, new DoorwayDockerBuild(this, "doorway-import-listings", { buildspec: "../ci/buildspec/build_import_listings.yml", @@ -87,6 +131,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, configSource: configSourceArtifact, dockerHubSecret: dockerSecret, + buildRole: buildRole, }).action, new DoorwayDockerBuild(this, `doorway-partners`, { buildspec: "../ci/buildspec/build_partners.yml", @@ -94,6 +139,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, configSource: configSourceArtifact, dockerHubSecret: dockerSecret, + buildRole: buildRole, }).action, new DoorwayDockerBuild(this, "doorway-public", { buildspec: "../ci/buildspec/build_public.yml", @@ -101,6 +147,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, configSource: configSourceArtifact, dockerHubSecret: dockerSecret, + buildRole: buildRole, }).action, ], }) diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts index c6ad320aea7..6e14b27f054 100644 --- a/cdk/lib/build-pipeline/doorway-docker-build.ts +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -1,9 +1,10 @@ -import { Stack } from "aws-cdk-lib" +import { Aws } from "aws-cdk-lib" import { BuildSpec, ComputeType, PipelineProject } from "aws-cdk-lib/aws-codebuild" import { Artifact } from "aws-cdk-lib/aws-codepipeline" import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" import { Repository } from "aws-cdk-lib/aws-ecr" -import { PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import { Role } from "aws-cdk-lib/aws-iam" +import { Construct } from "constructs/lib/construct" import fs from "fs" import * as YAML from "yaml" @@ -13,77 +14,35 @@ export interface DoorwayDockerBuildProps { source: Artifact configSource?: Artifact dockerHubSecret: string + buildRole: Role } export class DoorwayDockerBuild { public readonly action: CodeBuildAction - constructor(stack: Stack, id: string, props: DoorwayDockerBuildProps) { + constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { // Create ECR repository - const repo = new Repository(stack, `${id}-ECRRepository`, { + const repo = new Repository(scope, `${id}-ECRRepository`, { repositoryName: `doorway/${props.imageName}`, }) - const role = new Role(stack, `${id}-doorway-app-build-role`, { - assumedBy: new ServicePrincipal("codebuild.amazonaws.com"), - managedPolicies: [ - { - managedPolicyArn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess", - }, - { - managedPolicyArn: "arn:aws:iam::aws:policy/AmazonS3FullAccess", - }, - ], - inlinePolicies: { - ECRPolicies: new PolicyDocument({ - statements: [ - new PolicyStatement({ - actions: [ - "ecr:BatchGetImage", - "ecr:GetDownloadUrlForLayer", - "ecr:BatchCheckLayerAvailability", - ], - resources: [`arn:aws:ecr:${stack.region}:${stack.account}:repository/*`], - }), - new PolicyStatement({ - actions: ["secretsmanager:GetSecretValue"], - resources: [ - `arn:aws:secretsmanager:${stack.region}:${stack.account}:secret:mtc/dockerHub*`, - ], - }), - new PolicyStatement({ - actions: [ - "cloudformation:*", - "ec2:*", - "ssm:*", - "codebuild:*", - "logs:*", - "iam:AssumeRole", - "iam:PassRole", - ], - resources: ["*"], - }), - ], - }), - }, - }) // Grant permissions to the provided role - repo.grantPullPush(role) + repo.grantPullPush(props.buildRole) // Create the CodeBuild project - const project = new PipelineProject(stack, `${id}-Project`, { + const project = new PipelineProject(scope, `${id}-Project`, { buildSpec: BuildSpec.fromObject(YAML.parse(fs.readFileSync(props.buildspec, "utf8"))), environment: { computeType: ComputeType.LARGE, }, environmentVariables: { - ECR_REGION: { value: stack.region }, - ECR_ACCOUNT_ID: { value: stack.account }, + ECR_REGION: { value: Aws.REGION }, + ECR_ACCOUNT_ID: { value: Aws.ACCOUNT_ID }, ECR_NAMESPACE: { value: "doorway" }, IMAGE_NAME: { value: props.imageName }, DOCKER_HUB_SECRET_ARN: { value: props.dockerHubSecret }, }, - role: role, + role: props.buildRole, }) // Create the CodeBuild action From d67cb1f2447e669a3ec21c4e8b0657a81608ade0 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 18 Aug 2025 09:54:05 -0700 Subject: [PATCH 017/217] fix: another buildrole move --- .../doorway-build-pipeline-stack.ts | 1 + .../doorway-database-migrate.ts | 55 ++++--------------- 2 files changed, 11 insertions(+), 45 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 4b44d82ae77..0386a1233f6 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -158,6 +158,7 @@ export class DoorwayBuildPipelineStack extends Stack { environment: "dev2", buildspec: "./ci/buildspec/migrate.yml", source: sourceArtifact, + buildRole: buildRole, }).action, ], }) diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts index b9caadce999..959883b662e 100644 --- a/cdk/lib/build-pipeline/doorway-database-migrate.ts +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -1,9 +1,10 @@ -import { Aws, Fn, Stack } from "aws-cdk-lib" +import { Aws, Fn } from "aws-cdk-lib" import { BuildSpec, ComputeType, LinuxBuildImage, PipelineProject } from "aws-cdk-lib/aws-codebuild" import { Artifact } from "aws-cdk-lib/aws-codepipeline" import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" -import { PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import { Role } from "aws-cdk-lib/aws-iam" +import { Construct } from "constructs" // Removed fs and YAML imports since we're using BuildSpec.fromSourceFilename @@ -13,63 +14,27 @@ export interface DoorwayDatabaseMigrateProps { environment: string buildspec: string source: Artifact + buildRole: Role } export class DoorwayDatabaseMigrate { public readonly action: CodeBuildAction - constructor(stack: Stack, id: string, props: DoorwayDatabaseMigrateProps) { + constructor(scope: Construct, id: string, props: DoorwayDatabaseMigrateProps) { const vpcId = Fn.importValue(`doorway-vpc-id-${props.environment}`) const subnetId = Fn.importValue(`doorway-app-subnet-1-${props.environment}`) const securityGroupId = Fn.importValue(`doorway-app-sg-${props.environment}`) const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) const azs = Fn.importValue(`doorway-azs-${props.environment}`).split(", ") - const vpc = Vpc.fromVpcAttributes(stack, "vpc", { + const vpc = Vpc.fromVpcAttributes(scope, "vpc", { vpcId: vpcId, availabilityZones: azs, }) - const subnet = Subnet.fromSubnetAttributes(stack, "subnet", { + const subnet = Subnet.fromSubnetAttributes(scope, "subnet", { subnetId: subnetId, }) - const sg = SecurityGroup.fromSecurityGroupId(stack, "sg", securityGroupId) + const sg = SecurityGroup.fromSecurityGroupId(scope, "sg", securityGroupId) - const buildRole = new Role(stack, "doorway-app-build-role", { - assumedBy: new ServicePrincipal("codebuild.amazonaws.com"), - managedPolicies: [ - { - managedPolicyArn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess", - }, - { - managedPolicyArn: "arn:aws:iam::aws:policy/AmazonS3FullAccess", - }, - ], - inlinePolicies: { - ECRPolicies: new PolicyDocument({ - statements: [ - new PolicyStatement({ - actions: ["ecr:*"], - resources: [`arn:aws:ecr:${Aws.REGION}:${Aws.ACCOUNT_ID}:repository/*`], - }), - new PolicyStatement({ - actions: ["secretsmanager:GetSecretValue"], - resources: [dbSecretArn], - }), - new PolicyStatement({ - actions: [ - "cloudformation:*", - "ec2:*", - "ssm:*", - "codebuild:*", - "logs:*", - "iam:AssumeRole", - "iam:PassRole", - ], - resources: ["*"], - }), - ], - }), - }, - }) - const project = new PipelineProject(stack, `doorway-dbMigrate-${props.environment}`, { + const project = new PipelineProject(scope, `doorway-dbMigrate-${props.environment}`, { vpc: vpc, subnetSelection: { subnets: [subnet] }, securityGroups: [sg], @@ -86,7 +51,7 @@ export class DoorwayDatabaseMigrate { ECR_NAMESPACE: { value: props.ecrNamespace || "doorway" }, PGDATABASE: { value: props.databaseName || "bloom" }, }, - role: buildRole, + role: props.buildRole, }) // Create the CodeBuild action this.action = new CodeBuildAction({ From 4f4054f5639142b5be836f79d19369524e241330 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 10:46:06 -0700 Subject: [PATCH 018/217] feat: api service build --- cdk/bin/doorway-cdk.ts | 2 + cdk/dev2.env | 21 ++ cdk/lib/build-pipeline/doorway-ecs-deploy.ts | 39 ++ cdk/lib/doorway-ecs-services-stack.ts | 222 ------------ cdk/lib/doorway-stack-props.ts | 5 - cdk/lib/doorway_api_service-stack.ts | 339 ------------------ .../doorway-app-environment-stack.ts | 15 + .../doorway-public-service.ts | 222 ++++++++++++ .../doorway-service-props.ts | 3 + .../doorway_environment/doorway_services.ts | 249 +++++++++++++ cdk/package.json | 3 +- cdk/yarn.lock | 5 + ci/buildspec/update_ecs.yml | 11 +- 13 files changed, 563 insertions(+), 573 deletions(-) create mode 100644 cdk/dev2.env create mode 100644 cdk/lib/build-pipeline/doorway-ecs-deploy.ts delete mode 100644 cdk/lib/doorway-ecs-services-stack.ts delete mode 100644 cdk/lib/doorway-stack-props.ts delete mode 100644 cdk/lib/doorway_api_service-stack.ts create mode 100644 cdk/lib/doorway_environment/doorway-app-environment-stack.ts create mode 100644 cdk/lib/doorway_environment/doorway-public-service.ts create mode 100644 cdk/lib/doorway_environment/doorway-service-props.ts create mode 100644 cdk/lib/doorway_environment/doorway_services.ts diff --git a/cdk/bin/doorway-cdk.ts b/cdk/bin/doorway-cdk.ts index 487bef28110..c488a14eff4 100644 --- a/cdk/bin/doorway-cdk.ts +++ b/cdk/bin/doorway-cdk.ts @@ -2,6 +2,7 @@ import * as cdk from "aws-cdk-lib" import { DoorwayBuildPipelineStack } from "../lib/build-pipeline/doorway-build-pipeline-stack" +import { DoorwayAppEnvironmentStack } from "../lib/doorway_environment/doorway-app-environment-stack" const app = new cdk.App() @@ -9,3 +10,4 @@ new DoorwayBuildPipelineStack(app, "DoorwayBuildPipelineStack", { githubSecret: "mtc/githubSecret", dockerHubSecret: "mtc/dockerHub", }) +new DoorwayAppEnvironmentStack(app, "DoorwayAppEnvironmentStack") diff --git a/cdk/dev2.env b/cdk/dev2.env new file mode 100644 index 00000000000..3044b37f65a --- /dev/null +++ b/cdk/dev2.env @@ -0,0 +1,21 @@ +MAINTENANCE_WINDOW=2024-03-26 18:00 -08:00,2024-04-25 18:00 -08:00 +DEST_REPO_NAMESPACE=doorway-dev +CACHE_REVALIDATE=30 +LANGUAGES=en,es,zh,vi,tl +FEATURE_LISTINGS_APPROVAL=TRUE +BACKEND_API_BASE=http://backend.dev.housingbayarea.int +ECR_ACCOUNT_ID=364076391763 +MAPBOX_TOKEN=pk.eyJ1Ijoic2Vhbi1hbGJlcnQtZXh5Z3kiLCJhIjoiY2tybnY5amlyMDNraDJubXc4c3ZhOGswYyJ9.KE5FGb_Tch8BPrOYMvshXw +USE_SECURE_DOWNLOAD_PATHWAY=TRUE +ECR_REGION=us-west-1 +SOURCE_REPO_NAMESPACE=doorway-dev +ECR_NAMESPACE=doorway-dev +API_PASS_KEY=dev-key +IDLE_TIMEOUT=5 +CLOUDINARY_CLOUD_NAME=exygy +API_LOCAL_PORT=3100 +PUBLIC_LOCAL_PORT=3000 +PARTNERS_LOCAL_PORT=3200 +PUBLIC_SECRETS= +PARTNERS_SECRETS= +BACKEND_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVEDELIVERY_API_URL, diff --git a/cdk/lib/build-pipeline/doorway-ecs-deploy.ts b/cdk/lib/build-pipeline/doorway-ecs-deploy.ts new file mode 100644 index 00000000000..2952c6ba4e4 --- /dev/null +++ b/cdk/lib/build-pipeline/doorway-ecs-deploy.ts @@ -0,0 +1,39 @@ +import { BuildSpec, ComputeType, LinuxBuildImage, PipelineProject } from "aws-cdk-lib/aws-codebuild" +import { Artifact } from "aws-cdk-lib/aws-codepipeline" +import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" +import { Role } from "aws-cdk-lib/aws-iam" +import { Construct } from "constructs" + +// Removed fs and YAML imports since we're using BuildSpec.fromSourceFilename + +export interface DoorwayECSDeployProps { + ecrNamespace?: string + databaseName?: string + environment: string + buildspec: string + source: Artifact + buildRole: Role +} + +export class DoorwayECSDeploy { + public readonly action: CodeBuildAction + constructor(scope: Construct, id: string, props: DoorwayECSDeployProps) { + const project = new PipelineProject(scope, `doorway-dbMigrate-${props.environment}`, { + buildSpec: BuildSpec.fromSourceFilename(props.buildspec), + environment: { + buildImage: LinuxBuildImage.STANDARD_7_0, + computeType: ComputeType.LARGE, + privileged: true, // Required for Docker commands + }, + + role: props.buildRole, + }) + // Create the CodeBuild action + this.action = new CodeBuildAction({ + actionName: `${id}-ECSDeploy`, + input: props.source, + outputs: [new Artifact(`${id}-BuildOutput`)], + project, + }) + } +} diff --git a/cdk/lib/doorway-ecs-services-stack.ts b/cdk/lib/doorway-ecs-services-stack.ts deleted file mode 100644 index 05b6fceccad..00000000000 --- a/cdk/lib/doorway-ecs-services-stack.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { Fn, Stack } from "aws-cdk-lib" -import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager" -import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" -import { Repository } from "aws-cdk-lib/aws-ecr" -import { AwsLogDriver, Cluster, ContainerImage, Secret } from "aws-cdk-lib/aws-ecs" -import { ApplicationLoadBalancedFargateService } from "aws-cdk-lib/aws-ecs-patterns" -import { SslPolicy } from "aws-cdk-lib/aws-elasticloadbalancingv2" -import { ManagedPolicy, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" -import { LogGroup } from "aws-cdk-lib/aws-logs" -import { PublicHostedZone } from "aws-cdk-lib/aws-route53" -import * as secret from "aws-cdk-lib/aws-secretsmanager" -import { StringParameter } from "aws-cdk-lib/aws-ssm" -import { Construct } from "constructs" - -import { DoorwayStackProps } from "./doorway-stack-props" - -export class DoorwayEcsServicesStack extends Stack { - constructor(scope: Construct, id: string, props: DoorwayStackProps) { - super(scope, id, props) - // Create the execution role for the doorway API service - const executionRole = new Role(this, "executionRole", { - assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), - }) - executionRole.addManagedPolicy( - ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy") - ) - executionRole.addManagedPolicy( - ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess") - ) - executionRole.addManagedPolicy( - ManagedPolicy.fromAwsManagedPolicyName("AmazonRDSReadOnlyAccess") - ) - executionRole.addManagedPolicy( - ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly") - ) - const hostedZone = PublicHostedZone.fromHostedZoneAttributes( - this, - `doorway-public-hosted-zone-${props.environment}`, - { - hostedZoneId: StringParameter.valueForStringParameter(this, `/doorway/public-hosted-zone`), - zoneName: `housingbayarea.mtc.ca.gov`, - } - ) - const cert = new Certificate(this, "doorwayPublicCert", { - domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, - validation: { - method: CertificateValidation.fromDns().method, - props: { - hostedZone: hostedZone, - }, - }, - }) - - new ApplicationLoadBalancedFargateService( - this, - `doorway-public-portal-service-${props.environment}`, - { - cluster: Cluster.fromClusterAttributes(this, `doorway-ecs-cluster-${props.environment}`, { - clusterName: `doorway-ecs-cluster-${props.environment}`, - vpc: Vpc.fromVpcAttributes(this, "vpc", { - vpcId: Fn.importValue(`doorway-vpc-id-${props.environment}`), - availabilityZones: Fn.importValue(`doorway-azs-${props.environment}`).split(","), - publicSubnetIds: [ - Fn.importValue(`doorway-public-subnet-1-${props.environment}`), - Fn.importValue(`doorway-public-subnet-2-${props.environment}`), - ], - }), - securityGroups: [ - SecurityGroup.fromSecurityGroupId( - this, - `doorway-app-sg-${props.environment}`, - Fn.importValue(`doorway-app-sg-${props.environment}`) - ), - ], - }), - taskSubnets: { - subnets: [ - Subnet.fromSubnetId( - this, - `doorway-app-subnet-1-${props.environment}`, - Fn.importValue(`doorway-app-subnet-1-${props.environment}`) - ), - Subnet.fromSubnetId( - this, - `doorway-app-subnet-2-${props.environment}`, - Fn.importValue(`doorway-app-subnet-2-${props.environment}`) - ), - ], - }, - - assignPublicIp: true, - publicLoadBalancer: true, - taskImageOptions: { - image: ContainerImage.fromEcrRepository( - Repository.fromRepositoryName( - this, - `doorway-public-portal-repo-${props.environment}`, - `doorway/public` - ), - "run-f21478f5" - ), - enableLogging: true, - containerPort: 3000, - executionRole: executionRole, - taskRole: executionRole, - secrets: { - BLOOM_API_BASE: Secret.fromSsmParameter( - StringParameter.fromStringParameterName( - this, - "BLOOM_API_BASE", - `/doorway/${props.environment}/public-portal/BLOOM_API_BASE` - ) - ), - CACHE_REVALIDATE: Secret.fromSsmParameter( - StringParameter.fromStringParameterName( - this, - "CACHE_REVALIDATE", - `/doorway/${props.environment}/public-portal/CACHE_REVALIDATE` - ) - ), - GOOGLE_API_KEY: Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2(this, "googleApiKey", "/doorway/GOOGLE_API_KEY") - ), - GOOGLE_MAPS_API_KEY: Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2( - this, - "googleMapsApiKey", - "/doorway/GOOGLE_MAPS_API_KEY" - ) - ), - GOOGLE_MAPS_MAP_ID: Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2(this, "googleMapsMapId", "/doorway/GOOGLE_MAPS_MAP_ID") - ), - GTM_KEY: Secret.fromSsmParameter( - StringParameter.fromStringParameterName( - this, - "GTM_KEY", - `/doorway/${props.environment}/public-portal/GTM_KEY` - ) - ), - IDLE_TIMEOUT: Secret.fromSsmParameter( - StringParameter.fromStringParameterName( - this, - "IDLE_TIMEOUT", - `/doorway/${props.environment}/public-portal/IDLE_TIMEOUT` - ) - ), - JURISDICTION_NAME: Secret.fromSsmParameter( - StringParameter.fromStringParameterName( - this, - "JURISDICTION_NAME", - `/doorway/${props.environment}/public-portal/JURISDICTION_NAME` - ) - ), - LANGUAGES: Secret.fromSsmParameter( - StringParameter.fromStringParameterName( - this, - "LANGUAGES", - `/doorway/${props.environment}/public-portal/LANGUAGES` - ) - ), - LISTINGS_QUERY: Secret.fromSsmParameter( - StringParameter.fromStringParameterName( - this, - "LISTINGS_QUERY", - `/doorway/${props.environment}/public-portal/LISTINGS_QUERY` - ) - ), - NEXTJS_PORT: Secret.fromSsmParameter( - StringParameter.fromStringParameterName( - this, - "NEXTJS_PORT", - `/doorway/${props.environment}/public-portal/NEXTJS_PORT` - ) - ), - NOTIFICATIONS_SIGN_UP_URL: Secret.fromSsmParameter( - StringParameter.fromStringParameterName( - this, - "NOTIFICATIONS_SIGN_UP_URL", - `/doorway/${props.environment}/public-portal/NOTIFICATIONS_SIGN_UP_URL` - ) - ), - SHOW_ALL_MAP_PINS: Secret.fromSsmParameter( - StringParameter.fromStringParameterName( - this, - "SHOW_ALL_MAP_PINS", - `/doorway/${props.environment}/public-portal/SHOW_ALL_MAP_PINS` - ) - ), - SHOW_ALL_PROFESSIONAL_PARTNERS: Secret.fromSsmParameter( - StringParameter.fromStringParameterName( - this, - "SHOW_PROFESSIONAL_PARTNERS", - `/doorway/${props.environment}/public-portal/SHOW_PROFESSIONAL_PARTNERS` - ) - ), - }, - environment: { - BACKEND_API_BASE: `http://backend.${props.environment}.housingbayarea.int`, - }, - logDriver: AwsLogDriver.awsLogs({ - streamPrefix: "doorway-public-portal", - logGroup: new LogGroup(this, `doorway-${props.environment}-tasks`, { - logGroupName: `/doorway/${props.environment}/ecs/doorway-public-portal`, - }), - }), - }, - - certificate: cert, - listenerPort: 443, - redirectHTTP: true, - sslPolicy: SslPolicy.RECOMMENDED, - serviceName: `doorway-public-portal-service-${props.environment}`, - loadBalancerName: `doorway-public-lb-${props.environment}`, - cpu: 1024, - memoryLimitMiB: 2048, - domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, - domainZone: hostedZone, - } - ) - } -} diff --git a/cdk/lib/doorway-stack-props.ts b/cdk/lib/doorway-stack-props.ts deleted file mode 100644 index b2334529c66..00000000000 --- a/cdk/lib/doorway-stack-props.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { StackProps } from "aws-cdk-lib" - -export interface DoorwayStackProps extends StackProps { - environment: string -} diff --git a/cdk/lib/doorway_api_service-stack.ts b/cdk/lib/doorway_api_service-stack.ts deleted file mode 100644 index 06a30a70342..00000000000 --- a/cdk/lib/doorway_api_service-stack.ts +++ /dev/null @@ -1,339 +0,0 @@ -import * as cdk from "aws-cdk-lib" -import { Fn } from "aws-cdk-lib" -import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2" -import { - Cluster, - Compatibility, - ContainerImage, - FargateService, - LogDrivers, - NetworkMode, - Protocol, - Secret, - TaskDefinition, -} from "aws-cdk-lib/aws-ecs" -//import * as ecs from "aws-cdk-lib/aws-ecs"; -import { ManagedPolicy, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" -import { LogGroup } from "aws-cdk-lib/aws-logs" -import { Bucket } from "aws-cdk-lib/aws-s3" -import * as secret from "aws-cdk-lib/aws-secretsmanager" -import { EmailIdentity } from "aws-cdk-lib/aws-ses" -import { StringParameter } from "aws-cdk-lib/aws-ssm" -import { Construct } from "constructs" - -import { DoorwayStackProps } from "./doorway-stack-props" - -export class DoorwayApiServiceStack extends cdk.Stack { - public constructor( - scope: Construct, - id: string, - props: DoorwayStackProps = { - environment: "dev2", - env: { - account: process.env.CDK_DEFAULT_ACCOUNT || "none", - region: process.env.CDK_DEFAULT_REGION || "none", - }, - } - ) { - super(scope, id, props) - - const vpcId = cdk.Fn.importValue(`doorway-vpc-id-${props.environment}`) - const azs = cdk.Fn.importValue(`doorway-azs-${props.environment}`).split(",") - const vpc = cdk.aws_ec2.Vpc.fromVpcAttributes(this, "vpc", { - vpcId: vpcId, - availabilityZones: azs, - }) - - // Get the subnets - const appSubnetIds: string[] = [] - appSubnetIds.push(cdk.Fn.importValue(`doorway-app-subnet-1-${props.environment}`)) - appSubnetIds.push(cdk.Fn.importValue(`doorway-app-subnet-2-${props.environment}`)) - const appSubnets: ISubnet[] = [] - appSubnetIds.forEach((id) => { - appSubnets.push(Subnet.fromSubnetId(this, id, id)) - }) - const appTierPrivateSGId = cdk.Fn.importValue(`doorway-app-sg-${props.environment}`) - const appTierPrivateSG = cdk.aws_ec2.SecurityGroup.fromSecurityGroupId( - this, - "appTierPrivateSG", - appTierPrivateSGId - ) - - const publicUploadsBucket = Bucket.fromBucketArn( - this, - `publicUploadsBucket`, - Fn.importValue(`doorway-public-uploads-${props.environment}`) - ) - const secureUploadsBucket = Bucket.fromBucketArn( - this, - `secureUploadsBucket`, - Fn.importValue(`doorway-secure-uploads-${props.environment}`) - ) - // Get the SES Email Information - const sesIdentity = EmailIdentity.fromEmailIdentityName( - this, - "sesIdentity", - "housingbayarea2.org" - ) - - // Create the execution role for the doorway API service - const executionRole = new Role(this, "executionRole", { - assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), - }) - executionRole.addManagedPolicy( - ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy") - ) - executionRole.addManagedPolicy( - ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess") - ) - executionRole.addManagedPolicy( - ManagedPolicy.fromAwsManagedPolicyName("AmazonRDSReadOnlyAccess") - ) - executionRole.addManagedPolicy( - ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly") - ) - publicUploadsBucket.grantReadWrite(executionRole) - publicUploadsBucket.grantPut(executionRole) - secureUploadsBucket.grantReadWrite(executionRole) - secureUploadsBucket.grantPut(executionRole) - sesIdentity.grantSendEmail(executionRole) - // Add a bunch of random SES grants that grantSendEmail doesn't set up - const policy = new PolicyStatement({ - actions: [ - "ses:SendEmail", - "ses:SendRawEmail", - "ses:SendTemplatedEmail", - "ses:SendRawTemplatedEmail", - "ses:SendBulkTemplatedEmail", - "ses:UseConfiguration", - "ses:SendBulkEmail", - ], - resources: [ - sesIdentity.emailIdentityArn, - `arn:aws:ses:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:configuration-set/dway-config-set`, - `arn:aws:ses:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:identity/*`, - ], - }) - executionRole.addToPolicy(policy) - const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) - const dbSecret = secret.Secret.fromSecretCompleteArn(this, "dbSecret", dbSecretArn) - dbSecret.grantRead(executionRole) - const logGroup = new LogGroup(this, `doorway-${props.environment}-tasks`, { - logGroupName: `doorway-${props.environment}-tasks`, - removalPolicy: cdk.RemovalPolicy.DESTROY, - retention: cdk.aws_logs.RetentionDays.ONE_WEEK, - }) - logGroup.grantWrite(executionRole) - - // Now we're down to business! Set up the fargate container task for the api - // Notice the load of environment variables we add to it below - const task = new TaskDefinition(this, "apiTask", { - compatibility: Compatibility.FARGATE, - cpu: "2048", - memoryMiB: "4096", - executionRole: executionRole, - taskRole: executionRole, - networkMode: NetworkMode.AWS_VPC, - }) - task.addContainer("internal-api", { - image: ContainerImage.fromRegistry( - `${cdk.Aws.ACCOUNT_ID}.dkr.ecr.${cdk.Aws.REGION}.amazonaws.com/doorway/backend:run-candidate` - ), - cpu: 1, - memoryLimitMiB: 1024, - essential: true, - logging: LogDrivers.awsLogs({ - streamPrefix: "internal-api", - logGroup: LogGroup.fromLogGroupName(this, "logGroup", `doorway-${props.environment}-tasks`), - }), - - secrets: { - GOOGLE_API_ID: Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2(this, "googleApiId", "/doorway/GOOGLE_API_ID") - ), - GOOGLE_API_EMAIL: Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2(this, "googleApiEmail", "/doorway/GOOGLE_API_EMAIL") - ), - GOOGLE_API_KEY: Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2(this, "googleApiKey", "/doorway/GOOGLE_API_KEY") - ), - - PGUSER: Secret.fromSecretsManager(dbSecret, "username"), - PGPASSWORD: Secret.fromSecretsManager(dbSecret, "password"), - PGPORT: Secret.fromSecretsManager(dbSecret, "port"), - PGHOST: Secret.fromSecretsManager(dbSecret, "host"), - THROTTLE_LIMIT: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "THROTTLE_LIMIT", { - parameterName: `/doorway/${props.environment}/internal-api/THROTTLE_LIMIT`, - }) - ), - LOG_LEVEL: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "LOG_LEVEL", { - parameterName: `/doorway/${props.environment}/internal-api/LOG_LEVEL`, - }) - ), - - LISTING_PROCESSING_CRON_STRING: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "LISTING_PROCESSING_CRON_STRING", { - parameterName: `/doorway/${props.environment}/internal-api/LISTING_PROCESSING_CRON_STRING`, - }) - ), - LOTTERY_PROCESSING_CRON_STRING: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "LOTTERY_PROCESSING_CRON_STRING", { - parameterName: `/doorway/${props.environment}/internal-api/LOTTERY_PROCESSING_CRON_STRING`, - }) - ), - LOTTERY_PUBLISH_PROCESSING_CRON_STRING: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes( - this, - "LOTTERY_PUBLISH_PROCESSING_CRON_STRING", - { - parameterName: `/doorway/${props.environment}/internal-api/LOTTERY_PUBLISH_PROCESSING_CRON_STRING`, - } - ) - ), - AFS_PROCESSING_CRON_STRING: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "AFS_PROCESSING_CRON_STRING", { - parameterName: `/doorway/${props.environment}/internal-api/AFS_PROCESSING_CRON_STRING`, - }) - ), - TEMP_FILE_CLEAR_CRON_STRING: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "TEMP_FILE_CLEAR_CRON_STRING", { - parameterName: `/doorway/${props.environment}/internal-api/TEMP_FILE_CLEAR_CRON_STRING`, - }) - ), - LOTTERY_DAYS_TILL_EXPIRY: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "LOTTERY_DAYS_TILL_EXPIRY", { - parameterName: `/doorway/${props.environment}/internal-api/LOTTERY_DAYS_TILL_EXPIRY`, - }) - ), - MFA_CODE_LENGTH: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "MFA_CODE_LENGTH", { - parameterName: `/doorway/${props.environment}/internal-api/MFA_CODE_LENGTH`, - }) - ), - MFA_CODE_VALID: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "MFA_CODE_VALID", { - parameterName: `/doorway/${props.environment}/internal-api/MFA_CODE_VALID`, - }) - ), - - GOVDELIVERY_TOPIC: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "GOVDELIVERY_TOPIC", { - parameterName: `/doorway/${props.environment}/internal-api/GOVDELIVERY_TOPIC`, - }) - ), - - PARTNERS_PORTAL_URL: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "PARTNERS_PORTAL_URL", { - parameterName: `/doorway/${props.environment}/internal-api/PARTNERS_PORTAL_URL`, - }) - ), - PARTNERS_BASE_URL: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "PARTNERS_PORTAL_BASE_URL", { - parameterName: `/doorway/${props.environment}/internal-api/PARTNERS_BASE_URL`, - }) - ), - THROTTLE_TTL: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "THROTTLE_TTL", { - parameterName: `/doorway/${props.environment}/internal-api/THROTTLE_TTL`, - }) - ), - - ASSET_UPLOAD_MAX_SIZE: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "ASSET_UPLOAD_MAX_SIZE", { - parameterName: `/doorway/${props.environment}/internal-api/ASSET_UPLOAD_MAX_SIZE`, - }) - ), - AUTH_LOCK_LOGIN_COOLDOWN: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "AUTH_LOCK_LOGIN_COOLDOWN", { - parameterName: `/doorway/${props.environment}/internal-api/AUTH_LOCK_LOGIN_COOLDOWN`, - }) - ), - AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes( - this, - "AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS", - { - parameterName: `/doorway/${props.environment}/internal-api/AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS`, - } - ) - ), - CORS_ORIGINS: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "CORS_ORIGINS", { - parameterName: `/doorway/${props.environment}/internal-api/CORS_ORIGINS`, - }) - ), - DUPLICATES_CLOSE_DATE: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "DUPLICATES_CLOSE_DATE", { - parameterName: `/doorway/${props.environment}/internal-api/DUPLICATES_CLOSE_DATE`, - }) - ), - DUPLICATES_PROCESSING_CRON_STRING: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "DUPLICATES_PROCESSING_CRON_STRING", { - parameterName: `/doorway/${props.environment}/internal-api/DUPLICATES_PROCESSING_CRON_STRING`, - }) - ), - HTTPS_OFF: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "HTTPS_OFF", { - parameterName: `/doorway/${props.environment}/internal-api/HTTPS_OFF`, - }) - ), - SAME_SITE: Secret.fromSsmParameter( - StringParameter.fromStringParameterAttributes(this, "SAME_SITE", { - parameterName: `/doorway/${props.environment}/internal-api/SAME_SITE`, - }) - ), - }, - environment: { - APP_SECRET: "", - ASSET_FILE_SERVICE: "s3", - LISTINGS_PROCESSING_QUERY: "/listings", - PORT: "3100", - SHOW_LM_LINKS: "TRUE", - TIME_ZONE: "America/Los_Angeles", - SHOW_DUPLICATES: "FALSE", - NO_COLOR: "TRUE", - ASSET_FS_CONFIG_s3_PATH_PREFIX: "", - ASSET_FS_PRIVATE_CONFIG_s3_BUCKET: secureUploadsBucket.bucketName, - ASSET_FS_CONFIG_s3_BUCKET: publicUploadsBucket.bucketName, - ASSET_FS_CONFIG_s3_REGION: cdk.Aws.REGION, - ASSET_FS_CONFIG_s3_URL_FORMAT: "public", - PGDATABASE: "bloom", - }, - entryPoint: [], - portMappings: [ - { - containerPort: 3100, - protocol: Protocol.TCP, - hostPort: 3100, - }, - ], - }) - - // Create the service in ECS - const service = new FargateService(this, `doorway-${props.environment}-internal-api`, { - taskDefinition: task, - serviceName: `doorway-${props.environment}-internal-api`, - cluster: Cluster.fromClusterAttributes(this, "ecsCluster", { - clusterName: Fn.importValue(`doorway-ecs-cluster-${props.environment}`), - vpc: vpc, - }), - vpcSubnets: { - subnets: appSubnets, - }, - securityGroups: [appTierPrivateSG], - desiredCount: 3, - }) - - const scaling = service.autoScaleTaskCount({ - minCapacity: 2, - maxCapacity: 10, - }) - scaling.scaleOnCpuUtilization("CpuScaling", { - targetUtilizationPercent: 80, - scaleInCooldown: cdk.Duration.seconds(60), - scaleOutCooldown: cdk.Duration.seconds(60), - }) - } -} diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts new file mode 100644 index 00000000000..fd9b0311a91 --- /dev/null +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -0,0 +1,15 @@ +import { Stack } from "aws-cdk-lib" +import { Construct } from "constructs" +import dotenv from "dotenv" +import { DoorwayApiService } from "./doorway_services" +export class DoorwayAppEnvironmentStack extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id) + const environment = process.env.ENVIRONMENT || "dev2" + dotenv.config({ path: `${environment}.env` }) + + new DoorwayApiService(this, `doorway-api-service-${environment}`, { + environment: environment, + }) + } +} diff --git a/cdk/lib/doorway_environment/doorway-public-service.ts b/cdk/lib/doorway_environment/doorway-public-service.ts new file mode 100644 index 00000000000..059cd76575b --- /dev/null +++ b/cdk/lib/doorway_environment/doorway-public-service.ts @@ -0,0 +1,222 @@ +// import { Fn } from "aws-cdk-lib" +// import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager" +// import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" +// import { Repository } from "aws-cdk-lib/aws-ecr" +// import { AwsLogDriver, Cluster, ContainerImage, Secret } from "aws-cdk-lib/aws-ecs" +// import { ApplicationLoadBalancedFargateService } from "aws-cdk-lib/aws-ecs-patterns" +// import { SslPolicy } from "aws-cdk-lib/aws-elasticloadbalancingv2" +// import { ManagedPolicy, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +// import { LogGroup } from "aws-cdk-lib/aws-logs" +// import { PublicHostedZone } from "aws-cdk-lib/aws-route53" +// import * as secret from "aws-cdk-lib/aws-secretsmanager" +// import { StringParameter } from "aws-cdk-lib/aws-ssm" +// import { Construct } from "constructs" + +// export class DoorwayPublicService { +// public service: ApplicationLoadBalancedFargateService +// constructor(scope: Construct, id: string, props: DoorwayStackProps) { +// // Create the execution role for the doorway API service +// const executionRole = new Role(scope, "executionRole", { +// assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), +// }) +// executionRole.addManagedPolicy( +// ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy") +// ) +// executionRole.addManagedPolicy( +// ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess") +// ) + +// executionRole.addManagedPolicy( +// ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly") +// ) +// const hostedZone = PublicHostedZone.fromHostedZoneAttributes( +// scope, +// `doorway-public-hosted-zone-${props.environment}`, +// { +// hostedZoneId: StringParameter.valueForStringParameter(scope, `/doorway/public-hosted-zone`), +// zoneName: `housingbayarea.mtc.ca.gov`, +// } +// ) +// const cert = new Certificate(scope, "doorwayPublicCert", { +// domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, +// validation: { +// method: CertificateValidation.fromDns().method, +// props: { +// hostedZone: hostedZone, +// }, +// }, +// }) + +// new ApplicationLoadBalancedFargateService( +// scope, +// `doorway-public-portal-service-${props.environment}`, +// { +// cluster: Cluster.fromClusterAttributes(scope, `doorway-ecs-cluster-${props.environment}`, { +// clusterName: `doorway-ecs-cluster-${props.environment}`, +// vpc: Vpc.fromVpcAttributes(scope, "vpc", { +// vpcId: Fn.importValue(`doorway-vpc-id-${props.environment}`), +// availabilityZones: Fn.importValue(`doorway-azs-${props.environment}`).split(","), +// publicSubnetIds: [ +// Fn.importValue(`doorway-public-subnet-1-${props.environment}`), +// Fn.importValue(`doorway-public-subnet-2-${props.environment}`), +// ], +// }), +// securityGroups: [ +// SecurityGroup.fromSecurityGroupId( +// scope, +// `doorway-app-sg-${props.environment}`, +// Fn.importValue(`doorway-app-sg-${props.environment}`) +// ), +// ], +// }), +// taskSubnets: { +// subnets: [ +// Subnet.fromSubnetId( +// scope, +// `doorway-app-subnet-1-${props.environment}`, +// Fn.importValue(`doorway-app-subnet-1-${props.environment}`) +// ), +// Subnet.fromSubnetId( +// scope, +// `doorway-app-subnet-2-${props.environment}`, +// Fn.importValue(`doorway-app-subnet-2-${props.environment}`) +// ), +// ], +// }, + +// assignPublicIp: true, +// publicLoadBalancer: true, +// taskImageOptions: { +// image: ContainerImage.fromEcrRepository( +// Repository.fromRepositoryName( +// scope, +// `doorway-public-portal-repo-${props.environment}`, +// `doorway/public` +// ), +// "run-f21478f5" +// ), +// enableLogging: true, +// containerPort: 3000, +// executionRole: executionRole, +// taskRole: executionRole, +// secrets: { +// BLOOM_API_BASE: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "BLOOM_API_BASE", +// `/doorway/${props.environment}/public-portal/BLOOM_API_BASE` +// ) +// ), +// CACHE_REVALIDATE: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "CACHE_REVALIDATE", +// `/doorway/${props.environment}/public-portal/CACHE_REVALIDATE` +// ) +// ), +// GOOGLE_API_KEY: Secret.fromSecretsManager( +// secret.Secret.fromSecretNameV2(scope, "googleApiKey", "/doorway/GOOGLE_API_KEY") +// ), +// GOOGLE_MAPS_API_KEY: Secret.fromSecretsManager( +// secret.Secret.fromSecretNameV2( +// scope, +// "googleMapsApiKey", +// "/doorway/GOOGLE_MAPS_API_KEY" +// ) +// ), +// GOOGLE_MAPS_MAP_ID: Secret.fromSecretsManager( +// secret.Secret.fromSecretNameV2( +// scope, +// "googleMapsMapId", +// "/doorway/GOOGLE_MAPS_MAP_ID" +// ) +// ), +// GTM_KEY: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "GTM_KEY", +// `/doorway/${props.environment}/public-portal/GTM_KEY` +// ) +// ), +// IDLE_TIMEOUT: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "IDLE_TIMEOUT", +// `/doorway/${props.environment}/public-portal/IDLE_TIMEOUT` +// ) +// ), +// JURISDICTION_NAME: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "JURISDICTION_NAME", +// `/doorway/${props.environment}/public-portal/JURISDICTION_NAME` +// ) +// ), +// LANGUAGES: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "LANGUAGES", +// `/doorway/${props.environment}/public-portal/LANGUAGES` +// ) +// ), +// LISTINGS_QUERY: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "LISTINGS_QUERY", +// `/doorway/${props.environment}/public-portal/LISTINGS_QUERY` +// ) +// ), +// NEXTJS_PORT: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "NEXTJS_PORT", +// `/doorway/${props.environment}/public-portal/NEXTJS_PORT` +// ) +// ), +// NOTIFICATIONS_SIGN_UP_URL: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "NOTIFICATIONS_SIGN_UP_URL", +// `/doorway/${props.environment}/public-portal/NOTIFICATIONS_SIGN_UP_URL` +// ) +// ), +// SHOW_ALL_MAP_PINS: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "SHOW_ALL_MAP_PINS", +// `/doorway/${props.environment}/public-portal/SHOW_ALL_MAP_PINS` +// ) +// ), +// SHOW_ALL_PROFESSIONAL_PARTNERS: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "SHOW_PROFESSIONAL_PARTNERS", +// `/doorway/${props.environment}/public-portal/SHOW_PROFESSIONAL_PARTNERS` +// ) +// ), +// }, +// environment: { +// BACKEND_API_BASE: `http://backend.${props.environment}.housingbayarea.int`, +// }, +// logDriver: AwsLogDriver.awsLogs({ +// streamPrefix: "doorway-public-portal", +// logGroup: new LogGroup(scope, `doorway-${props.environment}-tasks`, { +// logGroupName: `/doorway/${props.environment}/ecs/doorway-public-portal`, +// }), +// }), +// }, + +// certificate: cert, +// listenerPort: 443, +// redirectHTTP: true, +// sslPolicy: SslPolicy.RECOMMENDED, +// serviceName: `doorway-public-portal-service-${props.environment}`, +// loadBalancerName: `doorway-public-lb-${props.environment}`, +// cpu: 1024, +// memoryLimitMiB: 2048, +// domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, +// domainZone: hostedZone, +// } +// ) +// } +// } diff --git a/cdk/lib/doorway_environment/doorway-service-props.ts b/cdk/lib/doorway_environment/doorway-service-props.ts new file mode 100644 index 00000000000..3c6dbc1bfe6 --- /dev/null +++ b/cdk/lib/doorway_environment/doorway-service-props.ts @@ -0,0 +1,3 @@ +export interface DoorwayServiceProps { + environment: string +} diff --git a/cdk/lib/doorway_environment/doorway_services.ts b/cdk/lib/doorway_environment/doorway_services.ts new file mode 100644 index 00000000000..109b86b36ce --- /dev/null +++ b/cdk/lib/doorway_environment/doorway_services.ts @@ -0,0 +1,249 @@ +import * as cdk from "aws-cdk-lib" +import { Fn } from "aws-cdk-lib" +import { ISecurityGroup, ISubnet, IVpc, Subnet } from "aws-cdk-lib/aws-ec2" +import { + Cluster, + Compatibility, + ContainerImage, + FargateService, + LogDrivers, + NetworkMode, + Protocol, + Secret, + TaskDefinition, +} from "aws-cdk-lib/aws-ecs" +//import * as ecs from "aws-cdk-lib/aws-ecs"; +import { ManagedPolicy, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import { LogGroup } from "aws-cdk-lib/aws-logs" +import { Bucket, IBucket } from "aws-cdk-lib/aws-s3" +import * as secret from "aws-cdk-lib/aws-secretsmanager" +import { EmailIdentity } from "aws-cdk-lib/aws-ses" +import { Construct } from "constructs" +import { DoorwayServiceProps } from "./doorway-service-props" + +abstract class DoorwayService { + protected executionRole: Role + protected privateSG: ISecurityGroup + protected publicSG: ISecurityGroup + protected vpc: IVpc + protected appsubnets: ISubnet[] + protected publicSubnets: ISubnet[] + protected publicUploads: IBucket + protected secureUploads: IBucket + + constructor(scope: Construct, id: string, props: DoorwayServiceProps) { + // Initial Execution Role Setup + // Create the execution role for the doorway API service + this.executionRole = new Role(scope, "executionRole", { + assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), + }) + this.executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy") + ) + this.executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess") + ) + this.executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("AmazonRDSReadOnlyAccess") + ) + this.executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly") + ) + // Get Network setup + const vpcId = cdk.Fn.importValue(`doorway-vpc-id-${props.environment}`) + const azs = cdk.Fn.importValue(`doorway-azs-${props.environment}`).split(",") + this.vpc = cdk.aws_ec2.Vpc.fromVpcAttributes(scope, "vpc", { + vpcId: vpcId, + availabilityZones: azs, + }) + // Get the subnets + const appSubnetIds: string[] = [] + appSubnetIds.push(cdk.Fn.importValue(`doorway-app-subnet-1-${props.environment}`)) + appSubnetIds.push(cdk.Fn.importValue(`doorway-app-subnet-2-${props.environment}`)) + appSubnetIds.forEach((id) => { + this.appsubnets.push(Subnet.fromSubnetId(scope, id, id)) + }) + const appTierPrivateSGId = cdk.Fn.importValue(`doorway-app-sg-${props.environment}`) + this.privateSG = cdk.aws_ec2.SecurityGroup.fromSecurityGroupId( + scope, + "appTierPrivateSG", + appTierPrivateSGId + ) + + //Uploads Buckets + this.publicUploads = Bucket.fromBucketArn( + scope, + `publicUploadsBucket`, + Fn.importValue(`doorway-public-uploads-${props.environment}`) + ) + this.secureUploads = Bucket.fromBucketArn( + scope, + `secureUploadsBucket`, + Fn.importValue(`doorway-secure-uploads-${props.environment}`) + ) + } +} + +export class DoorwayApiService extends DoorwayService { + public constructor(scope: Construct, id: string, props: DoorwayServiceProps) { + super(scope, id, props) + const environmentVariables = { + ASSET_FILE_SERVICE: process.env.ASSET_FILE_SERVICE || "s3", + LISTINGS_PROCESSING_QUERY: process.env.LISTINGS_PROCESSING_QUERY || "/listings", + PORT: process.env.API_LOCAL_PORT || "3100", + SHOW_LM_LINKS: process.env.SHOW_LM_LINKS || "TRUE", + TIME_ZONE: process.env.TIME_ZONE || "America/Los_Angeles", + SHOW_DUPLICATES: process.env.SHOW_DUPLICATES || "FALSE", + NO_COLOR: process.env.NO_COLOR || "TRUE", + ASSET_FS_CONFIG_s3_PATH_PREFIX: process.env.ASSET_FS_CONFIG_s3_PATH_PREFIX || "", + ASSET_FS_PRIVATE_CONFIG_s3_BUCKET: this.secureUploads.bucketName, + ASSET_FS_CONFIG_s3_BUCKET: this.publicUploads.bucketName, + ASSET_FS_CONFIG_s3_REGION: cdk.Aws.REGION, + ASSET_FS_CONFIG_s3_URL_FORMAT: process.env.ASSET_FS_CONFIG_s3_URL_FORMAT || "public", + THROTTLE_LIMIT: process.env.THROTTLE_LIMIT || "100", + THROTTLE_TTL: process.env.THROTTLE_TTL || "180000", + LOG_LEVEL: process.env.LOG_LEVEL || "info", + LISTING_PROCESSING_CRON_STRING: process.env.LISTING_PROCESSING_CRON_STRING || "0 * * * *", + LOTTERY_PROCESSING_CRON_STRING: process.env.LOTTERY_PROCESSING_CRON_STRING || "0 * * * *", + LOTTERY_PUBLISH_PROCESSING_CRON_STRING: + process.env.LOTTERY_PUBLISH_PROCESSING_CRON_STRING || "58 23 * * *", + AFS_PROCESSING_CRON_STRING: process.env.AFS_PROCESSING_CRON_STRING || "15 * * * *", + TEMP_FILE_CLEAR_CRON_STRING: process.env.TEMP_FILE_CLEAR_CRON_STRING || "30 * * * *", + DUPLICATES_PROCESSING_CRON_STRING: + process.env.DUPLICATES_PROCESSING_CRON_STRING || "5 * * * *", + LOTTERY_DAYS_TILL_EXPIRY: process.env.LOTTERY_DAYS_TILL_EXPIRY || "45", + MFA_CODE_LENGTH: process.env.MFA_CODE_LENGTH || "5", + MFA_CODE_VALID: process.env.MFA_CODE_VALID || "600000", + GOVDELIVERY_TOPIC: process.env.GOVDELIVERY_TOPIC || "5 * * * *", + PARTNERS_PORTAL_URL: + process.env.PARTNERS_PORTAL_URL || + `https://partners.${props.environment}.housingbayarea.mtc.ca.gov`, + PARTNERS_BASE_URL: + process.env.PARTNERS_BASE_URL || + `https://partners.${props.environment}.housingbayarea.mtc.ca.gov/api`, + ASSET_UPLOAD_MAX_SIZE: process.env.ASSET_UPLOAD_MAX_SIZE || "5", + AUTH_LOCK_LOGIN_COOLDOWN: process.env.AUTH_LOCK_LOGIN_COOLDOWN || "1800000", + AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS: + process.env.AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS || "5", + CORS_ORIGINS: + process.env.CORS_ORIGINS || + "https://housingbayarea.mtc.ca.gov, https://partners.housingbayarea.mtc.ca.gov", + DUPLICATES_CLOSE_DATE: process.env.DUPLICATES_CLOSE_DATE || "2024-10-08 00:00 -08:00", + HTTPS_OFF: process.env.HTTPS_OFF || "TRUE", + SAME_SITE: process.env.SAME_SITE || "false", + } + const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) + const dbSecret = secret.Secret.fromSecretCompleteArn(scope, "dbSecret", dbSecretArn) + dbSecret.grantRead(this.executionRole) + const secrets: { [key: string]: Secret } = { + PGUSER: Secret.fromSecretsManager(dbSecret, "username"), + PGPASSWORD: Secret.fromSecretsManager(dbSecret, "password"), + PGPORT: Secret.fromSecretsManager(dbSecret, "port"), + PGHOST: Secret.fromSecretsManager(dbSecret, "host"), + PGDATABASE: Secret.fromSecretsManager(dbSecret, "dbname"), + } + process.env.BACKEND_SECRETS?.split(",").forEach((secretName) => { + secrets[secretName] = Secret.fromSecretsManager( + secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${secretName}`) + ) + }) + // Grant write access to the uploads buckets. + this.publicUploads.grantReadWrite(this.executionRole) + this.publicUploads.grantPut(this.executionRole) + this.secureUploads.grantReadWrite(this.executionRole) + this.secureUploads.grantPut(this.executionRole) + // Get the SES Email Information + const sesIdentity = EmailIdentity.fromEmailIdentityName( + scope, + "sesIdentity", + "housingbayarea2.org" + ) + sesIdentity.grantSendEmail(this.executionRole) + // Add a bunch of random SES grants that grantSendEmail doesn't set up + const policy = new PolicyStatement({ + actions: [ + "ses:SendEmail", + "ses:SendRawEmail", + "ses:SendTemplatedEmail", + "ses:SendRawTemplatedEmail", + "ses:SendBulkTemplatedEmail", + "ses:UseConfiguration", + "ses:SendBulkEmail", + ], + resources: [ + sesIdentity.emailIdentityArn, + `arn:aws:ses:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:configuration-set/dway-config-set`, + `arn:aws:ses:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:identity/*`, + ], + }) + this.executionRole.addToPolicy(policy) + + const logGroup = new LogGroup(scope, `doorway-${props.environment}-tasks`, { + logGroupName: `doorway-${props.environment}-tasks`, + removalPolicy: cdk.RemovalPolicy.DESTROY, + retention: cdk.aws_logs.RetentionDays.ONE_WEEK, + }) + logGroup.grantWrite(this.executionRole) + // Now we're down to business! Set up the fargate container task for the api + // Notice the load of environment variables we add to it below + const task = new TaskDefinition(scope, "apiTask", { + compatibility: Compatibility.FARGATE, + cpu: process.env.API_CPUS || "2048", + memoryMiB: process.env.API_MEMORY || "4096", + executionRole: this.executionRole, + taskRole: this.executionRole, + networkMode: NetworkMode.AWS_VPC, + }) + + task.addContainer("internal-api", { + image: ContainerImage.fromRegistry( + `${cdk.Aws.ACCOUNT_ID}.dkr.ecr.${cdk.Aws.REGION}.amazonaws.com/doorway/backend:run-candidate` + ), + cpu: 1, + memoryLimitMiB: 1024, + essential: true, + logging: LogDrivers.awsLogs({ + streamPrefix: "internal-api", + logGroup: LogGroup.fromLogGroupName( + scope, + "logGroup", + `doorway-${props.environment}-tasks` + ), + }), + secrets: secrets, + environment: environmentVariables, + entryPoint: [], + portMappings: [ + { + containerPort: Number(process.env.API_LOCAL_PORT) || 3100, + protocol: Protocol.TCP, + hostPort: Number(process.env.API_LOCAL_PORT) || 3100, + }, + ], + }) + // Create the service in ECS + const service = new FargateService(scope, `doorway-${props.environment}-internal-api`, { + taskDefinition: task, + serviceName: `doorway-${props.environment}-internal-api`, + cluster: Cluster.fromClusterAttributes(scope, "ecsCluster", { + clusterName: Fn.importValue(`doorway-ecs-cluster-${props.environment}`), + vpc: this.vpc, + }), + vpcSubnets: { + subnets: this.appsubnets, + }, + securityGroups: [this.privateSG], + desiredCount: Number(process.env.API_INSTANCES) || 3, + }) + + const scaling = service.autoScaleTaskCount({ + minCapacity: Number(process.env.API_INSTANCES) || 3, + maxCapacity: Number(process.env.API_MAX_INSTANCES) || 10, + }) + scaling.scaleOnCpuUtilization("CpuScaling", { + targetUtilizationPercent: Number(process.env.API_AUTOSCALE_CPU) || 80, + scaleInCooldown: cdk.Duration.seconds(60), + scaleOutCooldown: cdk.Duration.seconds(60), + }) + } +} diff --git a/cdk/package.json b/cdk/package.json index b8c18074a0e..97a54c9db4d 100644 --- a/cdk/package.json +++ b/cdk/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "aws-cdk-lib": "2.208.0", - "constructs": "^10.0.0" + "constructs": "^10.0.0", + "dotenv": "^17.2.1" } } diff --git a/cdk/yarn.lock b/cdk/yarn.lock index 8b1d4944733..600f34292ca 100644 --- a/cdk/yarn.lock +++ b/cdk/yarn.lock @@ -1050,6 +1050,11 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +dotenv@^17.2.1: + version "17.2.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.2.1.tgz#6f32e10faf014883515538dc922a0fb8765d9b32" + integrity sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ== + electron-to-chromium@^1.5.199: version "1.5.202" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.202.tgz#e243716943a2cab144604a81b9ebaa87b4e8c1d4" diff --git a/ci/buildspec/update_ecs.yml b/ci/buildspec/update_ecs.yml index a4c7a485559..b7425b028bb 100644 --- a/ci/buildspec/update_ecs.yml +++ b/ci/buildspec/update_ecs.yml @@ -7,9 +7,8 @@ env: shell: bash phases: build: - commands: - # Force ECS to use the new images - - aws ecs update-service --force-new-deployment --cluster "${ECS_BACKEND_CLUSTER:-${ECS_DEFAULT_CLUSTER}}" --service "${ECS_BACKEND_SERVICE}" - - aws ecs update-service --force-new-deployment --cluster "${ECS_PUBLIC_CLUSTER:-${ECS_DEFAULT_CLUSTER}}" --service "${ECS_PUBLIC_SERVICE}" - - aws ecs update-service --force-new-deployment --cluster "${ECS_PARTNER_CLUSTER:-${ECS_DEFAULT_CLUSTER}}" --service "${ECS_PARTNER_SERVICE}" - - aws ecs update-service --force-new-deployment --cluster "${ECS_BACKEND_CLUSTER:-${ECS_DEFAULT_CLUSTER}}" --service "${ECS_PRIVATE_BACKEND_SERVICE}" + commands: npm install -g yarn + npm install -g aws-cdk + cd ${CODEBUILD_SRC_DIR}/cdk + yarn install + cdk deploy DoorwayAppEnvironmentStack --require-approval never From b7985441a51bad64cb9e0602472bdaf41e4242d6 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 10:58:02 -0700 Subject: [PATCH 019/217] fix: adding to pipeline --- cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 7 +++++++ cdk/lib/build-pipeline/doorway-ecs-deploy.ts | 2 -- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 0386a1233f6..83e6009d8e2 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -7,6 +7,7 @@ import { Construct } from "constructs" import { DoorwayDatabaseMigrate } from "./doorway-database-migrate" import { DoorwayDockerBuild } from "./doorway-docker-build" +import { DoorwayECSDeploy } from "./doorway-ecs-deploy" export class DoorwayBuildPipelineStack extends Stack { constructor(scope: Construct, id: string, props: PipelineProps) { @@ -160,6 +161,12 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, buildRole: buildRole, }).action, + new DoorwayECSDeploy(this, "doorway-ecs-deploy-dev", { + buildspec: "./ci/buildspec/update_ecs.yml", + source: sourceArtifact, + buildRole: buildRole, + environment: "dev2", + }).action, ], }) } diff --git a/cdk/lib/build-pipeline/doorway-ecs-deploy.ts b/cdk/lib/build-pipeline/doorway-ecs-deploy.ts index 2952c6ba4e4..6c9e2851f25 100644 --- a/cdk/lib/build-pipeline/doorway-ecs-deploy.ts +++ b/cdk/lib/build-pipeline/doorway-ecs-deploy.ts @@ -7,8 +7,6 @@ import { Construct } from "constructs" // Removed fs and YAML imports since we're using BuildSpec.fromSourceFilename export interface DoorwayECSDeployProps { - ecrNamespace?: string - databaseName?: string environment: string buildspec: string source: Artifact From e7eaa95c5948d6c497892966686a97dbe3d6aa8c Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 11:24:11 -0700 Subject: [PATCH 020/217] fix: fixing some env variable issues. --- cdk/lib/build-pipeline/doorway-ecs-deploy.ts | 2 +- cdk/lib/doorway_environment/doorway_services.ts | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-ecs-deploy.ts b/cdk/lib/build-pipeline/doorway-ecs-deploy.ts index 6c9e2851f25..cc2645b7e6a 100644 --- a/cdk/lib/build-pipeline/doorway-ecs-deploy.ts +++ b/cdk/lib/build-pipeline/doorway-ecs-deploy.ts @@ -16,7 +16,7 @@ export interface DoorwayECSDeployProps { export class DoorwayECSDeploy { public readonly action: CodeBuildAction constructor(scope: Construct, id: string, props: DoorwayECSDeployProps) { - const project = new PipelineProject(scope, `doorway-dbMigrate-${props.environment}`, { + const project = new PipelineProject(scope, `doorway-ecsDeploy-${props.environment}`, { buildSpec: BuildSpec.fromSourceFilename(props.buildspec), environment: { buildImage: LinuxBuildImage.STANDARD_7_0, diff --git a/cdk/lib/doorway_environment/doorway_services.ts b/cdk/lib/doorway_environment/doorway_services.ts index 109b86b36ce..d1cdd4d8d11 100644 --- a/cdk/lib/doorway_environment/doorway_services.ts +++ b/cdk/lib/doorway_environment/doorway_services.ts @@ -26,8 +26,8 @@ abstract class DoorwayService { protected privateSG: ISecurityGroup protected publicSG: ISecurityGroup protected vpc: IVpc - protected appsubnets: ISubnet[] - protected publicSubnets: ISubnet[] + protected appsubnets: ISubnet[] = [] + protected publicSubnets: ISubnet[] = [] protected publicUploads: IBucket protected secureUploads: IBucket @@ -142,11 +142,12 @@ export class DoorwayApiService extends DoorwayService { PGHOST: Secret.fromSecretsManager(dbSecret, "host"), PGDATABASE: Secret.fromSecretsManager(dbSecret, "dbname"), } - process.env.BACKEND_SECRETS?.split(",").forEach((secretName) => { - secrets[secretName] = Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${secretName}`) - ) - }) + process.env.BACKEND_SECRETS || + "".split(",").forEach((secretName) => { + secrets[secretName] = Secret.fromSecretsManager( + secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${secretName}`) + ) + }) // Grant write access to the uploads buckets. this.publicUploads.grantReadWrite(this.executionRole) this.publicUploads.grantPut(this.executionRole) From 3e141869c7e39e3a202c7d710413b548b39e1aaf Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 11:49:29 -0700 Subject: [PATCH 021/217] fix: modifying migration commands --- cdk/lib/build-pipeline/doorway-database-migrate.ts | 3 +++ ci/buildspec/update_ecs.yml | 11 ++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts index 959883b662e..85a5c51c89b 100644 --- a/cdk/lib/build-pipeline/doorway-database-migrate.ts +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -4,6 +4,7 @@ import { Artifact } from "aws-cdk-lib/aws-codepipeline" import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" import { Role } from "aws-cdk-lib/aws-iam" +import { Secret } from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" // Removed fs and YAML imports since we're using BuildSpec.fromSourceFilename @@ -33,6 +34,7 @@ export class DoorwayDatabaseMigrate { subnetId: subnetId, }) const sg = SecurityGroup.fromSecurityGroupId(scope, "sg", securityGroupId) + Secret.fromSecretCompleteArn(scope, "dbSecret", dbSecretArn).grantRead(props.buildRole) const project = new PipelineProject(scope, `doorway-dbMigrate-${props.environment}`, { vpc: vpc, @@ -50,6 +52,7 @@ export class DoorwayDatabaseMigrate { ECR_ACCOUNT_ID: { value: Aws.ACCOUNT_ID }, ECR_NAMESPACE: { value: props.ecrNamespace || "doorway" }, PGDATABASE: { value: props.databaseName || "bloom" }, + MIGRATION_CMD: { value: "db:reseed:ci" }, }, role: props.buildRole, }) diff --git a/ci/buildspec/update_ecs.yml b/ci/buildspec/update_ecs.yml index b7425b028bb..9258510dd54 100644 --- a/ci/buildspec/update_ecs.yml +++ b/ci/buildspec/update_ecs.yml @@ -7,8 +7,9 @@ env: shell: bash phases: build: - commands: npm install -g yarn - npm install -g aws-cdk - cd ${CODEBUILD_SRC_DIR}/cdk - yarn install - cdk deploy DoorwayAppEnvironmentStack --require-approval never + commands: + - npm install -g yarn + - npm install -g aws-cdk + - cd ${CODEBUILD_SRC_DIR}/cdk + - yarn install + - cdk deploy DoorwayAppEnvironmentStack --require-approval never From e6e755f5e743d0db54b9c1cef703215220a5cf57 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 12:02:30 -0700 Subject: [PATCH 022/217] fix: switching to stop backend --- .../build-pipeline/doorway-build-pipeline-stack.ts | 2 +- cdk/lib/build-pipeline/doorway-database-migrate.ts | 1 - ci/buildspec/migrate_stop_backend.yml | 12 ++++++------ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 83e6009d8e2..794991851db 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -157,7 +157,7 @@ export class DoorwayBuildPipelineStack extends Stack { actions: [ new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev", { environment: "dev2", - buildspec: "./ci/buildspec/migrate.yml", + buildspec: "./ci/buildspec/migrate_stop_backend.yml", source: sourceArtifact, buildRole: buildRole, }).action, diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts index 85a5c51c89b..83a08316005 100644 --- a/cdk/lib/build-pipeline/doorway-database-migrate.ts +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -52,7 +52,6 @@ export class DoorwayDatabaseMigrate { ECR_ACCOUNT_ID: { value: Aws.ACCOUNT_ID }, ECR_NAMESPACE: { value: props.ecrNamespace || "doorway" }, PGDATABASE: { value: props.databaseName || "bloom" }, - MIGRATION_CMD: { value: "db:reseed:ci" }, }, role: props.buildRole, }) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 4a1184952fb..f5bd4086404 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -27,15 +27,15 @@ phases: # The backend needs to be completely turned down for a reseed # If the backend is still running, the reseed step fails due to open connections # Check current backend desired count - - export FOUND_BACKEND_COUNT=$(aws ecs describe-services --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_BACKEND_SERVICE}" | jq -r .services[0].desiredCount) + #- export FOUND_BACKEND_COUNT=$(aws ecs describe-services --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_BACKEND_SERVICE}" | jq -r .services[0].desiredCount) # We have to quote this line or YAML attempts to interpret the `: ` as a dict key(this is a YAML feature, not a bug) - - "export BACKEND_COUNT=$((FOUND_BACKEND_COUNT<1 ? 1 : FOUND_BACKEND_COUNT))" + #- "export BACKEND_COUNT=$((FOUND_BACKEND_COUNT<1 ? 1 : FOUND_BACKEND_COUNT))" # Scale to 0 - - aws ecs update-service --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_BACKEND_SERVICE}" --desired-count 0 - - if [[ ! -z "${ECS_PRIVATE_BACKEND_SERVICE}" ]]; then aws ecs update-service --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_PRIVATE_BACKEND_SERVICE}" --desired-count 0; fi + #- aws ecs update-service --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_BACKEND_SERVICE}" --desired-count 0 + # - if [[ ! -z "${ECS_PRIVATE_BACKEND_SERVICE}" ]]; then aws ecs update-service --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_PRIVATE_BACKEND_SERVICE}" --desired-count 0; fi # Wait for the service to stablize - - aws ecs wait services-stable --cluster "${ECS_BACKEND_CLUSTER}" --services "${ECS_BACKEND_SERVICE}" - - if [[ ! -z "${ECS_PRIVATE_BACKEND_SERVICE}" ]]; then aws ecs wait services-stable --cluster "${ECS_BACKEND_CLUSTER}" --services "${ECS_PRIVATE_BACKEND_SERVICE}"; fi + #- aws ecs wait services-stable --cluster "${ECS_BACKEND_CLUSTER}" --services "${ECS_BACKEND_SERVICE}" + #- if [[ ! -z "${ECS_PRIVATE_BACKEND_SERVICE}" ]]; then aws ecs wait services-stable --cluster "${ECS_BACKEND_CLUSTER}" --services "${ECS_PRIVATE_BACKEND_SERVICE}"; fi # Default to db:reseed:ci - export MIGRATION_CMD="${MIGRATION_CMD:-db:reseed:ci}" # Execute migration From 893fa79b374b44598d3c26a32edc984b434cad65 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 13:14:16 -0700 Subject: [PATCH 023/217] fix: putting ECS Restart back in --- cdk/lib/build-pipeline/doorway-database-migrate.ts | 1 + ci/buildspec/migrate_stop_backend.yml | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts index 83a08316005..dbccadbfa56 100644 --- a/cdk/lib/build-pipeline/doorway-database-migrate.ts +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -52,6 +52,7 @@ export class DoorwayDatabaseMigrate { ECR_ACCOUNT_ID: { value: Aws.ACCOUNT_ID }, ECR_NAMESPACE: { value: props.ecrNamespace || "doorway" }, PGDATABASE: { value: props.databaseName || "bloom" }, + SKIP_MIGRATIONS: { value: "TRUE" }, // Set to true to skip migrations }, role: props.buildRole, }) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index f5bd4086404..4a1184952fb 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -27,15 +27,15 @@ phases: # The backend needs to be completely turned down for a reseed # If the backend is still running, the reseed step fails due to open connections # Check current backend desired count - #- export FOUND_BACKEND_COUNT=$(aws ecs describe-services --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_BACKEND_SERVICE}" | jq -r .services[0].desiredCount) + - export FOUND_BACKEND_COUNT=$(aws ecs describe-services --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_BACKEND_SERVICE}" | jq -r .services[0].desiredCount) # We have to quote this line or YAML attempts to interpret the `: ` as a dict key(this is a YAML feature, not a bug) - #- "export BACKEND_COUNT=$((FOUND_BACKEND_COUNT<1 ? 1 : FOUND_BACKEND_COUNT))" + - "export BACKEND_COUNT=$((FOUND_BACKEND_COUNT<1 ? 1 : FOUND_BACKEND_COUNT))" # Scale to 0 - #- aws ecs update-service --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_BACKEND_SERVICE}" --desired-count 0 - # - if [[ ! -z "${ECS_PRIVATE_BACKEND_SERVICE}" ]]; then aws ecs update-service --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_PRIVATE_BACKEND_SERVICE}" --desired-count 0; fi + - aws ecs update-service --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_BACKEND_SERVICE}" --desired-count 0 + - if [[ ! -z "${ECS_PRIVATE_BACKEND_SERVICE}" ]]; then aws ecs update-service --cluster "${ECS_BACKEND_CLUSTER}" --service "${ECS_PRIVATE_BACKEND_SERVICE}" --desired-count 0; fi # Wait for the service to stablize - #- aws ecs wait services-stable --cluster "${ECS_BACKEND_CLUSTER}" --services "${ECS_BACKEND_SERVICE}" - #- if [[ ! -z "${ECS_PRIVATE_BACKEND_SERVICE}" ]]; then aws ecs wait services-stable --cluster "${ECS_BACKEND_CLUSTER}" --services "${ECS_PRIVATE_BACKEND_SERVICE}"; fi + - aws ecs wait services-stable --cluster "${ECS_BACKEND_CLUSTER}" --services "${ECS_BACKEND_SERVICE}" + - if [[ ! -z "${ECS_PRIVATE_BACKEND_SERVICE}" ]]; then aws ecs wait services-stable --cluster "${ECS_BACKEND_CLUSTER}" --services "${ECS_PRIVATE_BACKEND_SERVICE}"; fi # Default to db:reseed:ci - export MIGRATION_CMD="${MIGRATION_CMD:-db:reseed:ci}" # Execute migration From 9094671b1bea573d5448379e0b15bed82ab770ad Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 14:44:28 -0700 Subject: [PATCH 024/217] fix: different db entrypoint --- build/docker/entrypoint-migrate.sh | 2 +- .../doorway-build-pipeline-stack.ts | 29 ++++++++++--------- .../doorway-database-migrate.ts | 12 +++++++- cdk/lib/build-pipeline/doorway-ecs-deploy.ts | 1 + 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh index c29df6b9890..f3f31aaca93 100644 --- a/build/docker/entrypoint-migrate.sh +++ b/build/docker/entrypoint-migrate.sh @@ -1,3 +1,3 @@ #!/bin/sh export DATABASE_URL="postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" -yarn db:reseed:ci +yarn db:setup-staging-large diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 794991851db..fbefe0536fc 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -69,6 +69,7 @@ export class DoorwayBuildPipelineStack extends Stack { "logs:*", "iam:AssumeRole", "iam:PassRole", + "ecs:*", ], resources: ["*"], }), @@ -152,22 +153,22 @@ export class DoorwayBuildPipelineStack extends Stack { }).action, ], }) + const dbmigrate = new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev", { + environment: "dev2", + buildspec: "./ci/buildspec/migrate_stop_backend.yml", + source: sourceArtifact, + buildRole: buildRole, + }).action + const ecsDeploy = new DoorwayECSDeploy(this, "doorway-ecs-deploy-dev", { + buildspec: "./ci/buildspec/update_ecs.yml", + source: sourceArtifact, + buildRole: buildRole, + environment: "dev2", + }).action + pipeline.addStage({ stageName: "Dev", - actions: [ - new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev", { - environment: "dev2", - buildspec: "./ci/buildspec/migrate_stop_backend.yml", - source: sourceArtifact, - buildRole: buildRole, - }).action, - new DoorwayECSDeploy(this, "doorway-ecs-deploy-dev", { - buildspec: "./ci/buildspec/update_ecs.yml", - source: sourceArtifact, - buildRole: buildRole, - environment: "dev2", - }).action, - ], + actions: [dbmigrate, ecsDeploy], }) } } diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts index dbccadbfa56..f8902d2bded 100644 --- a/cdk/lib/build-pipeline/doorway-database-migrate.ts +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -52,7 +52,16 @@ export class DoorwayDatabaseMigrate { ECR_ACCOUNT_ID: { value: Aws.ACCOUNT_ID }, ECR_NAMESPACE: { value: props.ecrNamespace || "doorway" }, PGDATABASE: { value: props.databaseName || "bloom" }, - SKIP_MIGRATIONS: { value: "TRUE" }, // Set to true to skip migrations + CLOUDINARY_CLOUD_NAME: { value: "exygy" }, + CACHE_REVALIDATE: { value: "60" }, + LANGUAGES: { value: "en,es,zh,vi,tl" }, + IDLE_TIMEOUT: { value: "5" }, + ECS_BACKEND_SERVICE: { + value: `doorway-${props.environment}-internal-api`, + }, + ECS_BACKEND_CLUSTER: { + value: `doorway-ecs-cluster-${props.environment}`, + }, }, role: props.buildRole, }) @@ -62,6 +71,7 @@ export class DoorwayDatabaseMigrate { input: props.source, outputs: [new Artifact(`${id}-BuildOutput`)], project, + runOrder: 1, }) } } diff --git a/cdk/lib/build-pipeline/doorway-ecs-deploy.ts b/cdk/lib/build-pipeline/doorway-ecs-deploy.ts index cc2645b7e6a..3fd630a04cd 100644 --- a/cdk/lib/build-pipeline/doorway-ecs-deploy.ts +++ b/cdk/lib/build-pipeline/doorway-ecs-deploy.ts @@ -32,6 +32,7 @@ export class DoorwayECSDeploy { input: props.source, outputs: [new Artifact(`${id}-BuildOutput`)], project, + runOrder: 2, }) } } From a3618719c2ab3c27de388f4ff78699dc08b50da5 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 14:57:50 -0700 Subject: [PATCH 025/217] fix: another entrypoint change --- build/docker/entrypoint-migrate.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh index f3f31aaca93..66d37cde7d3 100644 --- a/build/docker/entrypoint-migrate.sh +++ b/build/docker/entrypoint-migrate.sh @@ -1,3 +1,5 @@ #!/bin/sh export DATABASE_URL="postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" -yarn db:setup-staging-large +if [ "$SKIP_MIGRATIONS" = TRUE ]; then yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ + else yarn db:migration:run; \ + fi \ No newline at end of file From 9af7e421c2797e1ee0198a35820ce8bd93d55387 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 19:27:07 -0700 Subject: [PATCH 026/217] fix: api service --- .../doorway-api-service.ts | 185 ++++++++++++++ .../doorway-app-environment-stack.ts | 4 +- .../doorway-public-service-old.ts | 222 +++++++++++++++++ .../doorway-public-service.ts | 228 +----------------- .../doorway_environment/doorway_services.ts | 184 +------------- 5 files changed, 421 insertions(+), 402 deletions(-) create mode 100644 cdk/lib/doorway_environment/doorway-api-service.ts create mode 100644 cdk/lib/doorway_environment/doorway-public-service-old.ts diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts new file mode 100644 index 00000000000..f492384d584 --- /dev/null +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -0,0 +1,185 @@ +import { Aws, aws_logs, Duration, Fn, RemovalPolicy } from "aws-cdk-lib" +import { + Cluster, + Compatibility, + ContainerImage, + FargateService, + LogDrivers, + NetworkMode, + Protocol, + Secret, + TaskDefinition, +} from "aws-cdk-lib/aws-ecs" +import { PolicyStatement } from "aws-cdk-lib/aws-iam" +import { LogGroup } from "aws-cdk-lib/aws-logs" +import * as secret from "aws-cdk-lib/aws-secretsmanager" +import { EmailIdentity } from "aws-cdk-lib/aws-ses" +import { Construct } from "constructs" + +import { DoorwayService } from "./doorway_services" +import { DoorwayServiceProps } from "./doorway-service-props" + +export class DoorwayApiService extends DoorwayService { + public constructor(scope: Construct, id: string, props: DoorwayServiceProps) { + super(scope, id, props) + const environmentVariables = { + ASSET_FILE_SERVICE: process.env.ASSET_FILE_SERVICE || "s3", + LISTINGS_PROCESSING_QUERY: process.env.LISTINGS_PROCESSING_QUERY || "/listings", + PORT: process.env.API_LOCAL_PORT || "3100", + SHOW_LM_LINKS: process.env.SHOW_LM_LINKS || "TRUE", + TIME_ZONE: process.env.TIME_ZONE || "America/Los_Angeles", + SHOW_DUPLICATES: process.env.SHOW_DUPLICATES || "FALSE", + NO_COLOR: process.env.NO_COLOR || "TRUE", + ASSET_FS_CONFIG_s3_PATH_PREFIX: process.env.ASSET_FS_CONFIG_s3_PATH_PREFIX || "", + ASSET_FS_PRIVATE_CONFIG_s3_BUCKET: this.secureUploads.bucketName, + ASSET_FS_CONFIG_s3_BUCKET: this.publicUploads.bucketName, + ASSET_FS_CONFIG_s3_REGION: Aws.REGION, + ASSET_FS_CONFIG_s3_URL_FORMAT: process.env.ASSET_FS_CONFIG_s3_URL_FORMAT || "public", + THROTTLE_LIMIT: process.env.THROTTLE_LIMIT || "100", + THROTTLE_TTL: process.env.THROTTLE_TTL || "180000", + LOG_LEVEL: process.env.LOG_LEVEL || "info", + LISTING_PROCESSING_CRON_STRING: process.env.LISTING_PROCESSING_CRON_STRING || "0 * * * *", + LOTTERY_PROCESSING_CRON_STRING: process.env.LOTTERY_PROCESSING_CRON_STRING || "0 * * * *", + LOTTERY_PUBLISH_PROCESSING_CRON_STRING: + process.env.LOTTERY_PUBLISH_PROCESSING_CRON_STRING || "58 23 * * *", + AFS_PROCESSING_CRON_STRING: process.env.AFS_PROCESSING_CRON_STRING || "15 * * * *", + TEMP_FILE_CLEAR_CRON_STRING: process.env.TEMP_FILE_CLEAR_CRON_STRING || "30 * * * *", + DUPLICATES_PROCESSING_CRON_STRING: + process.env.DUPLICATES_PROCESSING_CRON_STRING || "5 * * * *", + LOTTERY_DAYS_TILL_EXPIRY: process.env.LOTTERY_DAYS_TILL_EXPIRY || "45", + MFA_CODE_LENGTH: process.env.MFA_CODE_LENGTH || "5", + MFA_CODE_VALID: process.env.MFA_CODE_VALID || "600000", + GOVDELIVERY_TOPIC: process.env.GOVDELIVERY_TOPIC || "5 * * * *", + PARTNERS_PORTAL_URL: + process.env.PARTNERS_PORTAL_URL || + `https://partners.${props.environment}.housingbayarea.mtc.ca.gov`, + PARTNERS_BASE_URL: + process.env.PARTNERS_BASE_URL || + `https://partners.${props.environment}.housingbayarea.mtc.ca.gov/api`, + ASSET_UPLOAD_MAX_SIZE: process.env.ASSET_UPLOAD_MAX_SIZE || "5", + AUTH_LOCK_LOGIN_COOLDOWN: process.env.AUTH_LOCK_LOGIN_COOLDOWN || "1800000", + AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS: + process.env.AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS || "5", + CORS_ORIGINS: + process.env.CORS_ORIGINS || + "https://housingbayarea.mtc.ca.gov, https://partners.housingbayarea.mtc.ca.gov", + DUPLICATES_CLOSE_DATE: process.env.DUPLICATES_CLOSE_DATE || "2024-10-08 00:00 -08:00", + HTTPS_OFF: process.env.HTTPS_OFF || "TRUE", + SAME_SITE: process.env.SAME_SITE || "false", + } + const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) + const dbSecret = secret.Secret.fromSecretCompleteArn(scope, "dbSecret", dbSecretArn) + dbSecret.grantRead(this.executionRole) + const secrets: { [key: string]: Secret } = { + PGUSER: Secret.fromSecretsManager(dbSecret, "username"), + PGPASSWORD: Secret.fromSecretsManager(dbSecret, "password"), + PGPORT: Secret.fromSecretsManager(dbSecret, "port"), + PGHOST: Secret.fromSecretsManager(dbSecret, "host"), + PGDATABASE: Secret.fromSecretsManager(dbSecret, "dbname"), + } + process.env.BACKEND_SECRETS || + "".split(",").forEach((secretName) => { + secrets[secretName] = Secret.fromSecretsManager( + secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${secretName}`) + ) + }) + // Grant write access to the uploads buckets. + this.publicUploads.grantReadWrite(this.executionRole) + this.publicUploads.grantPut(this.executionRole) + this.secureUploads.grantReadWrite(this.executionRole) + this.secureUploads.grantPut(this.executionRole) + // Get the SES Email Information + const sesIdentity = EmailIdentity.fromEmailIdentityName( + scope, + "sesIdentity", + "housingbayarea2.org" + ) + sesIdentity.grantSendEmail(this.executionRole) + // Add a bunch of random SES grants that grantSendEmail doesn't set up + const policy = new PolicyStatement({ + actions: [ + "ses:SendEmail", + "ses:SendRawEmail", + "ses:SendTemplatedEmail", + "ses:SendRawTemplatedEmail", + "ses:SendBulkTemplatedEmail", + "ses:UseConfiguration", + "ses:SendBulkEmail", + ], + resources: [ + sesIdentity.emailIdentityArn, + `arn:aws:ses:${Aws.REGION}:${Aws.ACCOUNT_ID}:configuration-set/dway-config-set`, + `arn:aws:ses:${Aws.REGION}:${Aws.ACCOUNT_ID}:identity/*`, + ], + }) + this.executionRole.addToPolicy(policy) + + const logGroup = new LogGroup(scope, `doorway-${props.environment}-tasks`, { + logGroupName: `doorway-${props.environment}-tasks`, + removalPolicy: RemovalPolicy.DESTROY, + retention: aws_logs.RetentionDays.ONE_WEEK, + }) + logGroup.grantWrite(this.executionRole) + // Now we're down to business! Set up the fargate container task for the api + // Notice the load of environment variables we add to it below + const task = new TaskDefinition(scope, "apiTask", { + compatibility: Compatibility.FARGATE, + cpu: process.env.API_CPUS || "2048", + memoryMiB: process.env.API_MEMORY || "4096", + executionRole: this.executionRole, + taskRole: this.executionRole, + networkMode: NetworkMode.AWS_VPC, + }) + + task.addContainer("internal-api", { + image: ContainerImage.fromRegistry( + `${Aws.ACCOUNT_ID}.dkr.ecr.${Aws.REGION}.amazonaws.com/doorway/backend:run-candidate` + ), + cpu: 1, + memoryLimitMiB: 1024, + essential: true, + logging: LogDrivers.awsLogs({ + streamPrefix: "internal-api", + logGroup: LogGroup.fromLogGroupName( + scope, + "logGroup", + `doorway-${props.environment}-tasks` + ), + }), + secrets: secrets, + environment: environmentVariables, + entryPoint: [], + portMappings: [ + { + containerPort: Number(process.env.API_LOCAL_PORT) || 3100, + protocol: Protocol.TCP, + hostPort: Number(process.env.API_LOCAL_PORT) || 3100, + }, + ], + }) + // Create the service in ECS + const service = new FargateService(scope, `doorway-${props.environment}-internal-api`, { + taskDefinition: task, + serviceName: `doorway-${props.environment}-internal-api`, + cluster: Cluster.fromClusterAttributes(scope, "ecsCluster", { + clusterName: Fn.importValue(`doorway-ecs-cluster-${props.environment}`), + vpc: this.vpc, + }), + vpcSubnets: { + subnets: this.appsubnets, + }, + securityGroups: [this.privateSG], + desiredCount: Number(process.env.API_INSTANCES) || 3, + }) + + const scaling = service.autoScaleTaskCount({ + minCapacity: Number(process.env.API_INSTANCES) || 3, + maxCapacity: Number(process.env.API_MAX_INSTANCES) || 10, + }) + scaling.scaleOnCpuUtilization("CpuScaling", { + targetUtilizationPercent: Number(process.env.API_AUTOSCALE_CPU) || 80, + scaleInCooldown: Duration.seconds(60), + scaleOutCooldown: Duration.seconds(60), + }) + } +} diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index fd9b0311a91..caa34bf1530 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -1,7 +1,9 @@ import { Stack } from "aws-cdk-lib" import { Construct } from "constructs" import dotenv from "dotenv" -import { DoorwayApiService } from "./doorway_services" + +import { DoorwayApiService } from "./doorway-api-service" + export class DoorwayAppEnvironmentStack extends Stack { constructor(scope: Construct, id: string) { super(scope, id) diff --git a/cdk/lib/doorway_environment/doorway-public-service-old.ts b/cdk/lib/doorway_environment/doorway-public-service-old.ts new file mode 100644 index 00000000000..059cd76575b --- /dev/null +++ b/cdk/lib/doorway_environment/doorway-public-service-old.ts @@ -0,0 +1,222 @@ +// import { Fn } from "aws-cdk-lib" +// import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager" +// import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" +// import { Repository } from "aws-cdk-lib/aws-ecr" +// import { AwsLogDriver, Cluster, ContainerImage, Secret } from "aws-cdk-lib/aws-ecs" +// import { ApplicationLoadBalancedFargateService } from "aws-cdk-lib/aws-ecs-patterns" +// import { SslPolicy } from "aws-cdk-lib/aws-elasticloadbalancingv2" +// import { ManagedPolicy, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +// import { LogGroup } from "aws-cdk-lib/aws-logs" +// import { PublicHostedZone } from "aws-cdk-lib/aws-route53" +// import * as secret from "aws-cdk-lib/aws-secretsmanager" +// import { StringParameter } from "aws-cdk-lib/aws-ssm" +// import { Construct } from "constructs" + +// export class DoorwayPublicService { +// public service: ApplicationLoadBalancedFargateService +// constructor(scope: Construct, id: string, props: DoorwayStackProps) { +// // Create the execution role for the doorway API service +// const executionRole = new Role(scope, "executionRole", { +// assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), +// }) +// executionRole.addManagedPolicy( +// ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy") +// ) +// executionRole.addManagedPolicy( +// ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess") +// ) + +// executionRole.addManagedPolicy( +// ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly") +// ) +// const hostedZone = PublicHostedZone.fromHostedZoneAttributes( +// scope, +// `doorway-public-hosted-zone-${props.environment}`, +// { +// hostedZoneId: StringParameter.valueForStringParameter(scope, `/doorway/public-hosted-zone`), +// zoneName: `housingbayarea.mtc.ca.gov`, +// } +// ) +// const cert = new Certificate(scope, "doorwayPublicCert", { +// domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, +// validation: { +// method: CertificateValidation.fromDns().method, +// props: { +// hostedZone: hostedZone, +// }, +// }, +// }) + +// new ApplicationLoadBalancedFargateService( +// scope, +// `doorway-public-portal-service-${props.environment}`, +// { +// cluster: Cluster.fromClusterAttributes(scope, `doorway-ecs-cluster-${props.environment}`, { +// clusterName: `doorway-ecs-cluster-${props.environment}`, +// vpc: Vpc.fromVpcAttributes(scope, "vpc", { +// vpcId: Fn.importValue(`doorway-vpc-id-${props.environment}`), +// availabilityZones: Fn.importValue(`doorway-azs-${props.environment}`).split(","), +// publicSubnetIds: [ +// Fn.importValue(`doorway-public-subnet-1-${props.environment}`), +// Fn.importValue(`doorway-public-subnet-2-${props.environment}`), +// ], +// }), +// securityGroups: [ +// SecurityGroup.fromSecurityGroupId( +// scope, +// `doorway-app-sg-${props.environment}`, +// Fn.importValue(`doorway-app-sg-${props.environment}`) +// ), +// ], +// }), +// taskSubnets: { +// subnets: [ +// Subnet.fromSubnetId( +// scope, +// `doorway-app-subnet-1-${props.environment}`, +// Fn.importValue(`doorway-app-subnet-1-${props.environment}`) +// ), +// Subnet.fromSubnetId( +// scope, +// `doorway-app-subnet-2-${props.environment}`, +// Fn.importValue(`doorway-app-subnet-2-${props.environment}`) +// ), +// ], +// }, + +// assignPublicIp: true, +// publicLoadBalancer: true, +// taskImageOptions: { +// image: ContainerImage.fromEcrRepository( +// Repository.fromRepositoryName( +// scope, +// `doorway-public-portal-repo-${props.environment}`, +// `doorway/public` +// ), +// "run-f21478f5" +// ), +// enableLogging: true, +// containerPort: 3000, +// executionRole: executionRole, +// taskRole: executionRole, +// secrets: { +// BLOOM_API_BASE: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "BLOOM_API_BASE", +// `/doorway/${props.environment}/public-portal/BLOOM_API_BASE` +// ) +// ), +// CACHE_REVALIDATE: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "CACHE_REVALIDATE", +// `/doorway/${props.environment}/public-portal/CACHE_REVALIDATE` +// ) +// ), +// GOOGLE_API_KEY: Secret.fromSecretsManager( +// secret.Secret.fromSecretNameV2(scope, "googleApiKey", "/doorway/GOOGLE_API_KEY") +// ), +// GOOGLE_MAPS_API_KEY: Secret.fromSecretsManager( +// secret.Secret.fromSecretNameV2( +// scope, +// "googleMapsApiKey", +// "/doorway/GOOGLE_MAPS_API_KEY" +// ) +// ), +// GOOGLE_MAPS_MAP_ID: Secret.fromSecretsManager( +// secret.Secret.fromSecretNameV2( +// scope, +// "googleMapsMapId", +// "/doorway/GOOGLE_MAPS_MAP_ID" +// ) +// ), +// GTM_KEY: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "GTM_KEY", +// `/doorway/${props.environment}/public-portal/GTM_KEY` +// ) +// ), +// IDLE_TIMEOUT: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "IDLE_TIMEOUT", +// `/doorway/${props.environment}/public-portal/IDLE_TIMEOUT` +// ) +// ), +// JURISDICTION_NAME: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "JURISDICTION_NAME", +// `/doorway/${props.environment}/public-portal/JURISDICTION_NAME` +// ) +// ), +// LANGUAGES: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "LANGUAGES", +// `/doorway/${props.environment}/public-portal/LANGUAGES` +// ) +// ), +// LISTINGS_QUERY: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "LISTINGS_QUERY", +// `/doorway/${props.environment}/public-portal/LISTINGS_QUERY` +// ) +// ), +// NEXTJS_PORT: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "NEXTJS_PORT", +// `/doorway/${props.environment}/public-portal/NEXTJS_PORT` +// ) +// ), +// NOTIFICATIONS_SIGN_UP_URL: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "NOTIFICATIONS_SIGN_UP_URL", +// `/doorway/${props.environment}/public-portal/NOTIFICATIONS_SIGN_UP_URL` +// ) +// ), +// SHOW_ALL_MAP_PINS: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "SHOW_ALL_MAP_PINS", +// `/doorway/${props.environment}/public-portal/SHOW_ALL_MAP_PINS` +// ) +// ), +// SHOW_ALL_PROFESSIONAL_PARTNERS: Secret.fromSsmParameter( +// StringParameter.fromStringParameterName( +// scope, +// "SHOW_PROFESSIONAL_PARTNERS", +// `/doorway/${props.environment}/public-portal/SHOW_PROFESSIONAL_PARTNERS` +// ) +// ), +// }, +// environment: { +// BACKEND_API_BASE: `http://backend.${props.environment}.housingbayarea.int`, +// }, +// logDriver: AwsLogDriver.awsLogs({ +// streamPrefix: "doorway-public-portal", +// logGroup: new LogGroup(scope, `doorway-${props.environment}-tasks`, { +// logGroupName: `/doorway/${props.environment}/ecs/doorway-public-portal`, +// }), +// }), +// }, + +// certificate: cert, +// listenerPort: 443, +// redirectHTTP: true, +// sslPolicy: SslPolicy.RECOMMENDED, +// serviceName: `doorway-public-portal-service-${props.environment}`, +// loadBalancerName: `doorway-public-lb-${props.environment}`, +// cpu: 1024, +// memoryLimitMiB: 2048, +// domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, +// domainZone: hostedZone, +// } +// ) +// } +// } diff --git a/cdk/lib/doorway_environment/doorway-public-service.ts b/cdk/lib/doorway_environment/doorway-public-service.ts index 059cd76575b..3ea5eef7081 100644 --- a/cdk/lib/doorway_environment/doorway-public-service.ts +++ b/cdk/lib/doorway_environment/doorway-public-service.ts @@ -1,222 +1,10 @@ -// import { Fn } from "aws-cdk-lib" -// import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager" -// import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" -// import { Repository } from "aws-cdk-lib/aws-ecr" -// import { AwsLogDriver, Cluster, ContainerImage, Secret } from "aws-cdk-lib/aws-ecs" -// import { ApplicationLoadBalancedFargateService } from "aws-cdk-lib/aws-ecs-patterns" -// import { SslPolicy } from "aws-cdk-lib/aws-elasticloadbalancingv2" -// import { ManagedPolicy, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" -// import { LogGroup } from "aws-cdk-lib/aws-logs" -// import { PublicHostedZone } from "aws-cdk-lib/aws-route53" -// import * as secret from "aws-cdk-lib/aws-secretsmanager" -// import { StringParameter } from "aws-cdk-lib/aws-ssm" -// import { Construct } from "constructs" +import { Construct } from "constructs" -// export class DoorwayPublicService { -// public service: ApplicationLoadBalancedFargateService -// constructor(scope: Construct, id: string, props: DoorwayStackProps) { -// // Create the execution role for the doorway API service -// const executionRole = new Role(scope, "executionRole", { -// assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), -// }) -// executionRole.addManagedPolicy( -// ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy") -// ) -// executionRole.addManagedPolicy( -// ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess") -// ) +import { DoorwayService } from "./doorway_services" +import { DoorwayServiceProps } from "./doorway-service-props" -// executionRole.addManagedPolicy( -// ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly") -// ) -// const hostedZone = PublicHostedZone.fromHostedZoneAttributes( -// scope, -// `doorway-public-hosted-zone-${props.environment}`, -// { -// hostedZoneId: StringParameter.valueForStringParameter(scope, `/doorway/public-hosted-zone`), -// zoneName: `housingbayarea.mtc.ca.gov`, -// } -// ) -// const cert = new Certificate(scope, "doorwayPublicCert", { -// domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, -// validation: { -// method: CertificateValidation.fromDns().method, -// props: { -// hostedZone: hostedZone, -// }, -// }, -// }) - -// new ApplicationLoadBalancedFargateService( -// scope, -// `doorway-public-portal-service-${props.environment}`, -// { -// cluster: Cluster.fromClusterAttributes(scope, `doorway-ecs-cluster-${props.environment}`, { -// clusterName: `doorway-ecs-cluster-${props.environment}`, -// vpc: Vpc.fromVpcAttributes(scope, "vpc", { -// vpcId: Fn.importValue(`doorway-vpc-id-${props.environment}`), -// availabilityZones: Fn.importValue(`doorway-azs-${props.environment}`).split(","), -// publicSubnetIds: [ -// Fn.importValue(`doorway-public-subnet-1-${props.environment}`), -// Fn.importValue(`doorway-public-subnet-2-${props.environment}`), -// ], -// }), -// securityGroups: [ -// SecurityGroup.fromSecurityGroupId( -// scope, -// `doorway-app-sg-${props.environment}`, -// Fn.importValue(`doorway-app-sg-${props.environment}`) -// ), -// ], -// }), -// taskSubnets: { -// subnets: [ -// Subnet.fromSubnetId( -// scope, -// `doorway-app-subnet-1-${props.environment}`, -// Fn.importValue(`doorway-app-subnet-1-${props.environment}`) -// ), -// Subnet.fromSubnetId( -// scope, -// `doorway-app-subnet-2-${props.environment}`, -// Fn.importValue(`doorway-app-subnet-2-${props.environment}`) -// ), -// ], -// }, - -// assignPublicIp: true, -// publicLoadBalancer: true, -// taskImageOptions: { -// image: ContainerImage.fromEcrRepository( -// Repository.fromRepositoryName( -// scope, -// `doorway-public-portal-repo-${props.environment}`, -// `doorway/public` -// ), -// "run-f21478f5" -// ), -// enableLogging: true, -// containerPort: 3000, -// executionRole: executionRole, -// taskRole: executionRole, -// secrets: { -// BLOOM_API_BASE: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "BLOOM_API_BASE", -// `/doorway/${props.environment}/public-portal/BLOOM_API_BASE` -// ) -// ), -// CACHE_REVALIDATE: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "CACHE_REVALIDATE", -// `/doorway/${props.environment}/public-portal/CACHE_REVALIDATE` -// ) -// ), -// GOOGLE_API_KEY: Secret.fromSecretsManager( -// secret.Secret.fromSecretNameV2(scope, "googleApiKey", "/doorway/GOOGLE_API_KEY") -// ), -// GOOGLE_MAPS_API_KEY: Secret.fromSecretsManager( -// secret.Secret.fromSecretNameV2( -// scope, -// "googleMapsApiKey", -// "/doorway/GOOGLE_MAPS_API_KEY" -// ) -// ), -// GOOGLE_MAPS_MAP_ID: Secret.fromSecretsManager( -// secret.Secret.fromSecretNameV2( -// scope, -// "googleMapsMapId", -// "/doorway/GOOGLE_MAPS_MAP_ID" -// ) -// ), -// GTM_KEY: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "GTM_KEY", -// `/doorway/${props.environment}/public-portal/GTM_KEY` -// ) -// ), -// IDLE_TIMEOUT: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "IDLE_TIMEOUT", -// `/doorway/${props.environment}/public-portal/IDLE_TIMEOUT` -// ) -// ), -// JURISDICTION_NAME: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "JURISDICTION_NAME", -// `/doorway/${props.environment}/public-portal/JURISDICTION_NAME` -// ) -// ), -// LANGUAGES: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "LANGUAGES", -// `/doorway/${props.environment}/public-portal/LANGUAGES` -// ) -// ), -// LISTINGS_QUERY: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "LISTINGS_QUERY", -// `/doorway/${props.environment}/public-portal/LISTINGS_QUERY` -// ) -// ), -// NEXTJS_PORT: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "NEXTJS_PORT", -// `/doorway/${props.environment}/public-portal/NEXTJS_PORT` -// ) -// ), -// NOTIFICATIONS_SIGN_UP_URL: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "NOTIFICATIONS_SIGN_UP_URL", -// `/doorway/${props.environment}/public-portal/NOTIFICATIONS_SIGN_UP_URL` -// ) -// ), -// SHOW_ALL_MAP_PINS: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "SHOW_ALL_MAP_PINS", -// `/doorway/${props.environment}/public-portal/SHOW_ALL_MAP_PINS` -// ) -// ), -// SHOW_ALL_PROFESSIONAL_PARTNERS: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "SHOW_PROFESSIONAL_PARTNERS", -// `/doorway/${props.environment}/public-portal/SHOW_PROFESSIONAL_PARTNERS` -// ) -// ), -// }, -// environment: { -// BACKEND_API_BASE: `http://backend.${props.environment}.housingbayarea.int`, -// }, -// logDriver: AwsLogDriver.awsLogs({ -// streamPrefix: "doorway-public-portal", -// logGroup: new LogGroup(scope, `doorway-${props.environment}-tasks`, { -// logGroupName: `/doorway/${props.environment}/ecs/doorway-public-portal`, -// }), -// }), -// }, - -// certificate: cert, -// listenerPort: 443, -// redirectHTTP: true, -// sslPolicy: SslPolicy.RECOMMENDED, -// serviceName: `doorway-public-portal-service-${props.environment}`, -// loadBalancerName: `doorway-public-lb-${props.environment}`, -// cpu: 1024, -// memoryLimitMiB: 2048, -// domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, -// domainZone: hostedZone, -// } -// ) -// } -// } +export class DoorwayPublicService extends DoorwayService { + constructor(scope: Construct, id: string, props: DoorwayServiceProps) { + super(scope, id, props) + } +} diff --git a/cdk/lib/doorway_environment/doorway_services.ts b/cdk/lib/doorway_environment/doorway_services.ts index d1cdd4d8d11..8658f20e2f8 100644 --- a/cdk/lib/doorway_environment/doorway_services.ts +++ b/cdk/lib/doorway_environment/doorway_services.ts @@ -1,27 +1,14 @@ import * as cdk from "aws-cdk-lib" import { Fn } from "aws-cdk-lib" import { ISecurityGroup, ISubnet, IVpc, Subnet } from "aws-cdk-lib/aws-ec2" -import { - Cluster, - Compatibility, - ContainerImage, - FargateService, - LogDrivers, - NetworkMode, - Protocol, - Secret, - TaskDefinition, -} from "aws-cdk-lib/aws-ecs" //import * as ecs from "aws-cdk-lib/aws-ecs"; -import { ManagedPolicy, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" -import { LogGroup } from "aws-cdk-lib/aws-logs" +import { ManagedPolicy, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Bucket, IBucket } from "aws-cdk-lib/aws-s3" -import * as secret from "aws-cdk-lib/aws-secretsmanager" -import { EmailIdentity } from "aws-cdk-lib/aws-ses" import { Construct } from "constructs" + import { DoorwayServiceProps } from "./doorway-service-props" -abstract class DoorwayService { +export abstract class DoorwayService { protected executionRole: Role protected privateSG: ISecurityGroup protected publicSG: ISecurityGroup @@ -83,168 +70,3 @@ abstract class DoorwayService { ) } } - -export class DoorwayApiService extends DoorwayService { - public constructor(scope: Construct, id: string, props: DoorwayServiceProps) { - super(scope, id, props) - const environmentVariables = { - ASSET_FILE_SERVICE: process.env.ASSET_FILE_SERVICE || "s3", - LISTINGS_PROCESSING_QUERY: process.env.LISTINGS_PROCESSING_QUERY || "/listings", - PORT: process.env.API_LOCAL_PORT || "3100", - SHOW_LM_LINKS: process.env.SHOW_LM_LINKS || "TRUE", - TIME_ZONE: process.env.TIME_ZONE || "America/Los_Angeles", - SHOW_DUPLICATES: process.env.SHOW_DUPLICATES || "FALSE", - NO_COLOR: process.env.NO_COLOR || "TRUE", - ASSET_FS_CONFIG_s3_PATH_PREFIX: process.env.ASSET_FS_CONFIG_s3_PATH_PREFIX || "", - ASSET_FS_PRIVATE_CONFIG_s3_BUCKET: this.secureUploads.bucketName, - ASSET_FS_CONFIG_s3_BUCKET: this.publicUploads.bucketName, - ASSET_FS_CONFIG_s3_REGION: cdk.Aws.REGION, - ASSET_FS_CONFIG_s3_URL_FORMAT: process.env.ASSET_FS_CONFIG_s3_URL_FORMAT || "public", - THROTTLE_LIMIT: process.env.THROTTLE_LIMIT || "100", - THROTTLE_TTL: process.env.THROTTLE_TTL || "180000", - LOG_LEVEL: process.env.LOG_LEVEL || "info", - LISTING_PROCESSING_CRON_STRING: process.env.LISTING_PROCESSING_CRON_STRING || "0 * * * *", - LOTTERY_PROCESSING_CRON_STRING: process.env.LOTTERY_PROCESSING_CRON_STRING || "0 * * * *", - LOTTERY_PUBLISH_PROCESSING_CRON_STRING: - process.env.LOTTERY_PUBLISH_PROCESSING_CRON_STRING || "58 23 * * *", - AFS_PROCESSING_CRON_STRING: process.env.AFS_PROCESSING_CRON_STRING || "15 * * * *", - TEMP_FILE_CLEAR_CRON_STRING: process.env.TEMP_FILE_CLEAR_CRON_STRING || "30 * * * *", - DUPLICATES_PROCESSING_CRON_STRING: - process.env.DUPLICATES_PROCESSING_CRON_STRING || "5 * * * *", - LOTTERY_DAYS_TILL_EXPIRY: process.env.LOTTERY_DAYS_TILL_EXPIRY || "45", - MFA_CODE_LENGTH: process.env.MFA_CODE_LENGTH || "5", - MFA_CODE_VALID: process.env.MFA_CODE_VALID || "600000", - GOVDELIVERY_TOPIC: process.env.GOVDELIVERY_TOPIC || "5 * * * *", - PARTNERS_PORTAL_URL: - process.env.PARTNERS_PORTAL_URL || - `https://partners.${props.environment}.housingbayarea.mtc.ca.gov`, - PARTNERS_BASE_URL: - process.env.PARTNERS_BASE_URL || - `https://partners.${props.environment}.housingbayarea.mtc.ca.gov/api`, - ASSET_UPLOAD_MAX_SIZE: process.env.ASSET_UPLOAD_MAX_SIZE || "5", - AUTH_LOCK_LOGIN_COOLDOWN: process.env.AUTH_LOCK_LOGIN_COOLDOWN || "1800000", - AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS: - process.env.AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS || "5", - CORS_ORIGINS: - process.env.CORS_ORIGINS || - "https://housingbayarea.mtc.ca.gov, https://partners.housingbayarea.mtc.ca.gov", - DUPLICATES_CLOSE_DATE: process.env.DUPLICATES_CLOSE_DATE || "2024-10-08 00:00 -08:00", - HTTPS_OFF: process.env.HTTPS_OFF || "TRUE", - SAME_SITE: process.env.SAME_SITE || "false", - } - const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) - const dbSecret = secret.Secret.fromSecretCompleteArn(scope, "dbSecret", dbSecretArn) - dbSecret.grantRead(this.executionRole) - const secrets: { [key: string]: Secret } = { - PGUSER: Secret.fromSecretsManager(dbSecret, "username"), - PGPASSWORD: Secret.fromSecretsManager(dbSecret, "password"), - PGPORT: Secret.fromSecretsManager(dbSecret, "port"), - PGHOST: Secret.fromSecretsManager(dbSecret, "host"), - PGDATABASE: Secret.fromSecretsManager(dbSecret, "dbname"), - } - process.env.BACKEND_SECRETS || - "".split(",").forEach((secretName) => { - secrets[secretName] = Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${secretName}`) - ) - }) - // Grant write access to the uploads buckets. - this.publicUploads.grantReadWrite(this.executionRole) - this.publicUploads.grantPut(this.executionRole) - this.secureUploads.grantReadWrite(this.executionRole) - this.secureUploads.grantPut(this.executionRole) - // Get the SES Email Information - const sesIdentity = EmailIdentity.fromEmailIdentityName( - scope, - "sesIdentity", - "housingbayarea2.org" - ) - sesIdentity.grantSendEmail(this.executionRole) - // Add a bunch of random SES grants that grantSendEmail doesn't set up - const policy = new PolicyStatement({ - actions: [ - "ses:SendEmail", - "ses:SendRawEmail", - "ses:SendTemplatedEmail", - "ses:SendRawTemplatedEmail", - "ses:SendBulkTemplatedEmail", - "ses:UseConfiguration", - "ses:SendBulkEmail", - ], - resources: [ - sesIdentity.emailIdentityArn, - `arn:aws:ses:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:configuration-set/dway-config-set`, - `arn:aws:ses:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:identity/*`, - ], - }) - this.executionRole.addToPolicy(policy) - - const logGroup = new LogGroup(scope, `doorway-${props.environment}-tasks`, { - logGroupName: `doorway-${props.environment}-tasks`, - removalPolicy: cdk.RemovalPolicy.DESTROY, - retention: cdk.aws_logs.RetentionDays.ONE_WEEK, - }) - logGroup.grantWrite(this.executionRole) - // Now we're down to business! Set up the fargate container task for the api - // Notice the load of environment variables we add to it below - const task = new TaskDefinition(scope, "apiTask", { - compatibility: Compatibility.FARGATE, - cpu: process.env.API_CPUS || "2048", - memoryMiB: process.env.API_MEMORY || "4096", - executionRole: this.executionRole, - taskRole: this.executionRole, - networkMode: NetworkMode.AWS_VPC, - }) - - task.addContainer("internal-api", { - image: ContainerImage.fromRegistry( - `${cdk.Aws.ACCOUNT_ID}.dkr.ecr.${cdk.Aws.REGION}.amazonaws.com/doorway/backend:run-candidate` - ), - cpu: 1, - memoryLimitMiB: 1024, - essential: true, - logging: LogDrivers.awsLogs({ - streamPrefix: "internal-api", - logGroup: LogGroup.fromLogGroupName( - scope, - "logGroup", - `doorway-${props.environment}-tasks` - ), - }), - secrets: secrets, - environment: environmentVariables, - entryPoint: [], - portMappings: [ - { - containerPort: Number(process.env.API_LOCAL_PORT) || 3100, - protocol: Protocol.TCP, - hostPort: Number(process.env.API_LOCAL_PORT) || 3100, - }, - ], - }) - // Create the service in ECS - const service = new FargateService(scope, `doorway-${props.environment}-internal-api`, { - taskDefinition: task, - serviceName: `doorway-${props.environment}-internal-api`, - cluster: Cluster.fromClusterAttributes(scope, "ecsCluster", { - clusterName: Fn.importValue(`doorway-ecs-cluster-${props.environment}`), - vpc: this.vpc, - }), - vpcSubnets: { - subnets: this.appsubnets, - }, - securityGroups: [this.privateSG], - desiredCount: Number(process.env.API_INSTANCES) || 3, - }) - - const scaling = service.autoScaleTaskCount({ - minCapacity: Number(process.env.API_INSTANCES) || 3, - maxCapacity: Number(process.env.API_MAX_INSTANCES) || 10, - }) - scaling.scaleOnCpuUtilization("CpuScaling", { - targetUtilizationPercent: Number(process.env.API_AUTOSCALE_CPU) || 80, - scaleInCooldown: cdk.Duration.seconds(60), - scaleOutCooldown: cdk.Duration.seconds(60), - }) - } -} From d68d18ac483287f880d3e868d0ed53ac1db2d322 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 19:30:57 -0700 Subject: [PATCH 027/217] fix: removing config source --- .../doorway-build-pipeline-stack.ts | 16 +--------------- cdk/lib/build-pipeline/doorway-docker-build.ts | 2 -- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index fbefe0536fc..fab87d8599e 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -102,19 +102,9 @@ export class DoorwayBuildPipelineStack extends Stack { branch: "feat/tf_to_cdk", output: sourceArtifact, }) - const configSourceArtifact = new Artifact("ConfigSourceArtifact") - const doorwayConfigSource = new GitHubSourceAction({ - actionName: "ConfigSource", - oauthToken: githubSecret, - owner: "metrotranscom", - repo: "doorway-config", - branch: "feat/cdk-terraform", - output: configSourceArtifact, - }) - pipeline.addStage({ stageName: "Source", - actions: [doorwaySource, doorwayConfigSource], + actions: [doorwaySource], }) pipeline.addStage({ stageName: "Build", @@ -123,7 +113,6 @@ export class DoorwayBuildPipelineStack extends Stack { buildspec: "../ci/buildspec/build_backend.yml", imageName: "backend", source: sourceArtifact, - configSource: configSourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, }).action, @@ -131,7 +120,6 @@ export class DoorwayBuildPipelineStack extends Stack { buildspec: "../ci/buildspec/build_import_listings.yml", imageName: "import-listings", source: sourceArtifact, - configSource: configSourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, }).action, @@ -139,7 +127,6 @@ export class DoorwayBuildPipelineStack extends Stack { buildspec: "../ci/buildspec/build_partners.yml", imageName: "partners", source: sourceArtifact, - configSource: configSourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, }).action, @@ -147,7 +134,6 @@ export class DoorwayBuildPipelineStack extends Stack { buildspec: "../ci/buildspec/build_public.yml", imageName: "public", source: sourceArtifact, - configSource: configSourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, }).action, diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts index 6e14b27f054..a6a11da75e8 100644 --- a/cdk/lib/build-pipeline/doorway-docker-build.ts +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -12,7 +12,6 @@ export interface DoorwayDockerBuildProps { buildspec: string imageName: string source: Artifact - configSource?: Artifact dockerHubSecret: string buildRole: Role } @@ -49,7 +48,6 @@ export class DoorwayDockerBuild { this.action = new CodeBuildAction({ actionName: `${id}-DockerBuild`, input: props.source, - extraInputs: props.configSource ? [props.configSource] : undefined, outputs: [new Artifact(`${id}-BuildOutput`)], project, }) From f33a500a106a4f6093247cd5c8dd837cbc43f604 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 20:08:28 -0700 Subject: [PATCH 028/217] feat adding serviceconnect --- .../doorway-api-service.ts | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index f492384d584..0b774be96fd 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -13,7 +13,9 @@ import { import { PolicyStatement } from "aws-cdk-lib/aws-iam" import { LogGroup } from "aws-cdk-lib/aws-logs" import * as secret from "aws-cdk-lib/aws-secretsmanager" +import { PrivateDnsNamespace } from "aws-cdk-lib/aws-servicediscovery" import { EmailIdentity } from "aws-cdk-lib/aws-ses" +import { StringParameter } from "aws-cdk-lib/aws-ssm" import { Construct } from "constructs" import { DoorwayService } from "./doorway_services" @@ -157,6 +159,19 @@ export class DoorwayApiService extends DoorwayService { }, ], }) + const namespace = new PrivateDnsNamespace( + scope, + `doorway-${props.environment}-internal-api-namespace`, + { + vpc: this.vpc, + name: `doorway-${props.environment}-internal-api`, + description: `Private DNS namespace for the Doorway ${props.environment} internal API`, + } + ) + const privateCAArn = StringParameter.fromStringParameterAttributes(scope, "privateCAArn", { + parameterName: `/doorway/privateCertAuthority`, + }).stringValue + // Create the service in ECS const service = new FargateService(scope, `doorway-${props.environment}-internal-api`, { taskDefinition: task, @@ -170,6 +185,28 @@ export class DoorwayApiService extends DoorwayService { }, securityGroups: [this.privateSG], desiredCount: Number(process.env.API_INSTANCES) || 3, + serviceConnectConfiguration: { + namespace: namespace.namespaceName, + logDriver: LogDrivers.awsLogs({ + streamPrefix: "internal-api-service-connect", + logGroup: LogGroup.fromLogGroupName( + scope, + "logGroup", + `doorway-${props.environment}-tasks` + ), + }), + services: [ + { + portMappingName: process.env.API_LOCAL_PORT || "3100", + dnsName: `backend.${props.environment}.housingbayarea.int`, + + tls: { + awsPcaAuthorityArn: privateCAArn, + role: this.executionRole, + }, + }, + ], + }, }) const scaling = service.autoScaleTaskCount({ From 0d736597b39ced5403214109779ddab4f1e7dccd Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 20:23:54 -0700 Subject: [PATCH 029/217] fix: dupe log group name --- cdk/lib/doorway_environment/doorway-api-service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index 0b774be96fd..2370c2abde5 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -159,6 +159,9 @@ export class DoorwayApiService extends DoorwayService { }, ], }) + //Setting up ServiceConnect which will allow the partenr and public services + // to connect to the internal api service directly through ECS + // without a load balancer. const namespace = new PrivateDnsNamespace( scope, `doorway-${props.environment}-internal-api-namespace`, @@ -168,6 +171,7 @@ export class DoorwayApiService extends DoorwayService { description: `Private DNS namespace for the Doorway ${props.environment} internal API`, } ) + // The private CA is used for TLS encryption of ServiceConnect traffic. const privateCAArn = StringParameter.fromStringParameterAttributes(scope, "privateCAArn", { parameterName: `/doorway/privateCertAuthority`, }).stringValue @@ -189,11 +193,7 @@ export class DoorwayApiService extends DoorwayService { namespace: namespace.namespaceName, logDriver: LogDrivers.awsLogs({ streamPrefix: "internal-api-service-connect", - logGroup: LogGroup.fromLogGroupName( - scope, - "logGroup", - `doorway-${props.environment}-tasks` - ), + logGroup: logGroup, }), services: [ { From ddd9fb6f5919339b49cfe9e47870a7c3f1739928 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 20:41:21 -0700 Subject: [PATCH 030/217] fix: port mapping name --- cdk/lib/doorway_environment/doorway-api-service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index 2370c2abde5..69584538f1e 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -153,6 +153,7 @@ export class DoorwayApiService extends DoorwayService { entryPoint: [], portMappings: [ { + name: "internal-api-port", containerPort: Number(process.env.API_LOCAL_PORT) || 3100, protocol: Protocol.TCP, hostPort: Number(process.env.API_LOCAL_PORT) || 3100, @@ -197,7 +198,7 @@ export class DoorwayApiService extends DoorwayService { }), services: [ { - portMappingName: process.env.API_LOCAL_PORT || "3100", + portMappingName: "internal-api-port", dnsName: `backend.${props.environment}.housingbayarea.int`, tls: { From e1a9dcc03eafe6dac9d77afb69eb7b026d3f4c1f Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 19 Aug 2025 20:57:27 -0700 Subject: [PATCH 031/217] fix: removing role --- cdk/lib/doorway_environment/doorway-api-service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index 69584538f1e..73de8075ca1 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -203,7 +203,6 @@ export class DoorwayApiService extends DoorwayService { tls: { awsPcaAuthorityArn: privateCAArn, - role: this.executionRole, }, }, ], From ce0bb0bdf961e3e0aa94b7d8b992ff677e358c12 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 20 Aug 2025 09:28:10 -0700 Subject: [PATCH 032/217] fix: creating role for serviceconnect --- .../doorway-api-service.ts | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index 73de8075ca1..18f52a368ea 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -10,7 +10,13 @@ import { Secret, TaskDefinition, } from "aws-cdk-lib/aws-ecs" -import { PolicyStatement } from "aws-cdk-lib/aws-iam" +import { + CompositePrincipal, + ManagedPolicy, + PolicyStatement, + Role, + ServicePrincipal, +} from "aws-cdk-lib/aws-iam" import { LogGroup } from "aws-cdk-lib/aws-logs" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { PrivateDnsNamespace } from "aws-cdk-lib/aws-servicediscovery" @@ -18,8 +24,8 @@ import { EmailIdentity } from "aws-cdk-lib/aws-ses" import { StringParameter } from "aws-cdk-lib/aws-ssm" import { Construct } from "constructs" -import { DoorwayService } from "./doorway_services" import { DoorwayServiceProps } from "./doorway-service-props" +import { DoorwayService } from "./doorway_services" export class DoorwayApiService extends DoorwayService { public constructor(scope: Construct, id: string, props: DoorwayServiceProps) { @@ -176,6 +182,17 @@ export class DoorwayApiService extends DoorwayService { const privateCAArn = StringParameter.fromStringParameterAttributes(scope, "privateCAArn", { parameterName: `/doorway/privateCertAuthority`, }).stringValue + const scRole = new Role(scope, `doorway-${props.environment}-internal-api-sc-role`, { + assumedBy: new CompositePrincipal( + new ServicePrincipal("ecs.amazonaws.com"), + new ServicePrincipal("ecs-tasks.amazonaws.com") + ), + managedPolicies: [ + ManagedPolicy.fromAwsManagedPolicyName( + "AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity" + ), + ], + }) // Create the service in ECS const service = new FargateService(scope, `doorway-${props.environment}-internal-api`, { @@ -203,6 +220,7 @@ export class DoorwayApiService extends DoorwayService { tls: { awsPcaAuthorityArn: privateCAArn, + role: scRole, }, }, ], From cd26082ec22d08f4e2d0a2e7fb3d79465153148c Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 20 Aug 2025 09:45:14 -0700 Subject: [PATCH 033/217] fix: changing role name --- .../doorway-api-service.ts | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index 18f52a368ea..aef97a43df7 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -1,22 +1,6 @@ import { Aws, aws_logs, Duration, Fn, RemovalPolicy } from "aws-cdk-lib" -import { - Cluster, - Compatibility, - ContainerImage, - FargateService, - LogDrivers, - NetworkMode, - Protocol, - Secret, - TaskDefinition, -} from "aws-cdk-lib/aws-ecs" -import { - CompositePrincipal, - ManagedPolicy, - PolicyStatement, - Role, - ServicePrincipal, -} from "aws-cdk-lib/aws-iam" +import { Cluster, Compatibility, ContainerImage, FargateService, LogDrivers, NetworkMode, Protocol, Secret, TaskDefinition } from "aws-cdk-lib/aws-ecs" +import { CompositePrincipal, ManagedPolicy, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { LogGroup } from "aws-cdk-lib/aws-logs" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { PrivateDnsNamespace } from "aws-cdk-lib/aws-servicediscovery" @@ -24,8 +8,8 @@ import { EmailIdentity } from "aws-cdk-lib/aws-ses" import { StringParameter } from "aws-cdk-lib/aws-ssm" import { Construct } from "constructs" -import { DoorwayServiceProps } from "./doorway-service-props" import { DoorwayService } from "./doorway_services" +import { DoorwayServiceProps } from "./doorway-service-props" export class DoorwayApiService extends DoorwayService { public constructor(scope: Construct, id: string, props: DoorwayServiceProps) { @@ -189,7 +173,7 @@ export class DoorwayApiService extends DoorwayService { ), managedPolicies: [ ManagedPolicy.fromAwsManagedPolicyName( - "AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity" + "service-role/AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity" ), ], }) From 9fbf27de3fe5a3259b1078681566a54e5cc4ad4a Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 20 Aug 2025 09:46:41 -0700 Subject: [PATCH 034/217] fix: formatting --- .../doorway-api-service.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index aef97a43df7..5ff1cca58d7 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -1,6 +1,22 @@ import { Aws, aws_logs, Duration, Fn, RemovalPolicy } from "aws-cdk-lib" -import { Cluster, Compatibility, ContainerImage, FargateService, LogDrivers, NetworkMode, Protocol, Secret, TaskDefinition } from "aws-cdk-lib/aws-ecs" -import { CompositePrincipal, ManagedPolicy, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import { + Cluster, + Compatibility, + ContainerImage, + FargateService, + LogDrivers, + NetworkMode, + Protocol, + Secret, + TaskDefinition, +} from "aws-cdk-lib/aws-ecs" +import { + CompositePrincipal, + ManagedPolicy, + PolicyStatement, + Role, + ServicePrincipal, +} from "aws-cdk-lib/aws-iam" import { LogGroup } from "aws-cdk-lib/aws-logs" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { PrivateDnsNamespace } from "aws-cdk-lib/aws-servicediscovery" From f9015d11dec5b19ce647cd6a4fca13f35946649c Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 20 Aug 2025 10:03:47 -0700 Subject: [PATCH 035/217] fix: adding dependency on namespace --- cdk/lib/doorway_environment/doorway-api-service.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index 5ff1cca58d7..2fc79956a7d 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -206,9 +206,10 @@ export class DoorwayApiService extends DoorwayService { subnets: this.appsubnets, }, securityGroups: [this.privateSG], + desiredCount: Number(process.env.API_INSTANCES) || 3, serviceConnectConfiguration: { - namespace: namespace.namespaceName, + namespace: namespace.namespaceArn, logDriver: LogDrivers.awsLogs({ streamPrefix: "internal-api-service-connect", logGroup: logGroup, @@ -227,6 +228,9 @@ export class DoorwayApiService extends DoorwayService { }, }) + // Ensure the namespace is created before the service + service.node.addDependency(namespace) + const scaling = service.autoScaleTaskCount({ minCapacity: Number(process.env.API_INSTANCES) || 3, maxCapacity: Number(process.env.API_MAX_INSTANCES) || 10, From 9bef54a669b8c8b87c477afcb7832b9a48f84c08 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 20 Aug 2025 10:20:29 -0700 Subject: [PATCH 036/217] fix: granting access to private ca --- cdk/lib/doorway_environment/doorway-api-service.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index 2fc79956a7d..a2936fdca63 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -192,6 +192,12 @@ export class DoorwayApiService extends DoorwayService { "service-role/AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity" ), ], + inlinePolicies: { + pcaAuth: new PolicyStatement({ + actions: ["acm-pca:*"], + resources: [privateCAArn], + }), + }, }) // Create the service in ECS From 09079abfb2b8ae37a6e318c45d7d81f4e8b046e7 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 20 Aug 2025 10:22:44 -0700 Subject: [PATCH 037/217] fix: fixing policy document --- cdk/lib/doorway_environment/doorway-api-service.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index a2936fdca63..f51aed904a7 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -13,6 +13,7 @@ import { import { CompositePrincipal, ManagedPolicy, + PolicyDocument, PolicyStatement, Role, ServicePrincipal, @@ -193,9 +194,13 @@ export class DoorwayApiService extends DoorwayService { ), ], inlinePolicies: { - pcaAuth: new PolicyStatement({ - actions: ["acm-pca:*"], - resources: [privateCAArn], + pcaAuth: new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: ["acm-pca:*"], + resources: [privateCAArn], + }), + ], }), }, }) From ba3496260f9b20b131021921667793fc76891a97 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 20 Aug 2025 12:19:38 -0700 Subject: [PATCH 038/217] fix: adding api key back --- cdk/jest.config.js | 11 +++++++++++ cdk/lib/doorway_environment/doorway-api-service.ts | 1 + 2 files changed, 12 insertions(+) create mode 100644 cdk/jest.config.js diff --git a/cdk/jest.config.js b/cdk/jest.config.js new file mode 100644 index 00000000000..5223f6d1a6d --- /dev/null +++ b/cdk/jest.config.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +module.exports = { + testEnvironment: "node", + roots: ["/test"], + testMatch: ["**/*.test.ts"], + transform: { + "^.+\\.tsx?$": "ts-jest", + }, +}; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiamVzdC5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJqZXN0LmNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLE1BQU0sQ0FBQyxPQUFPLEdBQUc7SUFDZixlQUFlLEVBQUUsTUFBTTtJQUN2QixLQUFLLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQztJQUN6QixTQUFTLEVBQUUsQ0FBQyxjQUFjLENBQUM7SUFDM0IsU0FBUyxFQUFFO1FBQ1QsYUFBYSxFQUFFLFNBQVM7S0FDekI7Q0FDRixDQUFBIiwic291cmNlc0NvbnRlbnQiOlsibW9kdWxlLmV4cG9ydHMgPSB7XG4gIHRlc3RFbnZpcm9ubWVudDogXCJub2RlXCIsXG4gIHJvb3RzOiBbXCI8cm9vdERpcj4vdGVzdFwiXSxcbiAgdGVzdE1hdGNoOiBbXCIqKi8qLnRlc3QudHNcIl0sXG4gIHRyYW5zZm9ybToge1xuICAgIFwiXi4rXFxcXC50c3g/JFwiOiBcInRzLWplc3RcIixcbiAgfSxcbn1cbiJdfQ== \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index f51aed904a7..79d09bd63a3 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -75,6 +75,7 @@ export class DoorwayApiService extends DoorwayService { DUPLICATES_CLOSE_DATE: process.env.DUPLICATES_CLOSE_DATE || "2024-10-08 00:00 -08:00", HTTPS_OFF: process.env.HTTPS_OFF || "TRUE", SAME_SITE: process.env.SAME_SITE || "false", + API_KEY: process.env.API_KEY || "doorway-api-key", } const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) const dbSecret = secret.Secret.fromSecretCompleteArn(scope, "dbSecret", dbSecretArn) From ad783a0b30618ab5203ef5e749034b365350b12d Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 20 Aug 2025 14:25:24 -0700 Subject: [PATCH 039/217] adding public site --- cdk/dev2.env | 10 +- cdk/jest.config.js | 20 +-- .../build-pipeline/doorway-build-job-base.ts | 2 +- .../doorway-build-pipeline-stack.ts | 6 +- .../doorway-api-service.ts | 14 +- .../doorway-app-environment-stack.ts | 4 + .../doorway-public-site.ts | 157 ++++++++++++++++++ ...doorway_services.ts => doorway_service.ts} | 14 +- 8 files changed, 195 insertions(+), 32 deletions(-) create mode 100644 cdk/lib/doorway_environment/doorway-public-site.ts rename cdk/lib/doorway_environment/{doorway_services.ts => doorway_service.ts} (96%) diff --git a/cdk/dev2.env b/cdk/dev2.env index 3044b37f65a..bf626f06792 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -7,15 +7,17 @@ BACKEND_API_BASE=http://backend.dev.housingbayarea.int ECR_ACCOUNT_ID=364076391763 MAPBOX_TOKEN=pk.eyJ1Ijoic2Vhbi1hbGJlcnQtZXh5Z3kiLCJhIjoiY2tybnY5amlyMDNraDJubXc4c3ZhOGswYyJ9.KE5FGb_Tch8BPrOYMvshXw USE_SECURE_DOWNLOAD_PATHWAY=TRUE -ECR_REGION=us-west-1 -SOURCE_REPO_NAMESPACE=doorway-dev -ECR_NAMESPACE=doorway-dev +GTM_KEY=GTM-KF22FJP +ECR_REGION=us-west-2 +SOURCE_REPO_NAMESPACE=doorway +ECR_NAMESPACE=doorway API_PASS_KEY=dev-key IDLE_TIMEOUT=5 CLOUDINARY_CLOUD_NAME=exygy API_LOCAL_PORT=3100 PUBLIC_LOCAL_PORT=3000 PARTNERS_LOCAL_PORT=3200 -PUBLIC_SECRETS= +PUBLIC_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY PARTNERS_SECRETS= + BACKEND_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVEDELIVERY_API_URL, diff --git a/cdk/jest.config.js b/cdk/jest.config.js index 5223f6d1a6d..30259e5e5b6 100644 --- a/cdk/jest.config.js +++ b/cdk/jest.config.js @@ -1,11 +1,11 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); +"use strict" +Object.defineProperty(exports, "__esModule", { value: true }) module.exports = { - testEnvironment: "node", - roots: ["/test"], - testMatch: ["**/*.test.ts"], - transform: { - "^.+\\.tsx?$": "ts-jest", - }, -}; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiamVzdC5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJqZXN0LmNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLE1BQU0sQ0FBQyxPQUFPLEdBQUc7SUFDZixlQUFlLEVBQUUsTUFBTTtJQUN2QixLQUFLLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQztJQUN6QixTQUFTLEVBQUUsQ0FBQyxjQUFjLENBQUM7SUFDM0IsU0FBUyxFQUFFO1FBQ1QsYUFBYSxFQUFFLFNBQVM7S0FDekI7Q0FDRixDQUFBIiwic291cmNlc0NvbnRlbnQiOlsibW9kdWxlLmV4cG9ydHMgPSB7XG4gIHRlc3RFbnZpcm9ubWVudDogXCJub2RlXCIsXG4gIHJvb3RzOiBbXCI8cm9vdERpcj4vdGVzdFwiXSxcbiAgdGVzdE1hdGNoOiBbXCIqKi8qLnRlc3QudHNcIl0sXG4gIHRyYW5zZm9ybToge1xuICAgIFwiXi4rXFxcXC50c3g/JFwiOiBcInRzLWplc3RcIixcbiAgfSxcbn1cbiJdfQ== \ No newline at end of file + testEnvironment: "node", + roots: ["/test"], + testMatch: ["**/*.test.ts"], + transform: { + "^.+\\.tsx?$": "ts-jest", + }, +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiamVzdC5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJqZXN0LmNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLE1BQU0sQ0FBQyxPQUFPLEdBQUc7SUFDZixlQUFlLEVBQUUsTUFBTTtJQUN2QixLQUFLLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQztJQUN6QixTQUFTLEVBQUUsQ0FBQyxjQUFjLENBQUM7SUFDM0IsU0FBUyxFQUFFO1FBQ1QsYUFBYSxFQUFFLFNBQVM7S0FDekI7Q0FDRixDQUFBIiwic291cmNlc0NvbnRlbnQiOlsibW9kdWxlLmV4cG9ydHMgPSB7XG4gIHRlc3RFbnZpcm9ubWVudDogXCJub2RlXCIsXG4gIHJvb3RzOiBbXCI8cm9vdERpcj4vdGVzdFwiXSxcbiAgdGVzdE1hdGNoOiBbXCIqKi8qLnRlc3QudHNcIl0sXG4gIHRyYW5zZm9ybToge1xuICAgIFwiXi4rXFxcXC50c3g/JFwiOiBcInRzLWplc3RcIixcbiAgfSxcbn1cbiJdfQ== diff --git a/cdk/lib/build-pipeline/doorway-build-job-base.ts b/cdk/lib/build-pipeline/doorway-build-job-base.ts index ca633fe3326..5bd96dabdfc 100644 --- a/cdk/lib/build-pipeline/doorway-build-job-base.ts +++ b/cdk/lib/build-pipeline/doorway-build-job-base.ts @@ -105,7 +105,7 @@ export class DoorwayBuildJobBase { const project = new PipelineProject( stack, `doorway-dbMigrate-${props.environment}`, - projectProps + projectProps, ) // Create the CodeBuild action this.action = new CodeBuildAction({ diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index fab87d8599e..d0838e9e4a9 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -31,7 +31,7 @@ export class DoorwayBuildPipelineStack extends Stack { "iam:PassRole", ], resources: ["*"], - }) + }), ) const buildRole = new Role(this, `${id}-doorway-app-build-role`, { assumedBy: new ServicePrincipal("codebuild.amazonaws.com"), @@ -81,13 +81,13 @@ export class DoorwayBuildPipelineStack extends Stack { const dockerSecret = Secret.fromSecretNameV2( this, "dockerSecret", - props.dockerHubSecret + props.dockerHubSecret, ).secretArn const githubSecret = Secret.fromSecretNameV2( this, "githubSecret", - props.githubSecret + props.githubSecret, ).secretValue const pipeline = new Pipeline(this, "doorway-app-pipeline", { role: pipelineRole, diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index 79d09bd63a3..a98c7797a62 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -90,7 +90,7 @@ export class DoorwayApiService extends DoorwayService { process.env.BACKEND_SECRETS || "".split(",").forEach((secretName) => { secrets[secretName] = Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${secretName}`) + secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${secretName}`), ) }) // Grant write access to the uploads buckets. @@ -102,7 +102,7 @@ export class DoorwayApiService extends DoorwayService { const sesIdentity = EmailIdentity.fromEmailIdentityName( scope, "sesIdentity", - "housingbayarea2.org" + "housingbayarea2.org", ) sesIdentity.grantSendEmail(this.executionRole) // Add a bunch of random SES grants that grantSendEmail doesn't set up @@ -143,7 +143,7 @@ export class DoorwayApiService extends DoorwayService { task.addContainer("internal-api", { image: ContainerImage.fromRegistry( - `${Aws.ACCOUNT_ID}.dkr.ecr.${Aws.REGION}.amazonaws.com/doorway/backend:run-candidate` + `${Aws.ACCOUNT_ID}.dkr.ecr.${Aws.REGION}.amazonaws.com/doorway/backend:run-candidate`, ), cpu: 1, memoryLimitMiB: 1024, @@ -153,7 +153,7 @@ export class DoorwayApiService extends DoorwayService { logGroup: LogGroup.fromLogGroupName( scope, "logGroup", - `doorway-${props.environment}-tasks` + `doorway-${props.environment}-tasks`, ), }), secrets: secrets, @@ -178,7 +178,7 @@ export class DoorwayApiService extends DoorwayService { vpc: this.vpc, name: `doorway-${props.environment}-internal-api`, description: `Private DNS namespace for the Doorway ${props.environment} internal API`, - } + }, ) // The private CA is used for TLS encryption of ServiceConnect traffic. const privateCAArn = StringParameter.fromStringParameterAttributes(scope, "privateCAArn", { @@ -187,11 +187,11 @@ export class DoorwayApiService extends DoorwayService { const scRole = new Role(scope, `doorway-${props.environment}-internal-api-sc-role`, { assumedBy: new CompositePrincipal( new ServicePrincipal("ecs.amazonaws.com"), - new ServicePrincipal("ecs-tasks.amazonaws.com") + new ServicePrincipal("ecs-tasks.amazonaws.com"), ), managedPolicies: [ ManagedPolicy.fromAwsManagedPolicyName( - "service-role/AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity" + "service-role/AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity", ), ], inlinePolicies: { diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index caa34bf1530..bccceaa9f29 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -3,6 +3,7 @@ import { Construct } from "constructs" import dotenv from "dotenv" import { DoorwayApiService } from "./doorway-api-service" +import { DoorwayPublicService } from "./doorway-public-service" export class DoorwayAppEnvironmentStack extends Stack { constructor(scope: Construct, id: string) { @@ -13,5 +14,8 @@ export class DoorwayAppEnvironmentStack extends Stack { new DoorwayApiService(this, `doorway-api-service-${environment}`, { environment: environment, }) + new DoorwayPublicService(this, `doorway-public-service-${environment}`, { + environment: environment, + }) } } diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts new file mode 100644 index 00000000000..22964b8f3d0 --- /dev/null +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -0,0 +1,157 @@ +import { Fn } from "aws-cdk-lib" +import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager" +import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" +import { Repository } from "aws-cdk-lib/aws-ecr" +import { AwsLogDriver, Cluster, ContainerImage, Secret } from "aws-cdk-lib/aws-ecs" +import { ApplicationLoadBalancedFargateService } from "aws-cdk-lib/aws-ecs-patterns" +import { SslPolicy } from "aws-cdk-lib/aws-elasticloadbalancingv2" +import { LogGroup } from "aws-cdk-lib/aws-logs" +import { PublicHostedZone } from "aws-cdk-lib/aws-route53" +import * as secret from "aws-cdk-lib/aws-secretsmanager" +import { StringParameter } from "aws-cdk-lib/aws-ssm" +import { Construct } from "constructs" + +import { DoorwayServiceProps } from "./doorway-service-props" +import { DoorwayService } from "./doorway_services" + +export class DoorwayPublicSite extends DoorwayService { + constructor(scope: Construct, id: string, props: DoorwayServiceProps) { + super(scope, id, props) + const environmentVariables: { [key: string]: string } = { + BACKEND_API_BASE: + process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, + BLOOM_API_BASE: process.env.BLOOM_API_BASE || "https://proxy.housingbayarea.org", + CACHE_REVALIDATE: process.env.CACHE_REVALIDATE || "60", + GTM_KEY: process.env.GTM_KEY || "GTM-KF22FJP", + IDLE_TIMEOUT: process.env.IDLE_TIMEOUT || "5", + JURISDICTION_NAME: process.env.JURISDICTION_NAME || "Bay Area", + LANGUAGES: process.env.LANGUAGES || "en,es,zh,vi,tl", + LISTINGS_QUERY: process.env.LISTINGS_QUERY || "/listings", + NEXTJS_PORT: process.env.PUBLIC_PORT || "3000", + NODE_ENV: process.env.NODE_ENV || "development", + NOTIFICATIONS_SIGNUP_URL: + process.env.NOTIFICATIONS_SIGNUP_URL || + "https://public.govdelivery.com/accounts/CAMTC/signup/36832", + SHOW_ALL_MAP_PINS: process.env.SHOW_ALL_MAP_PINS || "true", + SHOW_PROFESSIONAL_PARTNERS: process.env.SHOW_PROFESSIONAL_PARTNERS || "true", + } + const secrets: { [key: string]: Secret } = {} + process.env.PUBLIC_SITE_SECRETS || + "".split(",").forEach((secretName) => { + secrets[secretName] = Secret.fromSecretsManager( + secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${secretName}`), + ) + }) + + const hostedZone = PublicHostedZone.fromHostedZoneAttributes( + scope, + `doorway-public-hosted-zone-${props.environment}`, + { + hostedZoneId: StringParameter.valueForStringParameter(scope, `/doorway/public-hosted-zone`), + zoneName: `housingbayarea.mtc.ca.gov`, + }, + ) + const cert = new Certificate(scope, "doorwayPublicCert", { + domainName: + process.env.PUBLIC_DOMAIN_NAME || `${props.environment}.housingbayarea.mtc.ca.gov`, + validation: { + method: CertificateValidation.fromDns().method, + props: { + hostedZone: hostedZone, + }, + }, + }) + + const publicService = new ApplicationLoadBalancedFargateService( + scope, + `doorway-public-portal-service-${props.environment}`, + { + cluster: Cluster.fromClusterAttributes(scope, `doorway-ecs-cluster-${props.environment}`, { + clusterName: `doorway-ecs-cluster-${props.environment}`, + vpc: Vpc.fromVpcAttributes(scope, "vpc", { + vpcId: Fn.importValue(`doorway-vpc-id-${props.environment}`), + availabilityZones: Fn.importValue(`doorway-azs-${props.environment}`).split(","), + publicSubnetIds: [ + Fn.importValue(`doorway-public-subnet-1-${props.environment}`), + Fn.importValue(`doorway-public-subnet-2-${props.environment}`), + ], + }), + securityGroups: [ + SecurityGroup.fromSecurityGroupId( + scope, + `doorway-app-sg-${props.environment}`, + Fn.importValue(`doorway-app-sg-${props.environment}`), + ), + ], + }), + taskSubnets: { + subnets: [ + Subnet.fromSubnetId( + scope, + `doorway-app-subnet-1-${props.environment}`, + Fn.importValue(`doorway-app-subnet-1-${props.environment}`), + ), + Subnet.fromSubnetId( + scope, + `doorway-app-subnet-2-${props.environment}`, + Fn.importValue(`doorway-app-subnet-2-${props.environment}`), + ), + ], + }, + + assignPublicIp: true, + publicLoadBalancer: true, + taskImageOptions: { + image: ContainerImage.fromEcrRepository( + Repository.fromRepositoryName( + scope, + `doorway-public-portal-repo-${props.environment}`, + `doorway/public`, + ), + "run-f21478f5", + ), + enableLogging: true, + containerPort: 3000, + executionRole: this.executionRole, + taskRole: this.executionRole, + secrets: secrets, + environment: environmentVariables, + logDriver: AwsLogDriver.awsLogs({ + streamPrefix: "doorway-public-portal", + logGroup: new LogGroup(scope, `doorway-${props.environment}-tasks`, { + logGroupName: `doorway-${props.environment}-tasks`, + }), + }), + }, + + certificate: cert, + listenerPort: 443, + redirectHTTP: true, + sslPolicy: SslPolicy.RECOMMENDED, + serviceName: `doorway-public-portal-service-${props.environment}`, + loadBalancerName: `doorway-public-lb-${props.environment}`, + cpu: 1024, + memoryLimitMiB: 2048, + domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, + domainZone: hostedZone, + }, + ) + + // Configure Service Connect on the underlying Fargate service + const cfnService = publicService.service.node.defaultChild as any + cfnService.serviceConnectConfiguration = { + enabled: true, + namespace: `doorway-${props.environment}-internal-api`, + services: [ + { + clientAliases: [ + { + dnsName: `backend.${props.environment}.housingbayarea.int`, + port: 3100, + }, + ], + }, + ], + } + } +} diff --git a/cdk/lib/doorway_environment/doorway_services.ts b/cdk/lib/doorway_environment/doorway_service.ts similarity index 96% rename from cdk/lib/doorway_environment/doorway_services.ts rename to cdk/lib/doorway_environment/doorway_service.ts index 8658f20e2f8..51ef8796212 100644 --- a/cdk/lib/doorway_environment/doorway_services.ts +++ b/cdk/lib/doorway_environment/doorway_service.ts @@ -25,16 +25,16 @@ export abstract class DoorwayService { assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), }) this.executionRole.addManagedPolicy( - ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy") + ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy"), ) this.executionRole.addManagedPolicy( - ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess") + ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess"), ) this.executionRole.addManagedPolicy( - ManagedPolicy.fromAwsManagedPolicyName("AmazonRDSReadOnlyAccess") + ManagedPolicy.fromAwsManagedPolicyName("AmazonRDSReadOnlyAccess"), ) this.executionRole.addManagedPolicy( - ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly") + ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly"), ) // Get Network setup const vpcId = cdk.Fn.importValue(`doorway-vpc-id-${props.environment}`) @@ -54,19 +54,19 @@ export abstract class DoorwayService { this.privateSG = cdk.aws_ec2.SecurityGroup.fromSecurityGroupId( scope, "appTierPrivateSG", - appTierPrivateSGId + appTierPrivateSGId, ) //Uploads Buckets this.publicUploads = Bucket.fromBucketArn( scope, `publicUploadsBucket`, - Fn.importValue(`doorway-public-uploads-${props.environment}`) + Fn.importValue(`doorway-public-uploads-${props.environment}`), ) this.secureUploads = Bucket.fromBucketArn( scope, `secureUploadsBucket`, - Fn.importValue(`doorway-secure-uploads-${props.environment}`) + Fn.importValue(`doorway-secure-uploads-${props.environment}`), ) } } From d5d1ac2a23cd996156f82cf7be341fdc4e85e871 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 20 Aug 2025 15:00:20 -0700 Subject: [PATCH 040/217] fix: fixing compile errors --- .prettierrc | 6 + cdk/jest.config.js | 20 +- .../doorway-api-service.ts | 2 +- .../doorway-app-environment-stack.ts | 4 +- .../doorway-public-service-old.ts | 222 - .../doorway-public-service.ts | 10 - .../doorway-public-site.ts | 2 +- .../doorway_environment/doorway_service.ts | 10 +- cdk/package-lock.json | 4422 ----------------- 9 files changed, 25 insertions(+), 4673 deletions(-) create mode 100644 .prettierrc delete mode 100644 cdk/lib/doorway_environment/doorway-public-service-old.ts delete mode 100644 cdk/lib/doorway_environment/doorway-public-service.ts delete mode 100644 cdk/package-lock.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000000..0cfcda40faa --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": false, + "trailingComma": "es5", + "semi": false, + "printWidth": 100 +} diff --git a/cdk/jest.config.js b/cdk/jest.config.js index 30259e5e5b6..5223f6d1a6d 100644 --- a/cdk/jest.config.js +++ b/cdk/jest.config.js @@ -1,11 +1,11 @@ -"use strict" -Object.defineProperty(exports, "__esModule", { value: true }) +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); module.exports = { - testEnvironment: "node", - roots: ["/test"], - testMatch: ["**/*.test.ts"], - transform: { - "^.+\\.tsx?$": "ts-jest", - }, -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiamVzdC5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJqZXN0LmNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLE1BQU0sQ0FBQyxPQUFPLEdBQUc7SUFDZixlQUFlLEVBQUUsTUFBTTtJQUN2QixLQUFLLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQztJQUN6QixTQUFTLEVBQUUsQ0FBQyxjQUFjLENBQUM7SUFDM0IsU0FBUyxFQUFFO1FBQ1QsYUFBYSxFQUFFLFNBQVM7S0FDekI7Q0FDRixDQUFBIiwic291cmNlc0NvbnRlbnQiOlsibW9kdWxlLmV4cG9ydHMgPSB7XG4gIHRlc3RFbnZpcm9ubWVudDogXCJub2RlXCIsXG4gIHJvb3RzOiBbXCI8cm9vdERpcj4vdGVzdFwiXSxcbiAgdGVzdE1hdGNoOiBbXCIqKi8qLnRlc3QudHNcIl0sXG4gIHRyYW5zZm9ybToge1xuICAgIFwiXi4rXFxcXC50c3g/JFwiOiBcInRzLWplc3RcIixcbiAgfSxcbn1cbiJdfQ== + testEnvironment: "node", + roots: ["/test"], + testMatch: ["**/*.test.ts"], + transform: { + "^.+\\.tsx?$": "ts-jest", + }, +}; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiamVzdC5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJqZXN0LmNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLE1BQU0sQ0FBQyxPQUFPLEdBQUc7SUFDZixlQUFlLEVBQUUsTUFBTTtJQUN2QixLQUFLLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQztJQUN6QixTQUFTLEVBQUUsQ0FBQyxjQUFjLENBQUM7SUFDM0IsU0FBUyxFQUFFO1FBQ1QsYUFBYSxFQUFFLFNBQVM7S0FDekI7Q0FDRixDQUFBIiwic291cmNlc0NvbnRlbnQiOlsibW9kdWxlLmV4cG9ydHMgPSB7XG4gIHRlc3RFbnZpcm9ubWVudDogXCJub2RlXCIsXG4gIHJvb3RzOiBbXCI8cm9vdERpcj4vdGVzdFwiXSxcbiAgdGVzdE1hdGNoOiBbXCIqKi8qLnRlc3QudHNcIl0sXG4gIHRyYW5zZm9ybToge1xuICAgIFwiXi4rXFxcXC50c3g/JFwiOiBcInRzLWplc3RcIixcbiAgfSxcbn1cbiJdfQ== \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index a98c7797a62..800bd086f88 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -25,7 +25,7 @@ import { EmailIdentity } from "aws-cdk-lib/aws-ses" import { StringParameter } from "aws-cdk-lib/aws-ssm" import { Construct } from "constructs" -import { DoorwayService } from "./doorway_services" +import { DoorwayService } from "./doorway_service" import { DoorwayServiceProps } from "./doorway-service-props" export class DoorwayApiService extends DoorwayService { diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index bccceaa9f29..594e08b2c91 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -3,7 +3,7 @@ import { Construct } from "constructs" import dotenv from "dotenv" import { DoorwayApiService } from "./doorway-api-service" -import { DoorwayPublicService } from "./doorway-public-service" +import { DoorwayPublicSite } from "./doorway-public-site" export class DoorwayAppEnvironmentStack extends Stack { constructor(scope: Construct, id: string) { @@ -14,7 +14,7 @@ export class DoorwayAppEnvironmentStack extends Stack { new DoorwayApiService(this, `doorway-api-service-${environment}`, { environment: environment, }) - new DoorwayPublicService(this, `doorway-public-service-${environment}`, { + new DoorwayPublicSite(this, `doorway-public-service-${environment}`, { environment: environment, }) } diff --git a/cdk/lib/doorway_environment/doorway-public-service-old.ts b/cdk/lib/doorway_environment/doorway-public-service-old.ts deleted file mode 100644 index 059cd76575b..00000000000 --- a/cdk/lib/doorway_environment/doorway-public-service-old.ts +++ /dev/null @@ -1,222 +0,0 @@ -// import { Fn } from "aws-cdk-lib" -// import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager" -// import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" -// import { Repository } from "aws-cdk-lib/aws-ecr" -// import { AwsLogDriver, Cluster, ContainerImage, Secret } from "aws-cdk-lib/aws-ecs" -// import { ApplicationLoadBalancedFargateService } from "aws-cdk-lib/aws-ecs-patterns" -// import { SslPolicy } from "aws-cdk-lib/aws-elasticloadbalancingv2" -// import { ManagedPolicy, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" -// import { LogGroup } from "aws-cdk-lib/aws-logs" -// import { PublicHostedZone } from "aws-cdk-lib/aws-route53" -// import * as secret from "aws-cdk-lib/aws-secretsmanager" -// import { StringParameter } from "aws-cdk-lib/aws-ssm" -// import { Construct } from "constructs" - -// export class DoorwayPublicService { -// public service: ApplicationLoadBalancedFargateService -// constructor(scope: Construct, id: string, props: DoorwayStackProps) { -// // Create the execution role for the doorway API service -// const executionRole = new Role(scope, "executionRole", { -// assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), -// }) -// executionRole.addManagedPolicy( -// ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy") -// ) -// executionRole.addManagedPolicy( -// ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess") -// ) - -// executionRole.addManagedPolicy( -// ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly") -// ) -// const hostedZone = PublicHostedZone.fromHostedZoneAttributes( -// scope, -// `doorway-public-hosted-zone-${props.environment}`, -// { -// hostedZoneId: StringParameter.valueForStringParameter(scope, `/doorway/public-hosted-zone`), -// zoneName: `housingbayarea.mtc.ca.gov`, -// } -// ) -// const cert = new Certificate(scope, "doorwayPublicCert", { -// domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, -// validation: { -// method: CertificateValidation.fromDns().method, -// props: { -// hostedZone: hostedZone, -// }, -// }, -// }) - -// new ApplicationLoadBalancedFargateService( -// scope, -// `doorway-public-portal-service-${props.environment}`, -// { -// cluster: Cluster.fromClusterAttributes(scope, `doorway-ecs-cluster-${props.environment}`, { -// clusterName: `doorway-ecs-cluster-${props.environment}`, -// vpc: Vpc.fromVpcAttributes(scope, "vpc", { -// vpcId: Fn.importValue(`doorway-vpc-id-${props.environment}`), -// availabilityZones: Fn.importValue(`doorway-azs-${props.environment}`).split(","), -// publicSubnetIds: [ -// Fn.importValue(`doorway-public-subnet-1-${props.environment}`), -// Fn.importValue(`doorway-public-subnet-2-${props.environment}`), -// ], -// }), -// securityGroups: [ -// SecurityGroup.fromSecurityGroupId( -// scope, -// `doorway-app-sg-${props.environment}`, -// Fn.importValue(`doorway-app-sg-${props.environment}`) -// ), -// ], -// }), -// taskSubnets: { -// subnets: [ -// Subnet.fromSubnetId( -// scope, -// `doorway-app-subnet-1-${props.environment}`, -// Fn.importValue(`doorway-app-subnet-1-${props.environment}`) -// ), -// Subnet.fromSubnetId( -// scope, -// `doorway-app-subnet-2-${props.environment}`, -// Fn.importValue(`doorway-app-subnet-2-${props.environment}`) -// ), -// ], -// }, - -// assignPublicIp: true, -// publicLoadBalancer: true, -// taskImageOptions: { -// image: ContainerImage.fromEcrRepository( -// Repository.fromRepositoryName( -// scope, -// `doorway-public-portal-repo-${props.environment}`, -// `doorway/public` -// ), -// "run-f21478f5" -// ), -// enableLogging: true, -// containerPort: 3000, -// executionRole: executionRole, -// taskRole: executionRole, -// secrets: { -// BLOOM_API_BASE: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "BLOOM_API_BASE", -// `/doorway/${props.environment}/public-portal/BLOOM_API_BASE` -// ) -// ), -// CACHE_REVALIDATE: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "CACHE_REVALIDATE", -// `/doorway/${props.environment}/public-portal/CACHE_REVALIDATE` -// ) -// ), -// GOOGLE_API_KEY: Secret.fromSecretsManager( -// secret.Secret.fromSecretNameV2(scope, "googleApiKey", "/doorway/GOOGLE_API_KEY") -// ), -// GOOGLE_MAPS_API_KEY: Secret.fromSecretsManager( -// secret.Secret.fromSecretNameV2( -// scope, -// "googleMapsApiKey", -// "/doorway/GOOGLE_MAPS_API_KEY" -// ) -// ), -// GOOGLE_MAPS_MAP_ID: Secret.fromSecretsManager( -// secret.Secret.fromSecretNameV2( -// scope, -// "googleMapsMapId", -// "/doorway/GOOGLE_MAPS_MAP_ID" -// ) -// ), -// GTM_KEY: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "GTM_KEY", -// `/doorway/${props.environment}/public-portal/GTM_KEY` -// ) -// ), -// IDLE_TIMEOUT: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "IDLE_TIMEOUT", -// `/doorway/${props.environment}/public-portal/IDLE_TIMEOUT` -// ) -// ), -// JURISDICTION_NAME: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "JURISDICTION_NAME", -// `/doorway/${props.environment}/public-portal/JURISDICTION_NAME` -// ) -// ), -// LANGUAGES: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "LANGUAGES", -// `/doorway/${props.environment}/public-portal/LANGUAGES` -// ) -// ), -// LISTINGS_QUERY: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "LISTINGS_QUERY", -// `/doorway/${props.environment}/public-portal/LISTINGS_QUERY` -// ) -// ), -// NEXTJS_PORT: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "NEXTJS_PORT", -// `/doorway/${props.environment}/public-portal/NEXTJS_PORT` -// ) -// ), -// NOTIFICATIONS_SIGN_UP_URL: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "NOTIFICATIONS_SIGN_UP_URL", -// `/doorway/${props.environment}/public-portal/NOTIFICATIONS_SIGN_UP_URL` -// ) -// ), -// SHOW_ALL_MAP_PINS: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "SHOW_ALL_MAP_PINS", -// `/doorway/${props.environment}/public-portal/SHOW_ALL_MAP_PINS` -// ) -// ), -// SHOW_ALL_PROFESSIONAL_PARTNERS: Secret.fromSsmParameter( -// StringParameter.fromStringParameterName( -// scope, -// "SHOW_PROFESSIONAL_PARTNERS", -// `/doorway/${props.environment}/public-portal/SHOW_PROFESSIONAL_PARTNERS` -// ) -// ), -// }, -// environment: { -// BACKEND_API_BASE: `http://backend.${props.environment}.housingbayarea.int`, -// }, -// logDriver: AwsLogDriver.awsLogs({ -// streamPrefix: "doorway-public-portal", -// logGroup: new LogGroup(scope, `doorway-${props.environment}-tasks`, { -// logGroupName: `/doorway/${props.environment}/ecs/doorway-public-portal`, -// }), -// }), -// }, - -// certificate: cert, -// listenerPort: 443, -// redirectHTTP: true, -// sslPolicy: SslPolicy.RECOMMENDED, -// serviceName: `doorway-public-portal-service-${props.environment}`, -// loadBalancerName: `doorway-public-lb-${props.environment}`, -// cpu: 1024, -// memoryLimitMiB: 2048, -// domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, -// domainZone: hostedZone, -// } -// ) -// } -// } diff --git a/cdk/lib/doorway_environment/doorway-public-service.ts b/cdk/lib/doorway_environment/doorway-public-service.ts deleted file mode 100644 index 3ea5eef7081..00000000000 --- a/cdk/lib/doorway_environment/doorway-public-service.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Construct } from "constructs" - -import { DoorwayService } from "./doorway_services" -import { DoorwayServiceProps } from "./doorway-service-props" - -export class DoorwayPublicService extends DoorwayService { - constructor(scope: Construct, id: string, props: DoorwayServiceProps) { - super(scope, id, props) - } -} diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts index 22964b8f3d0..662c4d88d35 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -11,8 +11,8 @@ import * as secret from "aws-cdk-lib/aws-secretsmanager" import { StringParameter } from "aws-cdk-lib/aws-ssm" import { Construct } from "constructs" +import { DoorwayService } from "./doorway_service" import { DoorwayServiceProps } from "./doorway-service-props" -import { DoorwayService } from "./doorway_services" export class DoorwayPublicSite extends DoorwayService { constructor(scope: Construct, id: string, props: DoorwayServiceProps) { diff --git a/cdk/lib/doorway_environment/doorway_service.ts b/cdk/lib/doorway_environment/doorway_service.ts index 51ef8796212..f20898fa13e 100644 --- a/cdk/lib/doorway_environment/doorway_service.ts +++ b/cdk/lib/doorway_environment/doorway_service.ts @@ -21,7 +21,7 @@ export abstract class DoorwayService { constructor(scope: Construct, id: string, props: DoorwayServiceProps) { // Initial Execution Role Setup // Create the execution role for the doorway API service - this.executionRole = new Role(scope, "executionRole", { + this.executionRole = new Role(scope, `executionRole-${id}`, { assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), }) this.executionRole.addManagedPolicy( @@ -39,7 +39,7 @@ export abstract class DoorwayService { // Get Network setup const vpcId = cdk.Fn.importValue(`doorway-vpc-id-${props.environment}`) const azs = cdk.Fn.importValue(`doorway-azs-${props.environment}`).split(",") - this.vpc = cdk.aws_ec2.Vpc.fromVpcAttributes(scope, "vpc", { + this.vpc = cdk.aws_ec2.Vpc.fromVpcAttributes(scope, `vpc-${id}`, { vpcId: vpcId, availabilityZones: azs, }) @@ -53,19 +53,19 @@ export abstract class DoorwayService { const appTierPrivateSGId = cdk.Fn.importValue(`doorway-app-sg-${props.environment}`) this.privateSG = cdk.aws_ec2.SecurityGroup.fromSecurityGroupId( scope, - "appTierPrivateSG", + `appTierPrivateSG-${id}`, appTierPrivateSGId, ) //Uploads Buckets this.publicUploads = Bucket.fromBucketArn( scope, - `publicUploadsBucket`, + `publicUploadsBucket-${id}`, Fn.importValue(`doorway-public-uploads-${props.environment}`), ) this.secureUploads = Bucket.fromBucketArn( scope, - `secureUploadsBucket`, + `secureUploadsBucket-${id}`, Fn.importValue(`doorway-secure-uploads-${props.environment}`), ) } diff --git a/cdk/package-lock.json b/cdk/package-lock.json deleted file mode 100644 index 230efff4c51..00000000000 --- a/cdk/package-lock.json +++ /dev/null @@ -1,4422 +0,0 @@ -{ - "name": "cdk", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "cdk", - "version": "0.1.0", - "dependencies": { - "aws-cdk-lib": "2.208.0", - "constructs": "^10.0.0" - }, - "bin": { - "cdk": "bin/cdk.js" - }, - "devDependencies": { - "@types/jest": "^29.5.14", - "@types/node": "22.7.9", - "aws-cdk": "2.1024.0", - "jest": "^29.7.0", - "ts-jest": "^29.2.5", - "ts-node": "^10.9.2", - "typescript": "~5.6.3" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.242", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.242.tgz", - "integrity": "sha512-4c1bAy2ISzcdKXYS1k4HYZsNrgiwbiDzj36ybwFVxEWZXVAP0dimQTCaB9fxu7sWzEjw3d+eaw6Fon+QTfTIpQ==", - "license": "Apache-2.0" - }, - "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.1.0.tgz", - "integrity": "sha512-7bY3J8GCVxLupn/kNmpPc5VJz8grx+4RKfnnJiO1LG+uxkZfANZG3RMHhE+qQxxwkyQ9/MfPtTpf748UhR425A==", - "license": "Apache-2.0" - }, - "node_modules/@aws-cdk/cloud-assembly-schema": { - "version": "45.2.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-45.2.0.tgz", - "integrity": "sha512-5TTUkGHQ+nfuUGwKA8/Yraxb+JdNUh4np24qk/VHXmrCMq+M6HfmGWfhcg/QlHA2S5P3YIamfYHdQAB4uSNLAg==", - "bundleDependencies": [ - "jsonschema", - "semver" - ], - "license": "Apache-2.0", - "dependencies": { - "jsonschema": "~1.4.1", - "semver": "^7.7.2" - }, - "engines": { - "node": ">= 18.0.0" - } - }, - "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": { - "version": "1.4.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/semver": { - "version": "7.7.2", - "inBundle": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", - "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.3", - "@babel/parser": "^7.28.3", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", - "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", - "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", - "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.3", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/node": { - "version": "22.7.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.9.tgz", - "integrity": "sha512-jrTfRC7FM6nChvU7X2KqcrgquofrWLFDeYC1hKfwNWomVvrn7JIksqf344WN2X/y8xrgqBd2dJATZV4GbatBfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/aws-cdk": { - "version": "2.1024.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1024.0.tgz", - "integrity": "sha512-hY0iVT2gPX/QOQXL7RSP2sqIRI/4BYU27vSmbhZxLEj//c3pkMkd9QpIHj7gOhyWC2gf6n5JuYPw27Dgw8FEdA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "cdk": "bin/cdk" - }, - "engines": { - "node": ">= 18.0.0" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/aws-cdk-lib": { - "version": "2.208.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.208.0.tgz", - "integrity": "sha512-lZW475enKz36A/hZvS7xUwfjeqLqxKgBmZ2cGA7BB5PAlQsjBfN70ZGDgGl0egug5fb/hsYy1Dy4OrclfNzxFg==", - "bundleDependencies": [ - "@balena/dockerignore", - "case", - "fs-extra", - "ignore", - "jsonschema", - "minimatch", - "punycode", - "semver", - "table", - "yaml", - "mime-types" - ], - "license": "Apache-2.0", - "dependencies": { - "@aws-cdk/asset-awscli-v1": "2.2.242", - "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", - "@aws-cdk/cloud-assembly-schema": "^45.2.0", - "@balena/dockerignore": "^1.0.2", - "case": "1.6.3", - "fs-extra": "^11.3.0", - "ignore": "^5.3.2", - "jsonschema": "^1.5.0", - "mime-types": "^2.1.35", - "minimatch": "^3.1.2", - "punycode": "^2.3.1", - "semver": "^7.7.2", - "table": "^6.9.0", - "yaml": "1.10.2" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "constructs": "^10.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { - "version": "1.0.2", - "inBundle": true, - "license": "Apache-2.0" - }, - "node_modules/aws-cdk-lib/node_modules/ajv": { - "version": "8.17.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/aws-cdk-lib/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/ansi-styles": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/aws-cdk-lib/node_modules/astral-regex": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/balanced-match": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/brace-expansion": { - "version": "1.1.12", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/aws-cdk-lib/node_modules/case": { - "version": "1.6.3", - "inBundle": true, - "license": "(MIT OR GPL-3.0-or-later)", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/color-convert": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/color-name": { - "version": "1.1.4", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/concat-map": { - "version": "0.0.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/emoji-regex": { - "version": "8.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { - "version": "3.1.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/fast-uri": { - "version": "3.0.6", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "inBundle": true, - "license": "BSD-3-Clause" - }, - "node_modules/aws-cdk-lib/node_modules/fs-extra": { - "version": "11.3.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/aws-cdk-lib/node_modules/graceful-fs": { - "version": "4.2.11", - "inBundle": true, - "license": "ISC" - }, - "node_modules/aws-cdk-lib/node_modules/ignore": { - "version": "5.3.2", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/jsonfile": { - "version": "6.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/aws-cdk-lib/node_modules/jsonschema": { - "version": "1.5.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { - "version": "4.4.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/mime-db": { - "version": "1.52.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/aws-cdk-lib/node_modules/mime-types": { - "version": "2.1.35", - "inBundle": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/aws-cdk-lib/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/aws-cdk-lib/node_modules/punycode": { - "version": "2.3.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/aws-cdk-lib/node_modules/require-from-string": { - "version": "2.0.2", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/semver": { - "version": "7.7.2", - "inBundle": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/aws-cdk-lib/node_modules/slice-ansi": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/aws-cdk-lib/node_modules/string-width": { - "version": "4.2.3", - "inBundle": true, - "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/aws-cdk-lib/node_modules/strip-ansi": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/table": { - "version": "6.9.0", - "inBundle": true, - "license": "BSD-3-Clause", - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/universalify": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/yaml": { - "version": "1.10.2", - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "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/browserslist": { - "version": "4.25.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz", - "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001733", - "electron-to-chromium": "^1.5.199", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "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/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001735", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", - "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "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/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/constructs": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.4.2.tgz", - "integrity": "sha512-wsNxBlAott2qg8Zv87q3eYZYgheb9lchtBfjHzzLHtXbttwSrHPs1NNQbBrmbb1YZvYg2+Vh0Dor76w4mFxJkA==", - "license": "Apache-2.0" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "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.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.202", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.202.tgz", - "integrity": "sha512-NxbYjRmiHcHXV1Ws3fWUW+SLb62isauajk45LUJ/HgIOkUA7jLZu/X2Iif+X9FBNK8QkF9Zb4Q2mcwXCcY30mg==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "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": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "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/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "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/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "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/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "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/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "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/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "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-jest": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.1.tgz", - "integrity": "sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.8", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.2", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "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/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "license": "MIT" - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "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/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} From c95e9a5a916eca9c4c602b6979db23e4bef5b22d Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 21 Aug 2025 13:39:22 -0700 Subject: [PATCH 041/217] feat: reconfiguring services --- cdk/dev2.env | 34 ++- .../doorway-api-service.ts | 218 ++++-------------- .../doorway-app-environment-stack.ts | 16 +- .../doorway-public-site.ts | 168 ++++---------- .../doorway-service-props.ts | 27 ++- .../doorway_environment/doorway_service.ts | 183 ++++++++++++--- 6 files changed, 307 insertions(+), 339 deletions(-) diff --git a/cdk/dev2.env b/cdk/dev2.env index bf626f06792..f1701462cce 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -1,23 +1,35 @@ MAINTENANCE_WINDOW=2024-03-26 18:00 -08:00,2024-04-25 18:00 -08:00 -DEST_REPO_NAMESPACE=doorway-dev +DEST_REPO_NAMESPACE=doorway CACHE_REVALIDATE=30 LANGUAGES=en,es,zh,vi,tl FEATURE_LISTINGS_APPROVAL=TRUE -BACKEND_API_BASE=http://backend.dev.housingbayarea.int + ECR_ACCOUNT_ID=364076391763 +ECR_NAMESPACE=doorway +ECR_REGION=us-west-2 + MAPBOX_TOKEN=pk.eyJ1Ijoic2Vhbi1hbGJlcnQtZXh5Z3kiLCJhIjoiY2tybnY5amlyMDNraDJubXc4c3ZhOGswYyJ9.KE5FGb_Tch8BPrOYMvshXw USE_SECURE_DOWNLOAD_PATHWAY=TRUE GTM_KEY=GTM-KF22FJP -ECR_REGION=us-west-2 SOURCE_REPO_NAMESPACE=doorway -ECR_NAMESPACE=doorway -API_PASS_KEY=dev-key IDLE_TIMEOUT=5 CLOUDINARY_CLOUD_NAME=exygy -API_LOCAL_PORT=3100 -PUBLIC_LOCAL_PORT=3000 -PARTNERS_LOCAL_PORT=3200 -PUBLIC_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY -PARTNERS_SECRETS= +APP_SECRET="" + +PUBLIC_PORTAL_PORT=3000 +PUBLIC_PORTAL_DOMAIN=dev2.housingbayarea.mtc.ca.gov +PUBLIC_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY + + +PARTNERS_PORTAL_PORT=3200 +PARTNERS_PORTAL_DOMAIN=dev2.housingbayarea.mtc.ca.gov +PARTNERS_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY + +BACKEND_API_BASE=http://backend.dev2.housingbayarea.int +BACKEND_API_PORT=3100 +BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVEDELIVERY_API_URL, +BACKEND_API_MEMORY=4096 +BACKEND_API_CPUS=2 +BACKEND_API_INSTANCES=3 +API_PASS_KEY=dev-key -BACKEND_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVEDELIVERY_API_URL, diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index 800bd086f88..00c40be7617 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -1,47 +1,42 @@ -import { Aws, aws_logs, Duration, Fn, RemovalPolicy } from "aws-cdk-lib" -import { - Cluster, - Compatibility, - ContainerImage, - FargateService, - LogDrivers, - NetworkMode, - Protocol, - Secret, - TaskDefinition, -} from "aws-cdk-lib/aws-ecs" -import { - CompositePrincipal, - ManagedPolicy, - PolicyDocument, - PolicyStatement, - Role, - ServicePrincipal, -} from "aws-cdk-lib/aws-iam" -import { LogGroup } from "aws-cdk-lib/aws-logs" +import { Aws, Fn } from "aws-cdk-lib" +import { FargateService, Secret } from "aws-cdk-lib/aws-ecs" +import { PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" -import { PrivateDnsNamespace } from "aws-cdk-lib/aws-servicediscovery" import { EmailIdentity } from "aws-cdk-lib/aws-ses" -import { StringParameter } from "aws-cdk-lib/aws-ssm" import { Construct } from "constructs" +import { DoorwayProps } from "./doorway-service-props" import { DoorwayService } from "./doorway_service" -import { DoorwayServiceProps } from "./doorway-service-props" -export class DoorwayApiService extends DoorwayService { - public constructor(scope: Construct, id: string, props: DoorwayServiceProps) { - super(scope, id, props) +export class DoorwayApiService { + public service: FargateService + public constructor(scope: Construct, id: string, props: DoorwayProps) { + const executionRole = new Role(scope, `executionRole-${id}`, { + assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), + }) + const publicUploads = Bucket.fromBucketArn( + scope, + `publicUploadsBucket-${id}`, + Fn.importValue(`doorway-public-uploads-${props.environment}`), + ) + const secureUploads = Bucket.fromBucketArn( + scope, + `secureUploadsBucket-${id}`, + Fn.importValue(`doorway-secure-uploads-${props.environment}`), + ) + const environmentVariables = { ASSET_FILE_SERVICE: process.env.ASSET_FILE_SERVICE || "s3", LISTINGS_PROCESSING_QUERY: process.env.LISTINGS_PROCESSING_QUERY || "/listings", - PORT: process.env.API_LOCAL_PORT || "3100", + PORT: process.env.BACKEND_API_PORT || "3100", SHOW_LM_LINKS: process.env.SHOW_LM_LINKS || "TRUE", TIME_ZONE: process.env.TIME_ZONE || "America/Los_Angeles", SHOW_DUPLICATES: process.env.SHOW_DUPLICATES || "FALSE", NO_COLOR: process.env.NO_COLOR || "TRUE", ASSET_FS_CONFIG_s3_PATH_PREFIX: process.env.ASSET_FS_CONFIG_s3_PATH_PREFIX || "", - ASSET_FS_PRIVATE_CONFIG_s3_BUCKET: this.secureUploads.bucketName, - ASSET_FS_CONFIG_s3_BUCKET: this.publicUploads.bucketName, + ASSET_FS_PRIVATE_CONFIG_s3_BUCKET: secureUploads.bucketName, + ASSET_FS_CONFIG_s3_BUCKET: publicUploads.bucketName, ASSET_FS_CONFIG_s3_REGION: Aws.REGION, ASSET_FS_CONFIG_s3_URL_FORMAT: process.env.ASSET_FS_CONFIG_s3_URL_FORMAT || "public", THROTTLE_LIMIT: process.env.THROTTLE_LIMIT || "100", @@ -75,11 +70,12 @@ export class DoorwayApiService extends DoorwayService { DUPLICATES_CLOSE_DATE: process.env.DUPLICATES_CLOSE_DATE || "2024-10-08 00:00 -08:00", HTTPS_OFF: process.env.HTTPS_OFF || "TRUE", SAME_SITE: process.env.SAME_SITE || "false", - API_KEY: process.env.API_KEY || "doorway-api-key", + API_PASS_KEY: process.env.API_PASS_KEY || "doorway-api-key", + APP_SECRET: process.env.APP_SECRET || "" } const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) const dbSecret = secret.Secret.fromSecretCompleteArn(scope, "dbSecret", dbSecretArn) - dbSecret.grantRead(this.executionRole) + dbSecret.grantRead(executionRole) const secrets: { [key: string]: Secret } = { PGUSER: Secret.fromSecretsManager(dbSecret, "username"), PGPASSWORD: Secret.fromSecretsManager(dbSecret, "password"), @@ -87,24 +83,25 @@ export class DoorwayApiService extends DoorwayService { PGHOST: Secret.fromSecretsManager(dbSecret, "host"), PGDATABASE: Secret.fromSecretsManager(dbSecret, "dbname"), } - process.env.BACKEND_SECRETS || + process.env.BACKEND_API_SECRETS || "".split(",").forEach((secretName) => { secrets[secretName] = Secret.fromSecretsManager( secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${secretName}`), ) }) + // Grant write access to the uploads buckets. - this.publicUploads.grantReadWrite(this.executionRole) - this.publicUploads.grantPut(this.executionRole) - this.secureUploads.grantReadWrite(this.executionRole) - this.secureUploads.grantPut(this.executionRole) + publicUploads.grantReadWrite(executionRole) + publicUploads.grantPut(executionRole) + secureUploads.grantReadWrite(executionRole) + secureUploads.grantPut(executionRole) // Get the SES Email Information const sesIdentity = EmailIdentity.fromEmailIdentityName( scope, "sesIdentity", "housingbayarea2.org", ) - sesIdentity.grantSendEmail(this.executionRole) + sesIdentity.grantSendEmail(executionRole) // Add a bunch of random SES grants that grantSendEmail doesn't set up const policy = new PolicyStatement({ actions: [ @@ -122,135 +119,22 @@ export class DoorwayApiService extends DoorwayService { `arn:aws:ses:${Aws.REGION}:${Aws.ACCOUNT_ID}:identity/*`, ], }) - this.executionRole.addToPolicy(policy) - - const logGroup = new LogGroup(scope, `doorway-${props.environment}-tasks`, { - logGroupName: `doorway-${props.environment}-tasks`, - removalPolicy: RemovalPolicy.DESTROY, - retention: aws_logs.RetentionDays.ONE_WEEK, - }) - logGroup.grantWrite(this.executionRole) - // Now we're down to business! Set up the fargate container task for the api - // Notice the load of environment variables we add to it below - const task = new TaskDefinition(scope, "apiTask", { - compatibility: Compatibility.FARGATE, - cpu: process.env.API_CPUS || "2048", - memoryMiB: process.env.API_MEMORY || "4096", - executionRole: this.executionRole, - taskRole: this.executionRole, - networkMode: NetworkMode.AWS_VPC, - }) - - task.addContainer("internal-api", { - image: ContainerImage.fromRegistry( - `${Aws.ACCOUNT_ID}.dkr.ecr.${Aws.REGION}.amazonaws.com/doorway/backend:run-candidate`, - ), - cpu: 1, - memoryLimitMiB: 1024, - essential: true, - logging: LogDrivers.awsLogs({ - streamPrefix: "internal-api", - logGroup: LogGroup.fromLogGroupName( - scope, - "logGroup", - `doorway-${props.environment}-tasks`, - ), - }), + executionRole.addToPolicy(policy) + this.service = new DoorwayService(scope, `doorway-api-service-${props.environment}`, { + memory: Number(process.env.BACKEND_MEMORY || 4096), + cpu: Number(process.env.BACKEND_CPU || 2), + instances: Number(process.env.BACKEND_INSTANCES || 3), + port: Number(process.env.BACKEND_API_PORT || 3100), secrets: secrets, - environment: environmentVariables, - entryPoint: [], - portMappings: [ - { - name: "internal-api-port", - containerPort: Number(process.env.API_LOCAL_PORT) || 3100, - protocol: Protocol.TCP, - hostPort: Number(process.env.API_LOCAL_PORT) || 3100, - }, - ], - }) - //Setting up ServiceConnect which will allow the partenr and public services - // to connect to the internal api service directly through ECS - // without a load balancer. - const namespace = new PrivateDnsNamespace( - scope, - `doorway-${props.environment}-internal-api-namespace`, - { - vpc: this.vpc, - name: `doorway-${props.environment}-internal-api`, - description: `Private DNS namespace for the Doorway ${props.environment} internal API`, - }, - ) - // The private CA is used for TLS encryption of ServiceConnect traffic. - const privateCAArn = StringParameter.fromStringParameterAttributes(scope, "privateCAArn", { - parameterName: `/doorway/privateCertAuthority`, - }).stringValue - const scRole = new Role(scope, `doorway-${props.environment}-internal-api-sc-role`, { - assumedBy: new CompositePrincipal( - new ServicePrincipal("ecs.amazonaws.com"), - new ServicePrincipal("ecs-tasks.amazonaws.com"), - ), - managedPolicies: [ - ManagedPolicy.fromAwsManagedPolicyName( - "service-role/AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity", - ), - ], - inlinePolicies: { - pcaAuth: new PolicyDocument({ - statements: [ - new PolicyStatement({ - actions: ["acm-pca:*"], - resources: [privateCAArn], - }), - ], - }), - }, - }) - - // Create the service in ECS - const service = new FargateService(scope, `doorway-${props.environment}-internal-api`, { - taskDefinition: task, - serviceName: `doorway-${props.environment}-internal-api`, - cluster: Cluster.fromClusterAttributes(scope, "ecsCluster", { - clusterName: Fn.importValue(`doorway-ecs-cluster-${props.environment}`), - vpc: this.vpc, - }), - vpcSubnets: { - subnets: this.appsubnets, - }, - securityGroups: [this.privateSG], - - desiredCount: Number(process.env.API_INSTANCES) || 3, - serviceConnectConfiguration: { - namespace: namespace.namespaceArn, - logDriver: LogDrivers.awsLogs({ - streamPrefix: "internal-api-service-connect", - logGroup: logGroup, - }), - services: [ - { - portMappingName: "internal-api-port", - dnsName: `backend.${props.environment}.housingbayarea.int`, - - tls: { - awsPcaAuthorityArn: privateCAArn, - role: scRole, - }, - }, - ], - }, - }) - - // Ensure the namespace is created before the service - service.node.addDependency(namespace) - - const scaling = service.autoScaleTaskCount({ - minCapacity: Number(process.env.API_INSTANCES) || 3, - maxCapacity: Number(process.env.API_MAX_INSTANCES) || 10, - }) - scaling.scaleOnCpuUtilization("CpuScaling", { - targetUtilizationPercent: Number(process.env.API_AUTOSCALE_CPU) || 80, - scaleInCooldown: Duration.seconds(60), - scaleOutCooldown: Duration.seconds(60), - }) + environmentVariables: environmentVariables, + serviceConnectServer: true, + domainName: `backend.${props.environment}.housingbayarea.int`, + executionRole: executionRole, + publicUploads: publicUploads, + secureUploads: secureUploads, + environment: props.environment, + logGroup: props.logGroup, + container: "doorway/backend:run-candidate" + }).service } } diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 594e08b2c91..971adba5816 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -1,4 +1,5 @@ -import { Stack } from "aws-cdk-lib" +import { aws_logs, RemovalPolicy, Stack } from "aws-cdk-lib" +import { LogGroup } from "aws-cdk-lib/aws-logs" import { Construct } from "constructs" import dotenv from "dotenv" @@ -10,12 +11,21 @@ export class DoorwayAppEnvironmentStack extends Stack { super(scope, id) const environment = process.env.ENVIRONMENT || "dev2" dotenv.config({ path: `${environment}.env` }) + const logGroup = new LogGroup(this, `doorway-${environment}-tasks`, { + logGroupName: `doorway-${environment}-tasks`, + removalPolicy: RemovalPolicy.DESTROY, + retention: aws_logs.RetentionDays.ONE_WEEK, + }) - new DoorwayApiService(this, `doorway-api-service-${environment}`, { + const api = new DoorwayApiService(this, `doorway-api-service-${environment}`, { environment: environment, + logGroup: logGroup, + }) - new DoorwayPublicSite(this, `doorway-public-service-${environment}`, { + const publicSite = new DoorwayPublicSite(this, `doorway-public-service-${environment}`, { environment: environment, + logGroup: logGroup }) + publicSite.service.node.addDependency(api.service) } } diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts index 662c4d88d35..3d06f1d66b0 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -1,22 +1,29 @@ -import { Fn } from "aws-cdk-lib" -import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager" -import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" -import { Repository } from "aws-cdk-lib/aws-ecr" -import { AwsLogDriver, Cluster, ContainerImage, Secret } from "aws-cdk-lib/aws-ecs" -import { ApplicationLoadBalancedFargateService } from "aws-cdk-lib/aws-ecs-patterns" -import { SslPolicy } from "aws-cdk-lib/aws-elasticloadbalancingv2" -import { LogGroup } from "aws-cdk-lib/aws-logs" -import { PublicHostedZone } from "aws-cdk-lib/aws-route53" +import { FargateService, Secret } from "aws-cdk-lib/aws-ecs" import * as secret from "aws-cdk-lib/aws-secretsmanager" -import { StringParameter } from "aws-cdk-lib/aws-ssm" import { Construct } from "constructs" +import { Fn } from "aws-cdk-lib" +import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import { Bucket } from "aws-cdk-lib/aws-s3" +import { DoorwayProps } from "./doorway-service-props" import { DoorwayService } from "./doorway_service" -import { DoorwayServiceProps } from "./doorway-service-props" -export class DoorwayPublicSite extends DoorwayService { - constructor(scope: Construct, id: string, props: DoorwayServiceProps) { - super(scope, id, props) +export class DoorwayPublicSite { + public service: FargateService + constructor(scope: Construct, id: string, props: DoorwayProps) { + const executionRole = new Role(scope, `executionRole-${id}`, { + assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), + }) + const publicUploads = Bucket.fromBucketArn( + scope, + `publicUploadsBucket-${id}`, + Fn.importValue(`doorway-public-uploads-${props.environment}`), + ) + const secureUploads = Bucket.fromBucketArn( + scope, + `secureUploadsBucket-${id}`, + Fn.importValue(`doorway-secure-uploads-${props.environment}`), + ) const environmentVariables: { [key: string]: string } = { BACKEND_API_BASE: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, @@ -27,7 +34,7 @@ export class DoorwayPublicSite extends DoorwayService { JURISDICTION_NAME: process.env.JURISDICTION_NAME || "Bay Area", LANGUAGES: process.env.LANGUAGES || "en,es,zh,vi,tl", LISTINGS_QUERY: process.env.LISTINGS_QUERY || "/listings", - NEXTJS_PORT: process.env.PUBLIC_PORT || "3000", + NEXTJS_PORT: process.env.PUBLIC_PORTAL_PORT || "3000", NODE_ENV: process.env.NODE_ENV || "development", NOTIFICATIONS_SIGNUP_URL: process.env.NOTIFICATIONS_SIGNUP_URL || @@ -36,122 +43,37 @@ export class DoorwayPublicSite extends DoorwayService { SHOW_PROFESSIONAL_PARTNERS: process.env.SHOW_PROFESSIONAL_PARTNERS || "true", } const secrets: { [key: string]: Secret } = {} - process.env.PUBLIC_SITE_SECRETS || + process.env.PUBLIC_PORTAL_SECRETS || "".split(",").forEach((secretName) => { secrets[secretName] = Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${secretName}`), + secret.Secret.fromSecretNameV2(scope, `${secretName}-${id}`, `/doorway/${secretName}`), ) }) + this.service = new DoorwayService(scope, `${id}-service`, { + ...props, + memory: Number(process.env.PUBLIC_PORTAL_MEMORY || 4096), + cpu: Number(process.env.PUBLIC_PORTAL_CPU || 2), + instances: Number(process.env.PUBLIC_PORTAL_INSTANCES || 3), + port: Number(process.env.PUBLIC_PORTAL_PORT || 3000), + secrets: secrets, + environmentVariables: environmentVariables, + serviceConnectServer: false, + domainName: process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`, + executionRole: executionRole, + publicUploads: publicUploads, + secureUploads: secureUploads, + logGroup: props.logGroup, + apiTargetDomainName: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, + apiTargetPort: Number(process.env.BACKEND_API_PORT || 3000), + container: "doorway/public:run-candidate" + + }).service + + - const hostedZone = PublicHostedZone.fromHostedZoneAttributes( - scope, - `doorway-public-hosted-zone-${props.environment}`, - { - hostedZoneId: StringParameter.valueForStringParameter(scope, `/doorway/public-hosted-zone`), - zoneName: `housingbayarea.mtc.ca.gov`, - }, - ) - const cert = new Certificate(scope, "doorwayPublicCert", { - domainName: - process.env.PUBLIC_DOMAIN_NAME || `${props.environment}.housingbayarea.mtc.ca.gov`, - validation: { - method: CertificateValidation.fromDns().method, - props: { - hostedZone: hostedZone, - }, - }, - }) - const publicService = new ApplicationLoadBalancedFargateService( - scope, - `doorway-public-portal-service-${props.environment}`, - { - cluster: Cluster.fromClusterAttributes(scope, `doorway-ecs-cluster-${props.environment}`, { - clusterName: `doorway-ecs-cluster-${props.environment}`, - vpc: Vpc.fromVpcAttributes(scope, "vpc", { - vpcId: Fn.importValue(`doorway-vpc-id-${props.environment}`), - availabilityZones: Fn.importValue(`doorway-azs-${props.environment}`).split(","), - publicSubnetIds: [ - Fn.importValue(`doorway-public-subnet-1-${props.environment}`), - Fn.importValue(`doorway-public-subnet-2-${props.environment}`), - ], - }), - securityGroups: [ - SecurityGroup.fromSecurityGroupId( - scope, - `doorway-app-sg-${props.environment}`, - Fn.importValue(`doorway-app-sg-${props.environment}`), - ), - ], - }), - taskSubnets: { - subnets: [ - Subnet.fromSubnetId( - scope, - `doorway-app-subnet-1-${props.environment}`, - Fn.importValue(`doorway-app-subnet-1-${props.environment}`), - ), - Subnet.fromSubnetId( - scope, - `doorway-app-subnet-2-${props.environment}`, - Fn.importValue(`doorway-app-subnet-2-${props.environment}`), - ), - ], - }, - assignPublicIp: true, - publicLoadBalancer: true, - taskImageOptions: { - image: ContainerImage.fromEcrRepository( - Repository.fromRepositoryName( - scope, - `doorway-public-portal-repo-${props.environment}`, - `doorway/public`, - ), - "run-f21478f5", - ), - enableLogging: true, - containerPort: 3000, - executionRole: this.executionRole, - taskRole: this.executionRole, - secrets: secrets, - environment: environmentVariables, - logDriver: AwsLogDriver.awsLogs({ - streamPrefix: "doorway-public-portal", - logGroup: new LogGroup(scope, `doorway-${props.environment}-tasks`, { - logGroupName: `doorway-${props.environment}-tasks`, - }), - }), - }, - certificate: cert, - listenerPort: 443, - redirectHTTP: true, - sslPolicy: SslPolicy.RECOMMENDED, - serviceName: `doorway-public-portal-service-${props.environment}`, - loadBalancerName: `doorway-public-lb-${props.environment}`, - cpu: 1024, - memoryLimitMiB: 2048, - domainName: `${props.environment}.housingbayarea.mtc.ca.gov`, - domainZone: hostedZone, - }, - ) - // Configure Service Connect on the underlying Fargate service - const cfnService = publicService.service.node.defaultChild as any - cfnService.serviceConnectConfiguration = { - enabled: true, - namespace: `doorway-${props.environment}-internal-api`, - services: [ - { - clientAliases: [ - { - dnsName: `backend.${props.environment}.housingbayarea.int`, - port: 3100, - }, - ], - }, - ], - } } } diff --git a/cdk/lib/doorway_environment/doorway-service-props.ts b/cdk/lib/doorway_environment/doorway-service-props.ts index 3c6dbc1bfe6..54e32855b65 100644 --- a/cdk/lib/doorway_environment/doorway-service-props.ts +++ b/cdk/lib/doorway_environment/doorway-service-props.ts @@ -1,3 +1,26 @@ -export interface DoorwayServiceProps { - environment: string +import { Secret } from "aws-cdk-lib/aws-ecs" +import { Role } from "aws-cdk-lib/aws-iam" +import { LogGroup } from "aws-cdk-lib/aws-logs" +import { IBucket } from "aws-cdk-lib/aws-s3" + +export interface DoorwayProps { + environment: string, + logGroup: LogGroup +} + +export interface DoorwayServiceProps extends DoorwayProps { + memory: number + cpu: number + instances: number + port: number + secrets: { [key: string]: Secret } + environmentVariables: { [key: string]: string } + serviceConnectServer: boolean + domainName: string + apiTargetDomainName?: string + apiTargetPort?: number + executionRole: Role + publicUploads: IBucket + secureUploads: IBucket + container: string } diff --git a/cdk/lib/doorway_environment/doorway_service.ts b/cdk/lib/doorway_environment/doorway_service.ts index f20898fa13e..ab591f90942 100644 --- a/cdk/lib/doorway_environment/doorway_service.ts +++ b/cdk/lib/doorway_environment/doorway_service.ts @@ -1,45 +1,40 @@ import * as cdk from "aws-cdk-lib" import { Fn } from "aws-cdk-lib" -import { ISecurityGroup, ISubnet, IVpc, Subnet } from "aws-cdk-lib/aws-ec2" +import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2" +import { Cluster, Compatibility, ContainerImage, FargateService, LogDrivers, NetworkMode, Protocol, ServiceConnectProps, TaskDefinition } from "aws-cdk-lib/aws-ecs" //import * as ecs from "aws-cdk-lib/aws-ecs"; -import { ManagedPolicy, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" -import { Bucket, IBucket } from "aws-cdk-lib/aws-s3" +import { CompositePrincipal, ManagedPolicy, PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import { PrivateDnsNamespace } from "aws-cdk-lib/aws-servicediscovery" +import { StringParameter } from "aws-cdk-lib/aws-ssm" import { Construct } from "constructs" import { DoorwayServiceProps } from "./doorway-service-props" -export abstract class DoorwayService { - protected executionRole: Role - protected privateSG: ISecurityGroup - protected publicSG: ISecurityGroup - protected vpc: IVpc - protected appsubnets: ISubnet[] = [] - protected publicSubnets: ISubnet[] = [] - protected publicUploads: IBucket - protected secureUploads: IBucket +export class DoorwayService { + + + public service: FargateService constructor(scope: Construct, id: string, props: DoorwayServiceProps) { // Initial Execution Role Setup // Create the execution role for the doorway API service - this.executionRole = new Role(scope, `executionRole-${id}`, { - assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), - }) - this.executionRole.addManagedPolicy( + + props.executionRole.addManagedPolicy( ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy"), ) - this.executionRole.addManagedPolicy( + props.executionRole.addManagedPolicy( ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess"), ) - this.executionRole.addManagedPolicy( + props.executionRole.addManagedPolicy( ManagedPolicy.fromAwsManagedPolicyName("AmazonRDSReadOnlyAccess"), ) - this.executionRole.addManagedPolicy( + props.executionRole.addManagedPolicy( ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly"), ) // Get Network setup const vpcId = cdk.Fn.importValue(`doorway-vpc-id-${props.environment}`) const azs = cdk.Fn.importValue(`doorway-azs-${props.environment}`).split(",") - this.vpc = cdk.aws_ec2.Vpc.fromVpcAttributes(scope, `vpc-${id}`, { + const vpc = cdk.aws_ec2.Vpc.fromVpcAttributes(scope, `vpc-${id}`, { vpcId: vpcId, availabilityZones: azs, }) @@ -47,26 +42,148 @@ export abstract class DoorwayService { const appSubnetIds: string[] = [] appSubnetIds.push(cdk.Fn.importValue(`doorway-app-subnet-1-${props.environment}`)) appSubnetIds.push(cdk.Fn.importValue(`doorway-app-subnet-2-${props.environment}`)) + const appsubnets: ISubnet[] = [] appSubnetIds.forEach((id) => { - this.appsubnets.push(Subnet.fromSubnetId(scope, id, id)) + appsubnets.push(Subnet.fromSubnetId(scope, id, id)) }) const appTierPrivateSGId = cdk.Fn.importValue(`doorway-app-sg-${props.environment}`) - this.privateSG = cdk.aws_ec2.SecurityGroup.fromSecurityGroupId( + const privateSG = cdk.aws_ec2.SecurityGroup.fromSecurityGroupId( scope, `appTierPrivateSG-${id}`, appTierPrivateSGId, ) - //Uploads Buckets - this.publicUploads = Bucket.fromBucketArn( - scope, - `publicUploadsBucket-${id}`, - Fn.importValue(`doorway-public-uploads-${props.environment}`), - ) - this.secureUploads = Bucket.fromBucketArn( - scope, - `secureUploadsBucket-${id}`, - Fn.importValue(`doorway-secure-uploads-${props.environment}`), - ) + + + + const task = new TaskDefinition(scope, `${id}-task`, { + compatibility: Compatibility.FARGATE, + executionRole: props.executionRole, + taskRole: props.executionRole, + networkMode: NetworkMode.AWS_VPC, + cpu: `${props.cpu * 1024}`, + memoryMiB: `${props.memory}` + }) + task.addContainer(`${id}-container`, { + image: ContainerImage.fromRegistry( + `${cdk.Aws.ACCOUNT_ID}.dkr.ecr.${cdk.Aws.REGION}.amazonaws.com/${props.container}`, + ), + cpu: props.cpu, + memoryLimitMiB: props.memory, + essential: true, + logging: LogDrivers.awsLogs({ + streamPrefix: id, + logGroup: props.logGroup, + }), + secrets: props.secrets, + environment: props.environmentVariables, + entryPoint: [], + portMappings: [ + { + name: `${id}-port-mapping`, + containerPort: props.port, + protocol: Protocol.TCP, + hostPort: props.port, + }, + ], + }) + //Setting up ServiceConnect which will allow the partenr and public services + // to connect to the internal api service directly through ECS + // without a load balancer. + let serviceConnectProps: ServiceConnectProps = { + namespace: `doorway-${props.environment}-internal-api`, + + logDriver: LogDrivers.awsLogs({ + logGroup: props.logGroup, + streamPrefix: `${id}-service-connect`, + }), + services: [{ + + portMappingName: `${id}-port-mapping`, + dnsName: props.domainName, + + }] + } + + + if (props.serviceConnectServer) { + + const namespace = new PrivateDnsNamespace( + scope, + `doorway-${props.environment}-internal-api-namespace`, + { + vpc: vpc, + name: `doorway-${props.environment}-internal-api`, + description: `Private DNS namespace for the Doorway ${props.environment} internal API`, + }, + ) + // The private CA is used for TLS encryption of ServiceConnect traffic. + const privateCAArn = StringParameter.fromStringParameterAttributes(scope, "privateCAArn", { + parameterName: `/doorway/privateCertAuthority`, + }).stringValue + const scRole = new Role(scope, `doorway-${props.environment}-internal-api-sc-role`, { + assumedBy: new CompositePrincipal( + new ServicePrincipal("ecs.amazonaws.com"), + new ServicePrincipal("ecs-tasks.amazonaws.com"), + ), + managedPolicies: [ + ManagedPolicy.fromAwsManagedPolicyName( + "service-role/AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity", + ), + ], + inlinePolicies: { + pcaAuth: new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: ["acm-pca:*"], + resources: [privateCAArn], + }), + ], + }), + }, + }) + props.logGroup.grantWrite(scRole) + serviceConnectProps = { + logDriver: LogDrivers.awsLogs({ + logGroup: props.logGroup, + streamPrefix: `${id}-service-connect`, + }), + namespace: namespace.namespaceName, + + services: [{ + tls: { + role: scRole, + awsPcaAuthorityArn: privateCAArn, + + }, + portMappingName: `${id}-port-mapping`, + dnsName: props.domainName, + discoveryName: `doorway-${props.environment}-internal-api`, + port: props.port, + + }], + + + } + } + + this.service = new FargateService(scope, `${id}-fargate-service`, { + taskDefinition: task, + serviceName: `${id}-service`, + cluster: Cluster.fromClusterAttributes(scope, `ecsCluster-${id}`, { + clusterName: Fn.importValue(`doorway-ecs-cluster-${props.environment}`), + vpc: vpc, + }), + vpcSubnets: { + subnets: appsubnets, + }, + securityGroups: [privateSG], + desiredCount: props.instances, + serviceConnectConfiguration: serviceConnectProps, + + }) + props.logGroup.grantWrite(props.executionRole) + } + } From 4ec445317d67c14fdd687859f426c2df3b167bff Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 22 Aug 2025 12:08:15 -0700 Subject: [PATCH 042/217] fix: using git commit hash for container name --- cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 2 +- cdk/lib/doorway_environment/doorway-public-site.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index d0838e9e4a9..4cb438e99b6 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -141,7 +141,7 @@ export class DoorwayBuildPipelineStack extends Stack { }) const dbmigrate = new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev", { environment: "dev2", - buildspec: "./ci/buildspec/migrate_stop_backend.yml", + buildspec: "./ci/buildspec/migrate.yml", source: sourceArtifact, buildRole: buildRole, }).action diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts index 3d06f1d66b0..f60b3883091 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -1,12 +1,12 @@ +import { Fn } from "aws-cdk-lib" import { FargateService, Secret } from "aws-cdk-lib/aws-ecs" +import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" -import { Fn } from "aws-cdk-lib" -import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" -import { Bucket } from "aws-cdk-lib/aws-s3" -import { DoorwayProps } from "./doorway-service-props" import { DoorwayService } from "./doorway_service" +import { DoorwayProps } from "./doorway-service-props" export class DoorwayPublicSite { public service: FargateService @@ -65,7 +65,7 @@ export class DoorwayPublicSite { logGroup: props.logGroup, apiTargetDomainName: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, apiTargetPort: Number(process.env.BACKEND_API_PORT || 3000), - container: "doorway/public:run-candidate" + container: `doorway/public:run-${process.env.CODEBUILD_RESOLVED_SOURCE_VERSION?.substring(0, 8) || "candidate"}` }).service From a1479134479f4489e3c8774bd95dd8bed0604ef0 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 22 Aug 2025 15:11:45 -0700 Subject: [PATCH 043/217] feat: load balancer setup --- .../doorway-api-service.ts | 2 +- .../doorway-app-environment-stack.ts | 8 ++ ...rway-service-props.ts => doorway-props.ts} | 6 +- .../doorway-public-load-balancer.ts | 106 ++++++++++++++++++ .../doorway-public-site.ts | 14 ++- .../doorway_environment/doorway_service.ts | 6 +- 6 files changed, 133 insertions(+), 9 deletions(-) rename cdk/lib/doorway_environment/{doorway-service-props.ts => doorway-props.ts} (76%) create mode 100644 cdk/lib/doorway_environment/doorway-public-load-balancer.ts diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-api-service.ts index 00c40be7617..066ed272849 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-api-service.ts @@ -6,7 +6,7 @@ import * as secret from "aws-cdk-lib/aws-secretsmanager" import { EmailIdentity } from "aws-cdk-lib/aws-ses" import { Construct } from "constructs" -import { DoorwayProps } from "./doorway-service-props" +import { DoorwayProps } from "./doorway-props" import { DoorwayService } from "./doorway_service" export class DoorwayApiService { diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 971adba5816..0aec9fec1a1 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -4,6 +4,7 @@ import { Construct } from "constructs" import dotenv from "dotenv" import { DoorwayApiService } from "./doorway-api-service" +import { DoorwayPublicLoadBalancer } from "./doorway-public-load-balancer" import { DoorwayPublicSite } from "./doorway-public-site" export class DoorwayAppEnvironmentStack extends Stack { @@ -27,5 +28,12 @@ export class DoorwayAppEnvironmentStack extends Stack { logGroup: logGroup }) publicSite.service.node.addDependency(api.service) + const lb = new DoorwayPublicLoadBalancer(this, `doorway-public-lb-${environment}`, { + environment: environment, + publicService: publicSite.service, + partnersService: publicSite.service, + logGroup: logGroup + }).loadBalancer; + lb.node.addDependency(publicSite.service) } } diff --git a/cdk/lib/doorway_environment/doorway-service-props.ts b/cdk/lib/doorway_environment/doorway-props.ts similarity index 76% rename from cdk/lib/doorway_environment/doorway-service-props.ts rename to cdk/lib/doorway_environment/doorway-props.ts index 54e32855b65..52b8378e809 100644 --- a/cdk/lib/doorway_environment/doorway-service-props.ts +++ b/cdk/lib/doorway_environment/doorway-props.ts @@ -1,4 +1,4 @@ -import { Secret } from "aws-cdk-lib/aws-ecs" +import { FargateService, Secret } from "aws-cdk-lib/aws-ecs" import { Role } from "aws-cdk-lib/aws-iam" import { LogGroup } from "aws-cdk-lib/aws-logs" import { IBucket } from "aws-cdk-lib/aws-s3" @@ -24,3 +24,7 @@ export interface DoorwayServiceProps extends DoorwayProps { secureUploads: IBucket container: string } +export interface DoorwayLoadBalancerProps extends DoorwayProps { + publicService: FargateService, + partnersService: FargateService, +} diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts new file mode 100644 index 00000000000..01e4d1a29bb --- /dev/null +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -0,0 +1,106 @@ +import { aws_ec2, Duration, Fn } from "aws-cdk-lib"; +import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; +import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2"; +import { ApplicationListenerRule, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup, ListenerAction, ListenerCondition, TargetType } from "aws-cdk-lib/aws-elasticloadbalancingv2"; +import { ARecord, HostedZone, RecordTarget } from "aws-cdk-lib/aws-route53"; +import { LoadBalancerTarget } from "aws-cdk-lib/aws-route53-targets"; +import { StringParameter } from "aws-cdk-lib/aws-ssm"; +import { Construct } from "constructs"; + +import { DoorwayLoadBalancerProps } from "./doorway-props"; + +export class DoorwayPublicLoadBalancer { + public loadBalancer: ApplicationLoadBalancer + constructor(scope: Construct, id: string, props: DoorwayLoadBalancerProps) { + + // Get Network setup + const vpcId = Fn.importValue(`doorway-vpc-id-${props.environment}`) + const azs = Fn.importValue(`doorway-azs-${props.environment}`).split(",") + const vpc = aws_ec2.Vpc.fromVpcAttributes(scope, `vpc-${id}`, { + vpcId: vpcId, + availabilityZones: azs, + }) + const sgId = Fn.importValue(`doorway-public-sg-${props.environment}`) + + const sg = aws_ec2.SecurityGroup.fromSecurityGroupId(scope, `doorway-public-sg-${props.environment}`, sgId) + const publicSubnetIds: string[] = [] + publicSubnetIds.push(Fn.importValue(`doorway-public-subnet-1-${props.environment}`)) + publicSubnetIds.push(Fn.importValue(`doorway-public-subnet-2-${props.environment}`)) + const publicsubnets: ISubnet[] = [] + publicSubnetIds.forEach((id) => { + publicsubnets.push(Subnet.fromSubnetId(scope, id, id)) + }) + this.loadBalancer = new ApplicationLoadBalancer(scope, id, { + vpc: vpc, + internetFacing: true, + loadBalancerName: id, + securityGroup: sg, + vpcSubnets: { + + subnets: publicsubnets, + }, + + }); + const dnsZone = StringParameter.fromStringParameterName(scope, `publicDnsZoneId-${props.environment}`, `/doorway/public-hosted-zone`).stringValue; + new ARecord(scope, `publicDnsName-${props.environment}`, { + zone: HostedZone.fromHostedZoneAttributes(scope, `publicDnsZone-${props.environment}`, { + hostedZoneId: dnsZone, + zoneName: 'housingbayarea.mtc.ca.gov', + }), + recordName: process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`, + target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer, { + evaluateTargetHealth: true + })) + }) + const httpListener = this.loadBalancer.addListener("HttpListener", { + port: 80, + open: true, + }); + const httpsListener = this.loadBalancer.addListener("HttpsListener", { + port: 443, + open: true, + certificates: [new Certificate(scope, "PublicCertificate", { + domainName: process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`, + validation: CertificateValidation.fromDns() + })] + }); + httpListener.addAction("DefaultAction", { + action: ListenerAction.redirect({ + protocol: "HTTPS", + port: "443", + permanent: true, + + }), + }) + httpsListener.addAction("DefaultAction", { + action: ListenerAction.fixedResponse(200, { + contentType: "text/plain", + messageBody: "OK", + }) + }) + new ApplicationListenerRule(scope, "PublicDomainRule", { + listener: httpsListener, + priority: 1, + action: ListenerAction.forward([new ApplicationTargetGroup(scope, "PublicTargetGroup", { + port: Number(process.env.PUBLIC_PORTAL_PORT) || 3000, + protocol: ApplicationProtocol.HTTP, + vpc: vpc, + targetGroupName: `public-tg-${props.environment}`, + targetType: TargetType.IP, + targets: [props.publicService], + healthCheck: { + enabled: true, + healthyThresholdCount: 2, + interval: Duration.seconds(10), + path: "/", + timeout: Duration.seconds(5), + unhealthyThresholdCount: 2, + }, + })]), + conditions: [ListenerCondition.hostHeaders([process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`]) + ] + + + }); + } +} \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts index f60b3883091..a0ed5b67364 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -1,12 +1,13 @@ import { Fn } from "aws-cdk-lib" import { FargateService, Secret } from "aws-cdk-lib/aws-ecs" +import { ApplicationProtocol, ApplicationTargetGroup } from "aws-cdk-lib/aws-elasticloadbalancingv2" import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" +import { DoorwayProps } from "./doorway-props" import { DoorwayService } from "./doorway_service" -import { DoorwayProps } from "./doorway-service-props" export class DoorwayPublicSite { public service: FargateService @@ -24,6 +25,7 @@ export class DoorwayPublicSite { `secureUploadsBucket-${id}`, Fn.importValue(`doorway-secure-uploads-${props.environment}`), ) + const port = process.env.PUBLIC_PORTAL_PORT || "3000" const environmentVariables: { [key: string]: string } = { BACKEND_API_BASE: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, @@ -34,7 +36,7 @@ export class DoorwayPublicSite { JURISDICTION_NAME: process.env.JURISDICTION_NAME || "Bay Area", LANGUAGES: process.env.LANGUAGES || "en,es,zh,vi,tl", LISTINGS_QUERY: process.env.LISTINGS_QUERY || "/listings", - NEXTJS_PORT: process.env.PUBLIC_PORTAL_PORT || "3000", + NEXTJS_PORT: port, NODE_ENV: process.env.NODE_ENV || "development", NOTIFICATIONS_SIGNUP_URL: process.env.NOTIFICATIONS_SIGNUP_URL || @@ -68,6 +70,14 @@ export class DoorwayPublicSite { container: `doorway/public:run-${process.env.CODEBUILD_RESOLVED_SOURCE_VERSION?.substring(0, 8) || "candidate"}` }).service + const publicTG = new ApplicationTargetGroup(scope, `publicTG-${id}`, { + port: Number(port), + protocol: ApplicationProtocol.HTTP, + vpc: this.service.cluster.vpc, + targets: [this.service], + targetGroupName: `public-${props.environment}`, + }); + diff --git a/cdk/lib/doorway_environment/doorway_service.ts b/cdk/lib/doorway_environment/doorway_service.ts index ab591f90942..a5c74a2a6c7 100644 --- a/cdk/lib/doorway_environment/doorway_service.ts +++ b/cdk/lib/doorway_environment/doorway_service.ts @@ -8,7 +8,7 @@ import { PrivateDnsNamespace } from "aws-cdk-lib/aws-servicediscovery" import { StringParameter } from "aws-cdk-lib/aws-ssm" import { Construct } from "constructs" -import { DoorwayServiceProps } from "./doorway-service-props" +import { DoorwayServiceProps } from "./doorway-props" export class DoorwayService { @@ -52,10 +52,6 @@ export class DoorwayService { `appTierPrivateSG-${id}`, appTierPrivateSGId, ) - - - - const task = new TaskDefinition(scope, `${id}-task`, { compatibility: Compatibility.FARGATE, executionRole: props.executionRole, From 2b2550c5aac341828df7986292c9f32d204f2cce Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sat, 23 Aug 2025 10:03:02 -0700 Subject: [PATCH 044/217] feat: more load balancer enhancements --- .../doorway-app-environment-stack.ts | 17 ++-- .../doorway-public-load-balancer.ts | 81 ++++++++++--------- .../doorway-public-site.ts | 9 +-- 3 files changed, 53 insertions(+), 54 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 0aec9fec1a1..a900919df98 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -23,17 +23,20 @@ export class DoorwayAppEnvironmentStack extends Stack { logGroup: logGroup, }) + const lb = new DoorwayPublicLoadBalancer(this, `doorway-public-lb-${environment}`, { + environment: environment, + + logGroup: logGroup + }); const publicSite = new DoorwayPublicSite(this, `doorway-public-service-${environment}`, { environment: environment, logGroup: logGroup }) publicSite.service.node.addDependency(api.service) - const lb = new DoorwayPublicLoadBalancer(this, `doorway-public-lb-${environment}`, { - environment: environment, - publicService: publicSite.service, - partnersService: publicSite.service, - logGroup: logGroup - }).loadBalancer; - lb.node.addDependency(publicSite.service) + publicSite.service.node.addDependency(lb.loadBalancer) + lb.targetGroup.addTarget(publicSite.service) + + + } } diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index 01e4d1a29bb..ac3f75e6bc8 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -1,17 +1,17 @@ import { aws_ec2, Duration, Fn } from "aws-cdk-lib"; import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2"; -import { ApplicationListenerRule, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup, ListenerAction, ListenerCondition, TargetType } from "aws-cdk-lib/aws-elasticloadbalancingv2"; -import { ARecord, HostedZone, RecordTarget } from "aws-cdk-lib/aws-route53"; -import { LoadBalancerTarget } from "aws-cdk-lib/aws-route53-targets"; +import { ApplicationListener, ApplicationListenerRule, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup, ListenerAction, ListenerCondition, TargetType } from "aws-cdk-lib/aws-elasticloadbalancingv2"; +import { HostedZone } from "aws-cdk-lib/aws-route53"; import { StringParameter } from "aws-cdk-lib/aws-ssm"; import { Construct } from "constructs"; -import { DoorwayLoadBalancerProps } from "./doorway-props"; +import { DoorwayProps } from "./doorway-props"; export class DoorwayPublicLoadBalancer { public loadBalancer: ApplicationLoadBalancer - constructor(scope: Construct, id: string, props: DoorwayLoadBalancerProps) { + public targetGroup: ApplicationTargetGroup; + constructor(scope: Construct, id: string, props: DoorwayProps) { // Get Network setup const vpcId = Fn.importValue(`doorway-vpc-id-${props.environment}`) @@ -42,28 +42,26 @@ export class DoorwayPublicLoadBalancer { }); const dnsZone = StringParameter.fromStringParameterName(scope, `publicDnsZoneId-${props.environment}`, `/doorway/public-hosted-zone`).stringValue; - new ARecord(scope, `publicDnsName-${props.environment}`, { - zone: HostedZone.fromHostedZoneAttributes(scope, `publicDnsZone-${props.environment}`, { + + const cert = new Certificate(scope, "PublicCertificate", { + domainName: process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`, + + validation: CertificateValidation.fromDns(HostedZone.fromHostedZoneAttributes(scope, "PublicHostedZone", { hostedZoneId: dnsZone, zoneName: 'housingbayarea.mtc.ca.gov', - }), - recordName: process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`, - target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer, { - evaluateTargetHealth: true - })) + })), }) const httpListener = this.loadBalancer.addListener("HttpListener", { port: 80, open: true, }); - const httpsListener = this.loadBalancer.addListener("HttpsListener", { + const httpsListener = new ApplicationListener(scope, "HttpsListener", { + loadBalancer: this.loadBalancer, port: 443, - open: true, - certificates: [new Certificate(scope, "PublicCertificate", { - domainName: process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`, - validation: CertificateValidation.fromDns() - })] - }); + certificates: [cert], + protocol: ApplicationProtocol.HTTPS, + open: true + }) httpListener.addAction("DefaultAction", { action: ListenerAction.redirect({ protocol: "HTTPS", @@ -73,34 +71,39 @@ export class DoorwayPublicLoadBalancer { }), }) httpsListener.addAction("DefaultAction", { - action: ListenerAction.fixedResponse(200, { + action: ListenerAction.fixedResponse(403, { contentType: "text/plain", - messageBody: "OK", + messageBody: "Forbidden", }) }) - new ApplicationListenerRule(scope, "PublicDomainRule", { + this.targetGroup = new ApplicationTargetGroup(scope, "PublicTargetGroup", { + port: Number(process.env.PUBLIC_PORTAL_PORT) || 3000, + protocol: ApplicationProtocol.HTTP, + vpc: vpc, + targetGroupName: `public-tg-${props.environment}`, + targetType: TargetType.IP, + + + + healthCheck: { + enabled: true, + healthyThresholdCount: 2, + interval: Duration.seconds(10), + path: "/", + timeout: Duration.seconds(5), + unhealthyThresholdCount: 2, + }, + }) + const listenerRule = new ApplicationListenerRule(scope, "PublicDomainRule", { listener: httpsListener, - priority: 1, - action: ListenerAction.forward([new ApplicationTargetGroup(scope, "PublicTargetGroup", { - port: Number(process.env.PUBLIC_PORTAL_PORT) || 3000, - protocol: ApplicationProtocol.HTTP, - vpc: vpc, - targetGroupName: `public-tg-${props.environment}`, - targetType: TargetType.IP, - targets: [props.publicService], - healthCheck: { - enabled: true, - healthyThresholdCount: 2, - interval: Duration.seconds(10), - path: "/", - timeout: Duration.seconds(5), - unhealthyThresholdCount: 2, - }, - })]), + priority: 100, + action: ListenerAction.forward([this.targetGroup]), conditions: [ListenerCondition.hostHeaders([process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`]) ] }); + + } } \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts index a0ed5b67364..31d5ead2fab 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -6,8 +6,8 @@ import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" -import { DoorwayProps } from "./doorway-props" import { DoorwayService } from "./doorway_service" +import { DoorwayProps } from "./doorway-props" export class DoorwayPublicSite { public service: FargateService @@ -70,13 +70,6 @@ export class DoorwayPublicSite { container: `doorway/public:run-${process.env.CODEBUILD_RESOLVED_SOURCE_VERSION?.substring(0, 8) || "candidate"}` }).service - const publicTG = new ApplicationTargetGroup(scope, `publicTG-${id}`, { - port: Number(port), - protocol: ApplicationProtocol.HTTP, - vpc: this.service.cluster.vpc, - targets: [this.service], - targetGroupName: `public-${props.environment}`, - }); From c607fd110cd89715a6d798206c6fa02fab5db8c7 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 25 Aug 2025 09:39:26 -0700 Subject: [PATCH 045/217] changing api to backend --- cdk/lib/doorway_environment/doorway-app-environment-stack.ts | 5 +++-- .../{doorway-api-service.ts => doorway-backend-service.ts} | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) rename cdk/lib/doorway_environment/{doorway-api-service.ts => doorway-backend-service.ts} (99%) diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index a900919df98..b56612d9cc4 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -3,7 +3,7 @@ import { LogGroup } from "aws-cdk-lib/aws-logs" import { Construct } from "constructs" import dotenv from "dotenv" -import { DoorwayApiService } from "./doorway-api-service" +import { DoorwayBackendService } from "./doorway-backend-service" import { DoorwayPublicLoadBalancer } from "./doorway-public-load-balancer" import { DoorwayPublicSite } from "./doorway-public-site" @@ -18,7 +18,7 @@ export class DoorwayAppEnvironmentStack extends Stack { retention: aws_logs.RetentionDays.ONE_WEEK, }) - const api = new DoorwayApiService(this, `doorway-api-service-${environment}`, { + const api = new DoorwayBackendService(this, `doorway-api-service-${environment}`, { environment: environment, logGroup: logGroup, @@ -38,5 +38,6 @@ export class DoorwayAppEnvironmentStack extends Stack { + } } diff --git a/cdk/lib/doorway_environment/doorway-api-service.ts b/cdk/lib/doorway_environment/doorway-backend-service.ts similarity index 99% rename from cdk/lib/doorway_environment/doorway-api-service.ts rename to cdk/lib/doorway_environment/doorway-backend-service.ts index 066ed272849..085fc5b4be1 100644 --- a/cdk/lib/doorway_environment/doorway-api-service.ts +++ b/cdk/lib/doorway_environment/doorway-backend-service.ts @@ -9,7 +9,7 @@ import { Construct } from "constructs" import { DoorwayProps } from "./doorway-props" import { DoorwayService } from "./doorway_service" -export class DoorwayApiService { +export class DoorwayBackendService { public service: FargateService public constructor(scope: Construct, id: string, props: DoorwayProps) { const executionRole = new Role(scope, `executionRole-${id}`, { From e0f08fd56f070343d57b516be8e5e2d9591c691e Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 25 Aug 2025 09:43:19 -0700 Subject: [PATCH 046/217] fix: adding arecord --- .../doorway-public-load-balancer.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index ac3f75e6bc8..f7aae242cd0 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -2,7 +2,8 @@ import { aws_ec2, Duration, Fn } from "aws-cdk-lib"; import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2"; import { ApplicationListener, ApplicationListenerRule, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup, ListenerAction, ListenerCondition, TargetType } from "aws-cdk-lib/aws-elasticloadbalancingv2"; -import { HostedZone } from "aws-cdk-lib/aws-route53"; +import { ARecord, HostedZone, RecordTarget } from "aws-cdk-lib/aws-route53"; +import { LoadBalancerTarget } from "aws-cdk-lib/aws-route53-targets"; import { StringParameter } from "aws-cdk-lib/aws-ssm"; import { Construct } from "constructs"; @@ -94,7 +95,7 @@ export class DoorwayPublicLoadBalancer { unhealthyThresholdCount: 2, }, }) - const listenerRule = new ApplicationListenerRule(scope, "PublicDomainRule", { + new ApplicationListenerRule(scope, "PublicDomainRule", { listener: httpsListener, priority: 100, action: ListenerAction.forward([this.targetGroup]), @@ -103,6 +104,14 @@ export class DoorwayPublicLoadBalancer { }); + new ARecord(scope, "PublicARecord", { + zone: HostedZone.fromHostedZoneAttributes(scope, "PublicHostedZone", { + hostedZoneId: dnsZone, + zoneName: 'housingbayarea.mtc.ca.gov', + }), + recordName: process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`, + target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), + }); } From 2b938fd65a9dd4ead8bb8417b61fd50576cb0947 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 25 Aug 2025 09:47:02 -0700 Subject: [PATCH 047/217] fix: duplicate zones --- .../doorway-public-load-balancer.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index f7aae242cd0..d95037625b2 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -42,15 +42,14 @@ export class DoorwayPublicLoadBalancer { }, }); - const dnsZone = StringParameter.fromStringParameterName(scope, `publicDnsZoneId-${props.environment}`, `/doorway/public-hosted-zone`).stringValue; - + const dnsZoneName = StringParameter.fromStringParameterName(scope, `publicDnsZoneId-${props.environment}`, `/doorway/public-hosted-zone`).stringValue; + const dnsZone = HostedZone.fromLookup(scope, `publicDnsZone-${props.environment}`, { + domainName: dnsZoneName, + }); const cert = new Certificate(scope, "PublicCertificate", { domainName: process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`, - validation: CertificateValidation.fromDns(HostedZone.fromHostedZoneAttributes(scope, "PublicHostedZone", { - hostedZoneId: dnsZone, - zoneName: 'housingbayarea.mtc.ca.gov', - })), + validation: CertificateValidation.fromDns(dnsZone), }) const httpListener = this.loadBalancer.addListener("HttpListener", { port: 80, @@ -105,10 +104,7 @@ export class DoorwayPublicLoadBalancer { }); new ARecord(scope, "PublicARecord", { - zone: HostedZone.fromHostedZoneAttributes(scope, "PublicHostedZone", { - hostedZoneId: dnsZone, - zoneName: 'housingbayarea.mtc.ca.gov', - }), + zone: dnsZone, recordName: process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`, target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), }); From cc8ee5ed0d7ea0fee04595024cadce8c0bed7c89 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 25 Aug 2025 10:21:01 -0700 Subject: [PATCH 048/217] fix: local synth issues --- cdk/cdk.context.json | 8 +++++++- .../doorway_environment/doorway-app-environment-stack.ts | 7 ++++++- .../doorway_environment/doorway-public-load-balancer.ts | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/cdk/cdk.context.json b/cdk/cdk.context.json index b114dbfe135..7feba94c75f 100644 --- a/cdk/cdk.context.json +++ b/cdk/cdk.context.json @@ -1,3 +1,9 @@ { - "acknowledged-issue-numbers": [34892] + "acknowledged-issue-numbers": [ + 34892 + ], + "hosted-zone:account=364076391763:domainName=housingbayarea.mtc.ca.gov:region=us-west-2": { + "Id": "/hostedzone/Z01682742VM0KIXZ4Y3W5", + "Name": "housingbayarea.mtc.ca.gov." + } } diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index b56612d9cc4..3956aab7f26 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -9,7 +9,12 @@ import { DoorwayPublicSite } from "./doorway-public-site" export class DoorwayAppEnvironmentStack extends Stack { constructor(scope: Construct, id: string) { - super(scope, id) + super(scope, id, { + env: { + account: process.env.CDK_DEFAULT_ACCOUNT || "364076391763", + region: process.env.CDK_DEFAULT_REGION || "us-west-2", + }, + }) const environment = process.env.ENVIRONMENT || "dev2" dotenv.config({ path: `${environment}.env` }) const logGroup = new LogGroup(this, `doorway-${environment}-tasks`, { diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index d95037625b2..7b6722ea4a0 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -44,7 +44,7 @@ export class DoorwayPublicLoadBalancer { }); const dnsZoneName = StringParameter.fromStringParameterName(scope, `publicDnsZoneId-${props.environment}`, `/doorway/public-hosted-zone`).stringValue; const dnsZone = HostedZone.fromLookup(scope, `publicDnsZone-${props.environment}`, { - domainName: dnsZoneName, + domainName: "housingbayarea.mtc.ca.gov", }); const cert = new Certificate(scope, "PublicCertificate", { domainName: process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`, From a287e092dbc2c308c0e83bbd5d713417975597a0 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 25 Aug 2025 14:08:21 -0700 Subject: [PATCH 049/217] fix: changing serviceconnect protocol --- cdk/lib/doorway_environment/doorway_service.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway_service.ts b/cdk/lib/doorway_environment/doorway_service.ts index a5c74a2a6c7..041a485dd67 100644 --- a/cdk/lib/doorway_environment/doorway_service.ts +++ b/cdk/lib/doorway_environment/doorway_service.ts @@ -1,7 +1,7 @@ import * as cdk from "aws-cdk-lib" import { Fn } from "aws-cdk-lib" import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2" -import { Cluster, Compatibility, ContainerImage, FargateService, LogDrivers, NetworkMode, Protocol, ServiceConnectProps, TaskDefinition } from "aws-cdk-lib/aws-ecs" +import { AppProtocol, Cluster, Compatibility, ContainerImage, FargateService, LogDrivers, NetworkMode, Protocol, ServiceConnectProps, TaskDefinition } from "aws-cdk-lib/aws-ecs" //import * as ecs from "aws-cdk-lib/aws-ecs"; import { CompositePrincipal, ManagedPolicy, PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { PrivateDnsNamespace } from "aws-cdk-lib/aws-servicediscovery" @@ -79,6 +79,7 @@ export class DoorwayService { name: `${id}-port-mapping`, containerPort: props.port, protocol: Protocol.TCP, + appProtocol: AppProtocol.http, hostPort: props.port, }, ], @@ -93,15 +94,8 @@ export class DoorwayService { logGroup: props.logGroup, streamPrefix: `${id}-service-connect`, }), - services: [{ - portMappingName: `${id}-port-mapping`, - dnsName: props.domainName, - - }] } - - if (props.serviceConnectServer) { const namespace = new PrivateDnsNamespace( @@ -109,6 +103,7 @@ export class DoorwayService { `doorway-${props.environment}-internal-api-namespace`, { vpc: vpc, + name: `doorway-${props.environment}-internal-api`, description: `Private DNS namespace for the Doorway ${props.environment} internal API`, }, @@ -150,7 +145,6 @@ export class DoorwayService { tls: { role: scRole, awsPcaAuthorityArn: privateCAArn, - }, portMappingName: `${id}-port-mapping`, dnsName: props.domainName, From d899fbc056fece385a8116fe2545a69eb2cc2fe9 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 25 Aug 2025 15:11:00 -0700 Subject: [PATCH 050/217] fix: changing port in backend definition --- cdk/README.md | 28 ++++++++++++++++++++-------- cdk/dev2.env | 2 +- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/cdk/README.md b/cdk/README.md index 80c3df35e1b..938ea8bb778 100644 --- a/cdk/README.md +++ b/cdk/README.md @@ -1,14 +1,26 @@ -# Welcome to your CDK TypeScript project +# Doorway App CDK Project -This is a blank project for CDK development with TypeScript. +This project uses [CDK](https://aws.amazon.com/cdk/) to deploy the ECS tasks and services to AWS. This presumes that you have already deployed [doorway-infra](https://github.com/metrotranscom/doorway-infra) to the environment. The `cdk.json` file tells the CDK Toolkit how to execute your app. +# Necessary workstation setup + +You will need the following things set up before you work with the code in this repo: + +- A Typescript IDE like VS Code. +- NodeJS - currently version 22 - I reccomend using [NVM](https://github.com/nvm-sh/nvm). +- The [AWS CLI](https://aws.amazon.com/cli/). +- The [AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting-started.html) +- [Yarn](https://yarnpkg.com/) + ## Useful commands -- `npm run build` compile typescript to js -- `npm run watch` watch for changes and compile -- `npm run test` perform the jest unit tests -- `npx cdk deploy` deploy this stack to your default AWS account/region -- `npx cdk diff` compare deployed stack with current state -- `npx cdk synth` emits the synthesized CloudFormation template +- `yarn test` Perform the jest unit tests. +- `cdk synth` emits the synthesized CloudFormation template. This is also good to use on your local + workstation to make sure your CDK code hangs together. **This doesn't touch the actual AWS + infrastructure.** +- `cdk deploy DoorwayBuildPipelineStack` This deploys the Doorway application delivery pipeline. **This impacts AWS and will only work if you have the proper access and have configured your AWS credentials file.** +- `cdk deploy DoorwayAppEnvironmentStack` This deploys a Doorway application. You will likely need to be an AWS admin to run this. You will also need to set an an environment variable named ENVIRONMENT. and a ".env" file named [$ENVIRONMENT].env for the scriot to load. **This impacts AWS and will only work if you have the proper access and have configured your AWS credentials file.** +- `cdk diff` compare deployed stack with current state + diff --git a/cdk/dev2.env b/cdk/dev2.env index f1701462cce..4ee27e21a18 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -25,7 +25,7 @@ PARTNERS_PORTAL_PORT=3200 PARTNERS_PORTAL_DOMAIN=dev2.housingbayarea.mtc.ca.gov PARTNERS_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY -BACKEND_API_BASE=http://backend.dev2.housingbayarea.int +BACKEND_API_BASE=http://backend.dev2.housingbayarea.int:3100 BACKEND_API_PORT=3100 BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVEDELIVERY_API_URL, BACKEND_API_MEMORY=4096 From 12c705cf69eef4a043d629c55137d9cd3dff6451 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 25 Aug 2025 16:58:52 -0700 Subject: [PATCH 051/217] fix: fixing secrets --- cdk/bin/doorway-cdk.ts | 5 +- cdk/dev2.env | 2 +- cdk/jsdoc.json | 22 + .../doorway-app-environment-stack.ts | 7 +- .../doorway-backend-service.ts | 12 +- .../doorway-public-site.ts | 15 +- cdk/package.json | 7 +- cdk/yarn.lock | 981 +++++++++++++++++- 8 files changed, 1028 insertions(+), 23 deletions(-) create mode 100644 cdk/jsdoc.json diff --git a/cdk/bin/doorway-cdk.ts b/cdk/bin/doorway-cdk.ts index c488a14eff4..857ba7d7c85 100644 --- a/cdk/bin/doorway-cdk.ts +++ b/cdk/bin/doorway-cdk.ts @@ -3,7 +3,10 @@ import * as cdk from "aws-cdk-lib" import { DoorwayBuildPipelineStack } from "../lib/build-pipeline/doorway-build-pipeline-stack" import { DoorwayAppEnvironmentStack } from "../lib/doorway_environment/doorway-app-environment-stack" - +/** + * This script is really just a wrapper to execute the various + * stacks that make up the Doorway Application infrastructure and build pipeline. + **/ const app = new cdk.App() new DoorwayBuildPipelineStack(app, "DoorwayBuildPipelineStack", { diff --git a/cdk/dev2.env b/cdk/dev2.env index 4ee27e21a18..09c7840fba2 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -27,7 +27,7 @@ PARTNERS_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY BACKEND_API_BASE=http://backend.dev2.housingbayarea.int:3100 BACKEND_API_PORT=3100 -BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVEDELIVERY_API_URL, +BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVEDELIVERY_API_URL BACKEND_API_MEMORY=4096 BACKEND_API_CPUS=2 BACKEND_API_INSTANCES=3 diff --git a/cdk/jsdoc.json b/cdk/jsdoc.json new file mode 100644 index 00000000000..d433fafa3bd --- /dev/null +++ b/cdk/jsdoc.json @@ -0,0 +1,22 @@ +{ + "plugins": [ + "plugins/markdown", + "node_modules/jsdoc-plugin-typescript" + ], + "source": { + "include": [ + "./lib", + "./bin", + "./README.md" + ], + "includePattern": ".+\\.(js|jsx|ts|tsx)$", + "excludePattern": "(^|\\/|\\\\)_" + }, + "templates": { + "cleverLinks": false, + "monospaceLinks": false, + "default": { + "outputSourceFiles": true + } + } +} \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 3956aab7f26..9e9b47e2a45 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -2,6 +2,8 @@ import { aws_logs, RemovalPolicy, Stack } from "aws-cdk-lib" import { LogGroup } from "aws-cdk-lib/aws-logs" import { Construct } from "constructs" import dotenv from "dotenv" +import path from "path" + import { DoorwayBackendService } from "./doorway-backend-service" import { DoorwayPublicLoadBalancer } from "./doorway-public-load-balancer" @@ -16,7 +18,9 @@ export class DoorwayAppEnvironmentStack extends Stack { }, }) const environment = process.env.ENVIRONMENT || "dev2" - dotenv.config({ path: `${environment}.env` }) + dotenv.config({ path: path.resolve(__dirname, `../../${environment}.env`) }) + + const logGroup = new LogGroup(this, `doorway-${environment}-tasks`, { logGroupName: `doorway-${environment}-tasks`, removalPolicy: RemovalPolicy.DESTROY, @@ -30,7 +34,6 @@ export class DoorwayAppEnvironmentStack extends Stack { }) const lb = new DoorwayPublicLoadBalancer(this, `doorway-public-lb-${environment}`, { environment: environment, - logGroup: logGroup }); const publicSite = new DoorwayPublicSite(this, `doorway-public-service-${environment}`, { diff --git a/cdk/lib/doorway_environment/doorway-backend-service.ts b/cdk/lib/doorway_environment/doorway-backend-service.ts index 085fc5b4be1..08ed7c401e7 100644 --- a/cdk/lib/doorway_environment/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/doorway-backend-service.ts @@ -83,12 +83,12 @@ export class DoorwayBackendService { PGHOST: Secret.fromSecretsManager(dbSecret, "host"), PGDATABASE: Secret.fromSecretsManager(dbSecret, "dbname"), } - process.env.BACKEND_API_SECRETS || - "".split(",").forEach((secretName) => { - secrets[secretName] = Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${secretName}`), - ) - }) + const secretNames = process.env.BACKEND_API_SECRETS || "" + secretNames.split(",").forEach((secretName) => { + secrets[secretName] = Secret.fromSecretsManager( + secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${secretName}`), + ) + }) // Grant write access to the uploads buckets. publicUploads.grantReadWrite(executionRole) diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts index 31d5ead2fab..d6ba5d4cfe2 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -1,13 +1,12 @@ import { Fn } from "aws-cdk-lib" import { FargateService, Secret } from "aws-cdk-lib/aws-ecs" -import { ApplicationProtocol, ApplicationTargetGroup } from "aws-cdk-lib/aws-elasticloadbalancingv2" import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" -import { DoorwayService } from "./doorway_service" import { DoorwayProps } from "./doorway-props" +import { DoorwayService } from "./doorway_service" export class DoorwayPublicSite { public service: FargateService @@ -44,13 +43,13 @@ export class DoorwayPublicSite { SHOW_ALL_MAP_PINS: process.env.SHOW_ALL_MAP_PINS || "true", SHOW_PROFESSIONAL_PARTNERS: process.env.SHOW_PROFESSIONAL_PARTNERS || "true", } + const secretNames = (process.env.PUBLIC_PORTAL_SECRETS || "").split(",") const secrets: { [key: string]: Secret } = {} - process.env.PUBLIC_PORTAL_SECRETS || - "".split(",").forEach((secretName) => { - secrets[secretName] = Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2(scope, `${secretName}-${id}`, `/doorway/${secretName}`), - ) - }) + secretNames.forEach((secretName) => { + secrets[secretName] = Secret.fromSecretsManager( + secret.Secret.fromSecretNameV2(scope, `${secretName}-${id}`, `/doorway/${secretName}`), + ) + }) this.service = new DoorwayService(scope, `${id}-service`, { ...props, memory: Number(process.env.PUBLIC_PORTAL_MEMORY || 4096), diff --git a/cdk/package.json b/cdk/package.json index 97a54c9db4d..cedd864acf5 100644 --- a/cdk/package.json +++ b/cdk/package.json @@ -8,13 +8,16 @@ "build": "tsc", "watch": "tsc -w", "test": "jest", - "cdk": "cdk" + "cdk": "cdk", + "jsdoc": "jsdoc -c jsdoc.json" }, "devDependencies": { "@types/jest": "^29.5.14", "@types/node": "22.7.9", "aws-cdk": "2.1024.0", "jest": "^29.7.0", + "jsdoc": "^4.0.4", + "jsdoc-plugin-typescript": "^3.2.0", "ts-jest": "^29.2.5", "ts-node": "^10.9.2", "typescript": "~5.6.3" @@ -24,4 +27,4 @@ "constructs": "^10.0.0", "dotenv": "^17.2.1" } -} +} \ No newline at end of file diff --git a/cdk/yarn.lock b/cdk/yarn.lock index 600f34292ca..a9651564472 100644 --- a/cdk/yarn.lock +++ b/cdk/yarn.lock @@ -135,7 +135,7 @@ "@babel/template" "^7.27.2" "@babel/types" "^7.28.2" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.27.2", "@babel/parser@^7.28.3": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.15", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.27.2", "@babel/parser@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.3.tgz#d2d25b814621bca5fe9d172bc93792547e7a2a71" integrity sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA== @@ -550,6 +550,13 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jsdoc/salty@^0.2.1": + version "0.2.9" + resolved "https://registry.yarnpkg.com/@jsdoc/salty/-/salty-0.2.9.tgz#4d8c147f7ca011532681ce86352a77a0178f1dec" + integrity sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw== + dependencies: + lodash "^4.17.21" + "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -656,6 +663,24 @@ expect "^29.0.0" pretty-format "^29.0.0" +"@types/linkify-it@^5": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-5.0.0.tgz#21413001973106cda1c3a9b91eedd4ccd5469d76" + integrity sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q== + +"@types/markdown-it@^14.1.1": + version "14.1.2" + resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-14.1.2.tgz#57f2532a0800067d9b934f3521429a2e8bfb4c61" + integrity sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog== + dependencies: + "@types/linkify-it" "^5" + "@types/mdurl" "^2" + +"@types/mdurl@^2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-2.0.0.tgz#d43878b5b20222682163ae6f897b20447233bdfd" + integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg== + "@types/node@*": version "24.3.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-24.3.0.tgz#89b09f45cb9a8ee69466f18ee5864e4c3eb84dec" @@ -753,11 +778,49 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" + integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== + dependencies: + call-bound "^1.0.3" + is-array-buffer "^3.0.5" + +arraybuffer.prototype.slice@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" + integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + is-array-buffer "^3.0.4" + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +async-function@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" + integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + aws-cdk-lib@2.208.0: version "2.208.0" resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.208.0.tgz#bec8ae4219b857b75d2841e4c6f023c42bdedd10" @@ -853,6 +916,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + brace-expansion@^1.1.7: version "1.1.12" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" @@ -897,6 +965,32 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bind@^1.0.7, call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== + dependencies: + call-bind-apply-helpers "^1.0.0" + es-define-property "^1.0.0" + get-intrinsic "^1.2.4" + set-function-length "^1.2.2" + +call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -922,6 +1016,13 @@ case@1.6.3: resolved "https://registry.yarnpkg.com/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9" integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== +catharsis@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/catharsis/-/catharsis-0.9.0.tgz#40382a168be0e6da308c277d3a2b3eb40c7d2121" + integrity sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A== + dependencies: + lodash "^4.17.15" + chalk@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -1018,6 +1119,33 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +data-view-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" + integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + +data-view-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" + integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + +data-view-byte-offset@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" + integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-data-view "^1.0.1" + debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.4.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" @@ -1035,6 +1163,24 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -1055,6 +1201,15 @@ dotenv@^17.2.1: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.2.1.tgz#6f32e10faf014883515538dc922a0fb8765d9b32" integrity sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ== +dunder-proto@^1.0.0, dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + electron-to-chromium@^1.5.199: version "1.5.202" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.202.tgz#e243716943a2cab144604a81b9ebaa87b4e8c1d4" @@ -1070,6 +1225,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -1077,6 +1237,102 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9: + version "1.24.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" + integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg== + dependencies: + array-buffer-byte-length "^1.0.2" + arraybuffer.prototype.slice "^1.0.4" + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + data-view-buffer "^1.0.2" + data-view-byte-length "^1.0.2" + data-view-byte-offset "^1.0.1" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + es-set-tostringtag "^2.1.0" + es-to-primitive "^1.3.0" + function.prototype.name "^1.1.8" + get-intrinsic "^1.3.0" + get-proto "^1.0.1" + get-symbol-description "^1.1.0" + globalthis "^1.0.4" + gopd "^1.2.0" + has-property-descriptors "^1.0.2" + has-proto "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + internal-slot "^1.1.0" + is-array-buffer "^3.0.5" + is-callable "^1.2.7" + is-data-view "^1.0.2" + is-negative-zero "^2.0.3" + is-regex "^1.2.1" + is-set "^2.0.3" + is-shared-array-buffer "^1.0.4" + is-string "^1.1.1" + is-typed-array "^1.1.15" + is-weakref "^1.1.1" + math-intrinsics "^1.1.0" + object-inspect "^1.13.4" + object-keys "^1.1.1" + object.assign "^4.1.7" + own-keys "^1.0.1" + regexp.prototype.flags "^1.5.4" + safe-array-concat "^1.1.3" + safe-push-apply "^1.0.0" + safe-regex-test "^1.1.0" + set-proto "^1.0.0" + stop-iteration-iterator "^1.1.0" + string.prototype.trim "^1.2.10" + string.prototype.trimend "^1.0.9" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.3" + typed-array-byte-length "^1.0.3" + typed-array-byte-offset "^1.0.4" + typed-array-length "^1.0.7" + unbox-primitive "^1.1.0" + which-typed-array "^1.1.19" + +es-define-property@^1.0.0, es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +es-to-primitive@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== + dependencies: + is-callable "^1.2.7" + is-date-object "^1.0.5" + is-symbol "^1.0.4" + escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" @@ -1160,6 +1416,13 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +for-each@^0.3.3, for-each@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" + integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== + dependencies: + is-callable "^1.2.7" + fs-extra@^11.3.0: version "11.3.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.1.tgz#ba7a1f97a85f94c6db2e52ff69570db3671d5a74" @@ -1189,6 +1452,23 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== +function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" + integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + functions-have-names "^1.2.3" + hasown "^2.0.2" + is-callable "^1.2.7" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -1199,16 +1479,49 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-proto@^1.0.0, get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-symbol-description@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" + integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -1221,7 +1534,20 @@ glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: +globalthis@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +gopd@^1.0.1, gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -1238,11 +1564,42 @@ handlebars@^4.7.8: optionalDependencies: uglify-js "^3.1.4" +has-bigints@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" + integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg== + has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" + integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== + dependencies: + dunder-proto "^1.0.0" + +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" @@ -1291,11 +1648,60 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +internal-slot@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" + integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.2" + side-channel "^1.1.0" + +is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" + integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-async-function@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" + integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== + dependencies: + async-function "^1.0.0" + call-bound "^1.0.3" + get-proto "^1.0.1" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + +is-bigint@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== + dependencies: + has-bigints "^1.0.2" + +is-boolean-object@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" + integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + is-core-module@^2.16.0: version "2.16.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" @@ -1303,6 +1709,30 @@ is-core-module@^2.16.0: dependencies: hasown "^2.0.2" +is-data-view@^1.0.1, is-data-view@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" + integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== + dependencies: + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + is-typed-array "^1.1.13" + +is-date-object@^1.0.5, is-date-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" + integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== + dependencies: + call-bound "^1.0.2" + has-tostringtag "^1.0.2" + +is-finalizationregistry@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" + integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== + dependencies: + call-bound "^1.0.3" + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -1313,16 +1743,115 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.10: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.0.tgz#bf3eeda931201394f57b5dba2800f91a238309ca" + integrity sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ== + dependencies: + call-bound "^1.0.3" + get-proto "^1.0.0" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + +is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-number-object@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" + integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-regex@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== + dependencies: + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" + integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== + dependencies: + call-bound "^1.0.3" + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-string@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" + integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-symbol@^1.0.4, is-symbol@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" + integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== + dependencies: + call-bound "^1.0.2" + has-symbols "^1.1.0" + safe-regex-test "^1.1.0" + +is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== + dependencies: + which-typed-array "^1.1.16" + +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + +is-weakref@^1.0.2, is-weakref@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" + integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== + dependencies: + call-bound "^1.0.3" + +is-weakset@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" + integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== + dependencies: + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -1752,6 +2281,41 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js2xmlparser@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-4.0.2.tgz#2a1fdf01e90585ef2ae872a01bc169c6a8d5e60a" + integrity sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA== + dependencies: + xmlcreate "^2.0.4" + +jsdoc-plugin-typescript@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsdoc-plugin-typescript/-/jsdoc-plugin-typescript-3.2.0.tgz#044daa753417ee88db07fb51051aa07d8677ef72" + integrity sha512-9AbhhBo+mA6N8KV7Q2r/ucNpG7NEALrUqeWgIzszkXEylvyuC26eK9hkB81Wjav2+rmJEbZkvAhA2zuNUgw5Zg== + dependencies: + string.prototype.matchall "^4.0.0" + +jsdoc@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/jsdoc/-/jsdoc-4.0.4.tgz#86565a9e39cc723a3640465b3fb189a22d1206ca" + integrity sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw== + dependencies: + "@babel/parser" "^7.20.15" + "@jsdoc/salty" "^0.2.1" + "@types/markdown-it" "^14.1.1" + bluebird "^3.7.2" + catharsis "^0.9.0" + escape-string-regexp "^2.0.0" + js2xmlparser "^4.0.2" + klaw "^3.0.0" + markdown-it "^14.1.0" + markdown-it-anchor "^8.6.7" + marked "^4.0.10" + mkdirp "^1.0.4" + requizzle "^0.2.3" + strip-json-comments "^3.1.0" + underscore "~1.13.2" + jsesc@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" @@ -1791,6 +2355,13 @@ jsonschema@~1.4.1: resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== +klaw@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-3.0.0.tgz#b11bec9cf2492f06756d6e809ab73a2910259146" + integrity sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g== + dependencies: + graceful-fs "^4.1.9" + kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -1806,6 +2377,13 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +linkify-it@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421" + integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ== + dependencies: + uc.micro "^2.0.0" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -1823,6 +2401,11 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== +lodash@^4.17.15, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -1849,6 +2432,38 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +markdown-it-anchor@^8.6.7: + version "8.6.7" + resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz#ee6926daf3ad1ed5e4e3968b1740eef1c6399634" + integrity sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA== + +markdown-it@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45" + integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg== + dependencies: + argparse "^2.0.1" + entities "^4.4.0" + linkify-it "^5.0.0" + mdurl "^2.0.0" + punycode.js "^2.3.1" + uc.micro "^2.1.0" + +marked@^4.0.10: + version "4.3.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" + integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +mdurl@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0" + integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -1891,6 +2506,11 @@ minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -1928,6 +2548,28 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +object-inspect@^1.13.3, object-inspect@^1.13.4: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.7: + version "4.1.7" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" + integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + has-symbols "^1.1.0" + object-keys "^1.1.1" + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -1942,6 +2584,15 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +own-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" + integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== + dependencies: + get-intrinsic "^1.2.6" + object-keys "^1.1.1" + safe-push-apply "^1.0.0" + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -2020,6 +2671,11 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +possible-typed-array-names@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" + integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== + pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -2037,6 +2693,11 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" +punycode.js@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" + integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA== + punycode@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -2052,6 +2713,32 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== +reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" + integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.9" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.7" + get-proto "^1.0.1" + which-builtin-type "^1.2.1" + +regexp.prototype.flags@^1.5.3, regexp.prototype.flags@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" + integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-errors "^1.3.0" + get-proto "^1.0.1" + gopd "^1.2.0" + set-function-name "^2.0.2" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -2062,6 +2749,13 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +requizzle@^0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.4.tgz#319eb658b28c370f0c20f968fa8ceab98c13d27c" + integrity sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw== + dependencies: + lodash "^4.17.21" + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -2088,6 +2782,34 @@ resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +safe-array-concat@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" + integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + has-symbols "^1.1.0" + isarray "^2.0.5" + +safe-push-apply@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" + integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== + dependencies: + es-errors "^1.3.0" + isarray "^2.0.5" + +safe-regex-test@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-regex "^1.2.1" + semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -2098,6 +2820,37 @@ semver@^7.5.3, semver@^7.5.4, semver@^7.7.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== +set-function-length@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +set-proto@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" + integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== + dependencies: + dunder-proto "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -2110,6 +2863,46 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -2159,6 +2952,14 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +stop-iteration-iterator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" + integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== + dependencies: + es-errors "^1.3.0" + internal-slot "^1.1.0" + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -2176,6 +2977,57 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string.prototype.matchall@^4.0.0: + version "4.0.12" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz#6c88740e49ad4956b1332a911e949583a275d4c0" + integrity sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-abstract "^1.23.6" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.6" + gopd "^1.2.0" + has-symbols "^1.1.0" + internal-slot "^1.1.0" + regexp.prototype.flags "^1.5.3" + set-function-name "^2.0.2" + side-channel "^1.1.0" + +string.prototype.trim@^1.2.10: + version "1.2.10" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" + integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-data-property "^1.1.4" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-object-atoms "^1.0.0" + has-property-descriptors "^1.0.2" + +string.prototype.trimend@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" + integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -2193,7 +3045,7 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-json-comments@^3.1.1: +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -2298,16 +3150,81 @@ type-fest@^4.41.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== +typed-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" + integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-typed-array "^1.1.14" + +typed-array-byte-length@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" + integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== + dependencies: + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.14" + +typed-array-byte-offset@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" + integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.15" + reflect.getprototypeof "^1.0.9" + +typed-array-length@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + reflect.getprototypeof "^1.0.6" + typescript@~5.6.3: version "5.6.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== +uc.micro@^2.0.0, uc.micro@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" + integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== + uglify-js@^3.1.4: version "3.19.3" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== +unbox-primitive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" + integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== + dependencies: + call-bound "^1.0.3" + has-bigints "^1.0.2" + has-symbols "^1.1.0" + which-boxed-primitive "^1.1.1" + +underscore@~1.13.2: + version "1.13.7" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.7.tgz#970e33963af9a7dda228f17ebe8399e5fbe63a10" + integrity sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== + undici-types@~6.19.2: version "6.19.8" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" @@ -2352,6 +3269,59 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" +which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" + integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== + dependencies: + is-bigint "^1.1.0" + is-boolean-object "^1.2.1" + is-number-object "^1.1.1" + is-string "^1.1.1" + is-symbol "^1.1.1" + +which-builtin-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" + integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== + dependencies: + call-bound "^1.0.2" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" + is-async-function "^2.0.0" + is-date-object "^1.1.0" + is-finalizationregistry "^1.1.0" + is-generator-function "^1.0.10" + is-regex "^1.2.1" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.1.0" + which-collection "^1.0.2" + which-typed-array "^1.1.16" + +which-collection@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== + dependencies: + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + +which-typed-array@^1.1.16, which-typed-array@^1.1.19: + version "1.1.19" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" + integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + for-each "^0.3.5" + get-proto "^1.0.1" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -2386,6 +3356,11 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" +xmlcreate@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.4.tgz#0c5ab0f99cdd02a81065fa9cd8f8ae87624889be" + integrity sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" From 11a35d5afb281f347330c9145f82be864f56a603 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 25 Aug 2025 18:45:07 -0700 Subject: [PATCH 052/217] fix: govdelivery url --- cdk/dev2.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/dev2.env b/cdk/dev2.env index 09c7840fba2..39e354711bf 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -27,7 +27,7 @@ PARTNERS_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY BACKEND_API_BASE=http://backend.dev2.housingbayarea.int:3100 BACKEND_API_PORT=3100 -BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVEDELIVERY_API_URL +BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVDELIVERY_API_URL BACKEND_API_MEMORY=4096 BACKEND_API_CPUS=2 BACKEND_API_INSTANCES=3 From baea7bc40e07a0d9b2d728abde94e91de295c784 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 26 Aug 2025 09:24:09 -0700 Subject: [PATCH 053/217] fix: granting proper sg access --- .../doorway-backend-service.ts | 25 ++++++++++++++- cdk/lib/doorway_environment/doorway-props.ts | 2 ++ .../doorway-public-site.ts | 31 ++++++++++++++++--- .../doorway_environment/doorway_service.ts | 17 +++++----- 4 files changed, 60 insertions(+), 15 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-backend-service.ts b/cdk/lib/doorway_environment/doorway-backend-service.ts index 08ed7c401e7..4ada7c89eb9 100644 --- a/cdk/lib/doorway_environment/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/doorway-backend-service.ts @@ -1,4 +1,5 @@ import { Aws, Fn } from "aws-cdk-lib" +import { ISecurityGroup, Port, Protocol, SecurityGroup } from "aws-cdk-lib/aws-ec2" import { FargateService, Secret } from "aws-cdk-lib/aws-ecs" import { PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Bucket } from "aws-cdk-lib/aws-s3" @@ -119,6 +120,27 @@ export class DoorwayBackendService { `arn:aws:ses:${Aws.REGION}:${Aws.ACCOUNT_ID}:identity/*`, ], }) + const appTierPrivateSGId = Fn.importValue(`doorway-app-sg-${props.environment}`) + const privateSG: ISecurityGroup = SecurityGroup.fromSecurityGroupId( + scope, + `appTierPrivateSG-${id}`, + appTierPrivateSGId, + ) + const publicSGId = Fn.importValue(`doorway-public-sg-${props.environment}`) + const publicSG: ISecurityGroup = SecurityGroup.fromSecurityGroupId( + scope, + `publicSG-${id}`, + publicSGId, + ) + const inboundPort = Number(process.env.BACKEND_API_PORT || 3100) + privateSG.addIngressRule(privateSG, new Port({ + protocol: Protocol.TCP, + stringRepresentation: "inbound-backend", + fromPort: inboundPort, + toPort: inboundPort + + + }), "Allow internal traffic from web app servers") executionRole.addToPolicy(policy) this.service = new DoorwayService(scope, `doorway-api-service-${props.environment}`, { memory: Number(process.env.BACKEND_MEMORY || 4096), @@ -134,7 +156,8 @@ export class DoorwayBackendService { secureUploads: secureUploads, environment: props.environment, logGroup: props.logGroup, - container: "doorway/backend:run-candidate" + container: "doorway/backend:run-candidate", + securityGroup: privateSG, }).service } } diff --git a/cdk/lib/doorway_environment/doorway-props.ts b/cdk/lib/doorway_environment/doorway-props.ts index 52b8378e809..60f17e6f48d 100644 --- a/cdk/lib/doorway_environment/doorway-props.ts +++ b/cdk/lib/doorway_environment/doorway-props.ts @@ -1,3 +1,4 @@ +import { ISecurityGroup } from "aws-cdk-lib/aws-ec2" import { FargateService, Secret } from "aws-cdk-lib/aws-ecs" import { Role } from "aws-cdk-lib/aws-iam" import { LogGroup } from "aws-cdk-lib/aws-logs" @@ -23,6 +24,7 @@ export interface DoorwayServiceProps extends DoorwayProps { publicUploads: IBucket secureUploads: IBucket container: string + securityGroup: ISecurityGroup } export interface DoorwayLoadBalancerProps extends DoorwayProps { publicService: FargateService, diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts index d6ba5d4cfe2..e10ede03346 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -1,12 +1,13 @@ import { Fn } from "aws-cdk-lib" +import { ISecurityGroup, Port, Protocol, SecurityGroup } from "aws-cdk-lib/aws-ec2" import { FargateService, Secret } from "aws-cdk-lib/aws-ecs" import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" -import { DoorwayProps } from "./doorway-props" import { DoorwayService } from "./doorway_service" +import { DoorwayProps } from "./doorway-props" export class DoorwayPublicSite { public service: FargateService @@ -24,7 +25,28 @@ export class DoorwayPublicSite { `secureUploadsBucket-${id}`, Fn.importValue(`doorway-secure-uploads-${props.environment}`), ) - const port = process.env.PUBLIC_PORTAL_PORT || "3000" + const port = Number(process.env.PUBLIC_PORTAL_PORT || "3000") + const appTierPrivateSGId = Fn.importValue(`doorway-app-sg-${props.environment}`) + const privateSG: ISecurityGroup = SecurityGroup.fromSecurityGroupId( + scope, + `appTierPrivateSG-${id}`, + appTierPrivateSGId, + ) + const publicSGId = Fn.importValue(`doorway-public-sg-${props.environment}`) + const publicSG: ISecurityGroup = SecurityGroup.fromSecurityGroupId( + scope, + `publicSG-${id}`, + publicSGId, + ) + const inboundPort = Number(process.env.BACKEND_API_PORT || 3000) + privateSG.addIngressRule(publicSG, new Port({ + protocol: Protocol.TCP, + stringRepresentation: "inbound-backend", + fromPort: inboundPort, + toPort: inboundPort + + + }), "Allow internal traffic from web app servers") const environmentVariables: { [key: string]: string } = { BACKEND_API_BASE: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, @@ -35,7 +57,7 @@ export class DoorwayPublicSite { JURISDICTION_NAME: process.env.JURISDICTION_NAME || "Bay Area", LANGUAGES: process.env.LANGUAGES || "en,es,zh,vi,tl", LISTINGS_QUERY: process.env.LISTINGS_QUERY || "/listings", - NEXTJS_PORT: port, + NEXTJS_PORT: String(port), NODE_ENV: process.env.NODE_ENV || "development", NOTIFICATIONS_SIGNUP_URL: process.env.NOTIFICATIONS_SIGNUP_URL || @@ -66,7 +88,8 @@ export class DoorwayPublicSite { logGroup: props.logGroup, apiTargetDomainName: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, apiTargetPort: Number(process.env.BACKEND_API_PORT || 3000), - container: `doorway/public:run-${process.env.CODEBUILD_RESOLVED_SOURCE_VERSION?.substring(0, 8) || "candidate"}` + container: `doorway/public:run-${process.env.CODEBUILD_RESOLVED_SOURCE_VERSION?.substring(0, 8) || "candidate"}`, + securityGroup: privateSG }).service diff --git a/cdk/lib/doorway_environment/doorway_service.ts b/cdk/lib/doorway_environment/doorway_service.ts index 041a485dd67..0abb6947832 100644 --- a/cdk/lib/doorway_environment/doorway_service.ts +++ b/cdk/lib/doorway_environment/doorway_service.ts @@ -11,10 +11,7 @@ import { Construct } from "constructs" import { DoorwayServiceProps } from "./doorway-props" export class DoorwayService { - - public service: FargateService - constructor(scope: Construct, id: string, props: DoorwayServiceProps) { // Initial Execution Role Setup // Create the execution role for the doorway API service @@ -46,12 +43,7 @@ export class DoorwayService { appSubnetIds.forEach((id) => { appsubnets.push(Subnet.fromSubnetId(scope, id, id)) }) - const appTierPrivateSGId = cdk.Fn.importValue(`doorway-app-sg-${props.environment}`) - const privateSG = cdk.aws_ec2.SecurityGroup.fromSecurityGroupId( - scope, - `appTierPrivateSG-${id}`, - appTierPrivateSGId, - ) + const task = new TaskDefinition(scope, `${id}-task`, { compatibility: Compatibility.FARGATE, executionRole: props.executionRole, @@ -160,6 +152,11 @@ export class DoorwayService { this.service = new FargateService(scope, `${id}-fargate-service`, { taskDefinition: task, serviceName: `${id}-service`, + circuitBreaker: { + enable: true, + rollback: true, + }, + cluster: Cluster.fromClusterAttributes(scope, `ecsCluster-${id}`, { clusterName: Fn.importValue(`doorway-ecs-cluster-${props.environment}`), vpc: vpc, @@ -167,7 +164,7 @@ export class DoorwayService { vpcSubnets: { subnets: appsubnets, }, - securityGroups: [privateSG], + securityGroups: [props.securityGroup], desiredCount: props.instances, serviceConnectConfiguration: serviceConnectProps, From d4b63f67ade8325713e1a5f3515883ca28e6052f Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 26 Aug 2025 11:18:05 -0700 Subject: [PATCH 054/217] fix: renaming secrets location --- cdk/lib/doorway_environment/doorway-backend-service.ts | 4 ++-- cdk/lib/doorway_environment/doorway-public-site.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-backend-service.ts b/cdk/lib/doorway_environment/doorway-backend-service.ts index 4ada7c89eb9..1cffedcc46a 100644 --- a/cdk/lib/doorway_environment/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/doorway-backend-service.ts @@ -7,8 +7,8 @@ import * as secret from "aws-cdk-lib/aws-secretsmanager" import { EmailIdentity } from "aws-cdk-lib/aws-ses" import { Construct } from "constructs" -import { DoorwayProps } from "./doorway-props" import { DoorwayService } from "./doorway_service" +import { DoorwayProps } from "./doorway-props" export class DoorwayBackendService { public service: FargateService @@ -87,7 +87,7 @@ export class DoorwayBackendService { const secretNames = process.env.BACKEND_API_SECRETS || "" secretNames.split(",").forEach((secretName) => { secrets[secretName] = Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${secretName}`), + secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${props.environment}/${secretName}`), ) }) diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts index e10ede03346..2f064a2ab9f 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -6,8 +6,8 @@ import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" -import { DoorwayService } from "./doorway_service" import { DoorwayProps } from "./doorway-props" +import { DoorwayService } from "./doorway_service" export class DoorwayPublicSite { public service: FargateService @@ -69,7 +69,7 @@ export class DoorwayPublicSite { const secrets: { [key: string]: Secret } = {} secretNames.forEach((secretName) => { secrets[secretName] = Secret.fromSecretsManager( - secret.Secret.fromSecretNameV2(scope, `${secretName}-${id}`, `/doorway/${secretName}`), + secret.Secret.fromSecretNameV2(scope, `${secretName}-${id}`, `/doorway/${props.environment}/${secretName}`), ) }) this.service = new DoorwayService(scope, `${id}-service`, { From ca69ddeb03ff67d0e49c04cca1a8efc32250243b Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 26 Aug 2025 20:29:11 -0700 Subject: [PATCH 055/217] feat: adding restart lambdas --- cdk/cdk.context.json | 4 + .../doorway-app-environment-stack.ts | 4 +- .../doorway-backend-service.ts | 6 + .../doorway-lambda-props.ts | 20 + .../doorway-public-site.ts | 8 +- .../doorway_environment/lambda-base-class.ts | 67 + .../restart-services-lambda.ts | 58 + .../restart-services.handler.ts | 33 + cdk/package.json | 5 +- cdk/yarn.lock | 1224 +++++++++++++++++ 10 files changed, 1424 insertions(+), 5 deletions(-) create mode 100644 cdk/lib/doorway_environment/doorway-lambda-props.ts create mode 100644 cdk/lib/doorway_environment/lambda-base-class.ts create mode 100644 cdk/lib/doorway_environment/restart-services-lambda.ts create mode 100644 cdk/lib/doorway_environment/restart-services.handler.ts diff --git a/cdk/cdk.context.json b/cdk/cdk.context.json index 7feba94c75f..72b0a6d8e1b 100644 --- a/cdk/cdk.context.json +++ b/cdk/cdk.context.json @@ -5,5 +5,9 @@ "hosted-zone:account=364076391763:domainName=housingbayarea.mtc.ca.gov:region=us-west-2": { "Id": "/hostedzone/Z01682742VM0KIXZ4Y3W5", "Name": "housingbayarea.mtc.ca.gov." + }, + "hosted-zone:account=364076391763:domainName=housingbayarea.mtc.ca.gov:region=us-west-1": { + "Id": "/hostedzone/Z01682742VM0KIXZ4Y3W5", + "Name": "housingbayarea.mtc.ca.gov." } } diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 9e9b47e2a45..215f3f5a1b7 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -3,8 +3,6 @@ import { LogGroup } from "aws-cdk-lib/aws-logs" import { Construct } from "constructs" import dotenv from "dotenv" import path from "path" - - import { DoorwayBackendService } from "./doorway-backend-service" import { DoorwayPublicLoadBalancer } from "./doorway-public-load-balancer" import { DoorwayPublicSite } from "./doorway-public-site" @@ -36,7 +34,7 @@ export class DoorwayAppEnvironmentStack extends Stack { environment: environment, logGroup: logGroup }); - const publicSite = new DoorwayPublicSite(this, `doorway-public-service-${environment}`, { + const publicSite = new DoorwayPublicSite(this, `doorway-public-${environment}`, { environment: environment, logGroup: logGroup }) diff --git a/cdk/lib/doorway_environment/doorway-backend-service.ts b/cdk/lib/doorway_environment/doorway-backend-service.ts index 1cffedcc46a..deecc1775ac 100644 --- a/cdk/lib/doorway_environment/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/doorway-backend-service.ts @@ -9,6 +9,7 @@ import { Construct } from "constructs" import { DoorwayService } from "./doorway_service" import { DoorwayProps } from "./doorway-props" +import { RestartServicesLambda } from "./restart-services-lambda" export class DoorwayBackendService { public service: FargateService @@ -159,5 +160,10 @@ export class DoorwayBackendService { container: "doorway/backend:run-candidate", securityGroup: privateSG, }).service + new RestartServicesLambda(scope, `restart-backend-services-${props.environment}`, { + service: this.service, + secrets: secrets, + environment: props.environment, + }) } } diff --git a/cdk/lib/doorway_environment/doorway-lambda-props.ts b/cdk/lib/doorway_environment/doorway-lambda-props.ts new file mode 100644 index 00000000000..96754945ed5 --- /dev/null +++ b/cdk/lib/doorway_environment/doorway-lambda-props.ts @@ -0,0 +1,20 @@ +import { FargateService, Secret } from "aws-cdk-lib/aws-ecs"; + +export interface DoorwayLambdaProps { + environment: string; + name: string; + timeoutInMinutes: number; + handler: string; + memory?: number; + storage?: number; + bundling: {}; + + environmentVariables?: { [key: string]: string }; + +} +export interface DoorwayServiceMonitorLambdaProps { + secrets: { [key: string]: Secret }; + service: FargateService; + environment: string + +} \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts index 2f064a2ab9f..4dd9a43cd42 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -6,8 +6,9 @@ import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" -import { DoorwayProps } from "./doorway-props" import { DoorwayService } from "./doorway_service" +import { DoorwayProps } from "./doorway-props" +import { RestartServicesLambda } from "./restart-services-lambda" export class DoorwayPublicSite { public service: FargateService @@ -92,6 +93,11 @@ export class DoorwayPublicSite { securityGroup: privateSG }).service + new RestartServicesLambda(scope, `restart-public-services-${props.environment}`, { + service: this.service, + secrets: secrets, + environment: props.environment, + }) diff --git a/cdk/lib/doorway_environment/lambda-base-class.ts b/cdk/lib/doorway_environment/lambda-base-class.ts new file mode 100644 index 00000000000..6a34f291d04 --- /dev/null +++ b/cdk/lib/doorway_environment/lambda-base-class.ts @@ -0,0 +1,67 @@ +import { Duration, Size } from 'aws-cdk-lib'; +import { Effect, ManagedPolicy, PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; +import { Function, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; +import { Construct } from 'constructs'; + +import { DoorwayLambdaProps } from './doorway-lambda-props'; + +export class DoorwayLambdaBaseClass extends Construct { + lambdaFunction: Function; + lambdaRole: Role; + constructor(scope: Construct, id: string, props: DoorwayLambdaProps) { + super(scope, id); + const environment = props.environment; + + this.lambdaRole = new Role(this, `${props.name}-${environment}`, { + roleName: `${props.name}-${environment}`, + assumedBy: new ServicePrincipal('lambda.amazonaws.com'), + }); + this.lambdaRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole') + ); + this.lambdaRole.addToPolicy( + new PolicyStatement({ + actions: ['ec2:*'], + effect: Effect.ALLOW, + resources: ['*'], + }) + ); + this.lambdaRole.addToPolicy( + new PolicyStatement({ + actions: ['secretsmanager:GetSecretValue'], + effect: Effect.ALLOW, + resources: ['*'], + }) + ); + + + this.lambdaFunction = new NodejsFunction(this, 'function', { + runtime: Runtime.NODEJS_LATEST, + role: this.lambdaRole, + handler: 'handler', + entry: props.handler, + timeout: Duration.minutes(props.timeoutInMinutes), + environment: props.environmentVariables, + bundling: props.bundling, + memorySize: props.memory == undefined ? 128 : props.memory, + ephemeralStorageSize: Size.mebibytes(props.storage == undefined ? 512 : props.storage), + functionName: `${props.name}-${environment}`, + description: `${props.name}-${environment}`, + }); + const dwSecret = Secret.fromSecretNameV2( + this, + `DWSecret-${environment}`, + `doorway-lambdas-svcaccount/${environment}` + ); + dwSecret.grantRead(this.lambdaRole); + this.lambdaRole.addToPrincipalPolicy( + new PolicyStatement({ + actions: ['secretsmanager:GetSecretValue'], + effect: Effect.ALLOW, + resources: [dwSecret.secretArn], + }) + ); + } +} diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts new file mode 100644 index 00000000000..cf93e0d16a8 --- /dev/null +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -0,0 +1,58 @@ +import { Rule } from "aws-cdk-lib/aws-events"; +import { LambdaFunction } from "aws-cdk-lib/aws-events-targets"; +import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam"; +import { Construct } from "constructs"; + +import { DoorwayServiceMonitorLambdaProps } from "./doorway-lambda-props"; +import { DoorwayLambdaBaseClass } from "./lambda-base-class"; + +export interface ServiceRestartDetail { + eventSource: string; + eventName: string; + requestParameters: { + secretId: string; + clientRequestToken: string; + }; + responseElements: null; +} + +/** this lambda will restart an ecs service whenever the secrets change for that service. This allows the environment variables running in the containers to get the new values. */ +export class RestartServicesLambda { + constructor(scope: Construct, id: string, props: DoorwayServiceMonitorLambdaProps) { + + let secretArns: string[] = []; + for (const [key, secret] of Object.entries(props.secrets)) { + secretArns.push(secret.arn); + } + const secretRule = new Rule(scope, `${id}-secret-change-rule`, { + eventPattern: { + source: ["aws.secretsmanager"], + detail: { + "eventSource": ["secretsmanager.amazonaws.com"], + "eventName": ["PutSecretValue"], + "secret-id": secretArns, + + }, + }, + }); + const dwLambda = new DoorwayLambdaBaseClass(scope, `${id}-restart-lambda`, { + name: `${id}-function`, + environment: props.environment, + environmentVariables: { + SERVICE_NAME: props.service.serviceName, + CLUSTER_NAME: props.service.cluster.clusterName, + }, + timeoutInMinutes: 15, + memory: 1024, + storage: 512, + handler: "lib/doorway_environment/restart-services.handler.ts", + bundling: {} + }) + dwLambda.lambdaRole.addToPolicy(new PolicyStatement({ + actions: ["ecs:UpdateService", "ecs:DescribeServices"], + resources: [props.service.serviceArn], + effect: Effect.ALLOW, + })); + secretRule.addTarget(new LambdaFunction(dwLambda.lambdaFunction)); + } +} \ No newline at end of file diff --git a/cdk/lib/doorway_environment/restart-services.handler.ts b/cdk/lib/doorway_environment/restart-services.handler.ts new file mode 100644 index 00000000000..7f46d880846 --- /dev/null +++ b/cdk/lib/doorway_environment/restart-services.handler.ts @@ -0,0 +1,33 @@ +import { ECSClient, UpdateServiceCommand } from "@aws-sdk/client-ecs"; +import { EventBridgeEvent } from "aws-lambda"; + +import { ServiceRestartDetail } from "./restart-services-lambda"; + +export const handler = async function (event: EventBridgeEvent) { + const serviceName = process.env.SERVICE_NAME; + const cluster = process.env.CLUSTER_NAME; + const ecsClient = new ECSClient({ region: process.env.AWS_REGION }) + const restart = new UpdateServiceCommand( + { + service: serviceName, + forceNewDeployment: true, + cluster: cluster, + } + ) + try { + const response = await ecsClient.send(restart); + console.log("Service updated successfully:", response.service?.serviceArn); + + return { + statusCode: 200, + body: JSON.stringify({ message: "Service is restarting" }), + }; + } catch (error) { + console.error("Error updating service:", error); + return { + statusCode: 500, + body: JSON.stringify({ message: "Service Restart Failed!" }), + }; + } + +} \ No newline at end of file diff --git a/cdk/package.json b/cdk/package.json index cedd864acf5..f2b5c62c378 100644 --- a/cdk/package.json +++ b/cdk/package.json @@ -23,8 +23,11 @@ "typescript": "~5.6.3" }, "dependencies": { + "@aws-sdk/client-ecs": "^3.873.0", + "@aws-sdk/client-lambda": "^3.876.0", + "@types/aws-lambda": "^8.10.152", "aws-cdk-lib": "2.208.0", "constructs": "^10.0.0", "dotenv": "^17.2.1" } -} \ No newline at end of file +} diff --git a/cdk/yarn.lock b/cdk/yarn.lock index a9651564472..6796bcc3c41 100644 --- a/cdk/yarn.lock +++ b/cdk/yarn.lock @@ -28,6 +28,741 @@ jsonschema "~1.4.1" semver "^7.7.2" +"@aws-crypto/crc32@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-5.2.0.tgz#cfcc22570949c98c6689cfcbd2d693d36cdae2e1" + integrity sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/sha256-browser@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz#153895ef1dba6f9fce38af550e0ef58988eb649e" + integrity sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw== + dependencies: + "@aws-crypto/sha256-js" "^5.2.0" + "@aws-crypto/supports-web-crypto" "^5.2.0" + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-crypto/sha256-js@5.2.0", "@aws-crypto/sha256-js@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz#c4fdb773fdbed9a664fc1a95724e206cf3860042" + integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/supports-web-crypto@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz#a1e399af29269be08e695109aa15da0a07b5b5fb" + integrity sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg== + dependencies: + tslib "^2.6.2" + +"@aws-crypto/util@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da" + integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== + dependencies: + "@aws-sdk/types" "^3.222.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-ecs@^3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-ecs/-/client-ecs-3.873.0.tgz#58068a5d85742f6a123064a077d77eddf5d95cdf" + integrity sha512-5VjDWf7Oz5LIacFgR684M2GBUZU8cS+PVeh/IPwA0QWrTzhpfYkurzhiOlo0coutdxklU52CTM57h72izZ3k1g== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.873.0" + "@aws-sdk/credential-provider-node" "3.873.0" + "@aws-sdk/middleware-host-header" "3.873.0" + "@aws-sdk/middleware-logger" "3.873.0" + "@aws-sdk/middleware-recursion-detection" "3.873.0" + "@aws-sdk/middleware-user-agent" "3.873.0" + "@aws-sdk/region-config-resolver" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.873.0" + "@aws-sdk/util-user-agent-browser" "3.873.0" + "@aws-sdk/util-user-agent-node" "3.873.0" + "@smithy/config-resolver" "^4.1.5" + "@smithy/core" "^3.8.0" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/hash-node" "^4.0.5" + "@smithy/invalid-dependency" "^4.0.5" + "@smithy/middleware-content-length" "^4.0.5" + "@smithy/middleware-endpoint" "^4.1.18" + "@smithy/middleware-retry" "^4.1.19" + "@smithy/middleware-serde" "^4.0.9" + "@smithy/middleware-stack" "^4.0.5" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.4.10" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.26" + "@smithy/util-defaults-mode-node" "^4.0.26" + "@smithy/util-endpoints" "^3.0.7" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-retry" "^4.0.7" + "@smithy/util-utf8" "^4.0.0" + "@smithy/util-waiter" "^4.0.7" + "@types/uuid" "^9.0.1" + tslib "^2.6.2" + uuid "^9.0.1" + +"@aws-sdk/client-lambda@^3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-lambda/-/client-lambda-3.876.0.tgz#963ac141505ff30229534d4ff72b514f282323c6" + integrity sha512-dOzCiZFAG5mihv/ZVxDJmgdH6A6MoF8GP8QsWN0BuvkISPtKXE7IPJivCCh7BmcTDqH5iPje+SW+lmdrhYJ1qQ== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.876.0" + "@aws-sdk/credential-provider-node" "3.876.0" + "@aws-sdk/middleware-host-header" "3.873.0" + "@aws-sdk/middleware-logger" "3.876.0" + "@aws-sdk/middleware-recursion-detection" "3.873.0" + "@aws-sdk/middleware-user-agent" "3.876.0" + "@aws-sdk/region-config-resolver" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.873.0" + "@aws-sdk/util-user-agent-browser" "3.873.0" + "@aws-sdk/util-user-agent-node" "3.876.0" + "@smithy/config-resolver" "^4.1.5" + "@smithy/core" "^3.8.0" + "@smithy/eventstream-serde-browser" "^4.0.5" + "@smithy/eventstream-serde-config-resolver" "^4.1.3" + "@smithy/eventstream-serde-node" "^4.0.5" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/hash-node" "^4.0.5" + "@smithy/invalid-dependency" "^4.0.5" + "@smithy/middleware-content-length" "^4.0.5" + "@smithy/middleware-endpoint" "^4.1.18" + "@smithy/middleware-retry" "^4.1.19" + "@smithy/middleware-serde" "^4.0.9" + "@smithy/middleware-stack" "^4.0.5" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.4.10" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.26" + "@smithy/util-defaults-mode-node" "^4.0.26" + "@smithy/util-endpoints" "^3.0.7" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-retry" "^4.0.7" + "@smithy/util-stream" "^4.2.4" + "@smithy/util-utf8" "^4.0.0" + "@smithy/util-waiter" "^4.0.7" + tslib "^2.6.2" + +"@aws-sdk/client-sso@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.873.0.tgz#e51e7105ef47aa2675490ad602dc266637587403" + integrity sha512-EmcrOgFODWe7IsLKFTeSXM9TlQ80/BO1MBISlr7w2ydnOaUYIiPGRRJnDpeIgMaNqT4Rr2cRN2RiMrbFO7gDdA== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.873.0" + "@aws-sdk/middleware-host-header" "3.873.0" + "@aws-sdk/middleware-logger" "3.873.0" + "@aws-sdk/middleware-recursion-detection" "3.873.0" + "@aws-sdk/middleware-user-agent" "3.873.0" + "@aws-sdk/region-config-resolver" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.873.0" + "@aws-sdk/util-user-agent-browser" "3.873.0" + "@aws-sdk/util-user-agent-node" "3.873.0" + "@smithy/config-resolver" "^4.1.5" + "@smithy/core" "^3.8.0" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/hash-node" "^4.0.5" + "@smithy/invalid-dependency" "^4.0.5" + "@smithy/middleware-content-length" "^4.0.5" + "@smithy/middleware-endpoint" "^4.1.18" + "@smithy/middleware-retry" "^4.1.19" + "@smithy/middleware-serde" "^4.0.9" + "@smithy/middleware-stack" "^4.0.5" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.4.10" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.26" + "@smithy/util-defaults-mode-node" "^4.0.26" + "@smithy/util-endpoints" "^3.0.7" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-retry" "^4.0.7" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-sso@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.876.0.tgz#1088a594684aeb469b5986eeef7ca3819baf8cac" + integrity sha512-Vf0PMF7HVpvllrfPODnBZmlz6kT/y2AvOt1RQG3+qD0VrHWzShc5nwgRZ+yyP3xkKVhZsQ3sJapfZTFnjqMOYA== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.876.0" + "@aws-sdk/middleware-host-header" "3.873.0" + "@aws-sdk/middleware-logger" "3.876.0" + "@aws-sdk/middleware-recursion-detection" "3.873.0" + "@aws-sdk/middleware-user-agent" "3.876.0" + "@aws-sdk/region-config-resolver" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.873.0" + "@aws-sdk/util-user-agent-browser" "3.873.0" + "@aws-sdk/util-user-agent-node" "3.876.0" + "@smithy/config-resolver" "^4.1.5" + "@smithy/core" "^3.8.0" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/hash-node" "^4.0.5" + "@smithy/invalid-dependency" "^4.0.5" + "@smithy/middleware-content-length" "^4.0.5" + "@smithy/middleware-endpoint" "^4.1.18" + "@smithy/middleware-retry" "^4.1.19" + "@smithy/middleware-serde" "^4.0.9" + "@smithy/middleware-stack" "^4.0.5" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.4.10" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.26" + "@smithy/util-defaults-mode-node" "^4.0.26" + "@smithy/util-endpoints" "^3.0.7" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-retry" "^4.0.7" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + +"@aws-sdk/core@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.873.0.tgz#d0c2d4e890ff268c0d0c5ed9883203ef9197ab4b" + integrity sha512-WrROjp8X1VvmnZ4TBzwM7RF+EB3wRaY9kQJLXw+Aes0/3zRjUXvGIlseobGJMqMEGnM0YekD2F87UaVfot1xeQ== + dependencies: + "@aws-sdk/types" "3.862.0" + "@aws-sdk/xml-builder" "3.873.0" + "@smithy/core" "^3.8.0" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/property-provider" "^4.0.5" + "@smithy/protocol-http" "^5.1.3" + "@smithy/signature-v4" "^5.1.3" + "@smithy/smithy-client" "^4.4.10" + "@smithy/types" "^4.3.2" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-utf8" "^4.0.0" + fast-xml-parser "5.2.5" + tslib "^2.6.2" + +"@aws-sdk/core@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.876.0.tgz#8f5c6afac9c9f90e6dd19d5d257db1482e50c233" + integrity sha512-sVFBFkdoPOPyY13NaXO1E/R9O5J6ixzHnnRbqrbXYM2QQgLNPTKIiRtmVEuVoFV9YULg+/aKm7caix8m468y9w== + dependencies: + "@aws-sdk/types" "3.862.0" + "@aws-sdk/xml-builder" "3.873.0" + "@smithy/core" "^3.8.0" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/property-provider" "^4.0.5" + "@smithy/protocol-http" "^5.1.3" + "@smithy/signature-v4" "^5.1.3" + "@smithy/smithy-client" "^4.4.10" + "@smithy/types" "^4.3.2" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-utf8" "^4.0.0" + fast-xml-parser "5.2.5" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-env@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.873.0.tgz#6558387cc937c689d5e2a4d0d4e34798d526986f" + integrity sha512-FWj1yUs45VjCADv80JlGshAttUHBL2xtTAbJcAxkkJZzLRKVkdyrepFWhv/95MvDyzfbT6PgJiWMdW65l/8ooA== + dependencies: + "@aws-sdk/core" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-env@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.876.0.tgz#11e4d4964e6e2fb85351e68da3a0cea9ff3036ab" + integrity sha512-cof7lwp2AlrAfRs0pt4W2KMS2VMBvEmpcti1UOFfSJIqkn+cyJliMJ8LHg22GI+kUexjvxdAqSbf3M7OHvEW+w== + dependencies: + "@aws-sdk/core" "3.876.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-http@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.873.0.tgz#efa22f1a5fcf74c5a19b9857fe7205853eaa3191" + integrity sha512-0sIokBlXIsndjZFUfr3Xui8W6kPC4DAeBGAXxGi9qbFZ9PWJjn1vt2COLikKH3q2snchk+AsznREZG8NW6ezSg== + dependencies: + "@aws-sdk/core" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/property-provider" "^4.0.5" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.4.10" + "@smithy/types" "^4.3.2" + "@smithy/util-stream" "^4.2.4" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-http@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.876.0.tgz#da29a773b5b1ee53308978582582d6bfbb490c66" + integrity sha512-wzmef2NBp2+X1l8D4Q8hx1G8oI3+WdvLdPev9VnVpRYZxYGRWVPl++wvCBsCn/ZL0mdWopPkhHA3kFexQhMzvg== + dependencies: + "@aws-sdk/core" "3.876.0" + "@aws-sdk/types" "3.862.0" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/property-provider" "^4.0.5" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.4.10" + "@smithy/types" "^4.3.2" + "@smithy/util-stream" "^4.2.4" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-ini@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.873.0.tgz#49ddf51deb23842e91975f88e198b75a9e7cc68f" + integrity sha512-bQdGqh47Sk0+2S3C+N46aNQsZFzcHs7ndxYLARH/avYXf02Nl68p194eYFaAHJSQ1re5IbExU1+pbums7FJ9fA== + dependencies: + "@aws-sdk/core" "3.873.0" + "@aws-sdk/credential-provider-env" "3.873.0" + "@aws-sdk/credential-provider-http" "3.873.0" + "@aws-sdk/credential-provider-process" "3.873.0" + "@aws-sdk/credential-provider-sso" "3.873.0" + "@aws-sdk/credential-provider-web-identity" "3.873.0" + "@aws-sdk/nested-clients" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@smithy/credential-provider-imds" "^4.0.7" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-ini@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.876.0.tgz#6afb8bf97198f88d1740e922a9abf4b4e1155038" + integrity sha512-JHbW6fqnJsVjGHCyko7B0NVPT1nEAPxkM3CGjUcVGsHgJBkxOLVCMQqTRyHcDdeHR2qeojlLoOHRz97xIHQjYw== + dependencies: + "@aws-sdk/core" "3.876.0" + "@aws-sdk/credential-provider-env" "3.876.0" + "@aws-sdk/credential-provider-http" "3.876.0" + "@aws-sdk/credential-provider-process" "3.876.0" + "@aws-sdk/credential-provider-sso" "3.876.0" + "@aws-sdk/credential-provider-web-identity" "3.876.0" + "@aws-sdk/nested-clients" "3.876.0" + "@aws-sdk/types" "3.862.0" + "@smithy/credential-provider-imds" "^4.0.7" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-node@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.873.0.tgz#c33201aef628474056aad029f69255d94f5d990c" + integrity sha512-+v/xBEB02k2ExnSDL8+1gD6UizY4Q/HaIJkNSkitFynRiiTQpVOSkCkA0iWxzksMeN8k1IHTE5gzeWpkEjNwbA== + dependencies: + "@aws-sdk/credential-provider-env" "3.873.0" + "@aws-sdk/credential-provider-http" "3.873.0" + "@aws-sdk/credential-provider-ini" "3.873.0" + "@aws-sdk/credential-provider-process" "3.873.0" + "@aws-sdk/credential-provider-sso" "3.873.0" + "@aws-sdk/credential-provider-web-identity" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@smithy/credential-provider-imds" "^4.0.7" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-node@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.876.0.tgz#1619f84fee068217414ea2fa57c0e327768a9465" + integrity sha512-eHbNt1+Hi43e8ANnwf6toapLSxfMiyGq459y3Uh6i7NBOiWWKEsOVcgOfUC3RCoqeikxovt1tFM2cEElWUIOhg== + dependencies: + "@aws-sdk/credential-provider-env" "3.876.0" + "@aws-sdk/credential-provider-http" "3.876.0" + "@aws-sdk/credential-provider-ini" "3.876.0" + "@aws-sdk/credential-provider-process" "3.876.0" + "@aws-sdk/credential-provider-sso" "3.876.0" + "@aws-sdk/credential-provider-web-identity" "3.876.0" + "@aws-sdk/types" "3.862.0" + "@smithy/credential-provider-imds" "^4.0.7" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-process@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.873.0.tgz#2c75a8c7ab86af5cffb767e3652db6203c441513" + integrity sha512-ycFv9WN+UJF7bK/ElBq1ugWA4NMbYS//1K55bPQZb2XUpAM2TWFlEjG7DIyOhLNTdl6+CbHlCdhlKQuDGgmm0A== + dependencies: + "@aws-sdk/core" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-process@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.876.0.tgz#4f9760041e0c9f33d816ea7c5d004ef26d80a8f8" + integrity sha512-SMX4OlHvspu3gF4hxe7WAnZFhxpiCye+WlBSVoWfW/i9XNhtrZS1JMr29MK34GlCTk9qO7FlRwds/Z5k7xPpHg== + dependencies: + "@aws-sdk/core" "3.876.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-sso@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.873.0.tgz#d208878a955b74829cb36d70492f7e27f70995cd" + integrity sha512-SudkAOZmjEEYgUrqlUUjvrtbWJeI54/0Xo87KRxm4kfBtMqSx0TxbplNUAk8Gkg4XQNY0o7jpG8tK7r2Wc2+uw== + dependencies: + "@aws-sdk/client-sso" "3.873.0" + "@aws-sdk/core" "3.873.0" + "@aws-sdk/token-providers" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-sso@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.876.0.tgz#d4867ebf546c36ca3df7366bbf70ef99b958da9a" + integrity sha512-iP5dz9XqwePbgnh7Bdrq5e1319JpCRKLyomUfHH1XVeXkIHmwIJdmTj1Upeo1J8L/5cLHmhXAN6CTN11bLo8SA== + dependencies: + "@aws-sdk/client-sso" "3.876.0" + "@aws-sdk/core" "3.876.0" + "@aws-sdk/token-providers" "3.876.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-web-identity@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.873.0.tgz#f03d730c1395b6dbdc0a5e121ca4de4deeb586cc" + integrity sha512-Gw2H21+VkA6AgwKkBtTtlGZ45qgyRZPSKWs0kUwXVlmGOiPz61t/lBX0vG6I06ZIz2wqeTJ5OA1pWZLqw1j0JQ== + dependencies: + "@aws-sdk/core" "3.873.0" + "@aws-sdk/nested-clients" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-web-identity@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.876.0.tgz#ece58ac87f7b2eb13176f5f89aafe916562289ef" + integrity sha512-q/XSCP1uae5aB9veM8zcm6Gqu6A4ckX9ZbhHgCzURXVJDwp+nINW1hM9vppMjGw3ND9Ibx/adR+KfTI0TDMzqw== + dependencies: + "@aws-sdk/core" "3.876.0" + "@aws-sdk/nested-clients" "3.876.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/middleware-host-header@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.873.0.tgz#81e9c2f61674b96337472bcaefd85ce3b7a24f7b" + integrity sha512-KZ/W1uruWtMOs7D5j3KquOxzCnV79KQW9MjJFZM/M0l6KI8J6V3718MXxFHsTjUE4fpdV6SeCNLV1lwGygsjJA== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/middleware-logger@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.873.0.tgz#dc6e858ad1a2be56963a36bf7538e8c79a782a7a" + integrity sha512-QhNZ8X7pW68kFez9QxUSN65Um0Feo18ZmHxszQZNUhKDsXew/EG9NPQE/HgYcekcon35zHxC4xs+FeNuPurP2g== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/middleware-logger@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.876.0.tgz#16ee45f7bcd887badc8f12d80eef9ba18a0ac97c" + integrity sha512-cpWJhOuMSyz9oV25Z/CMHCBTgafDCbv7fHR80nlRrPdPZ8ETNsahwRgltXP1QJJ8r3X/c1kwpOR7tc+RabVzNA== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/middleware-recursion-detection@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.873.0.tgz#1f9086542800d355d85332acea7accf1856e408b" + integrity sha512-OtgY8EXOzRdEWR//WfPkA/fXl0+WwE8hq0y9iw2caNyKPtca85dzrrZWnPqyBK/cpImosrpR1iKMYr41XshsCg== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/middleware-user-agent@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.873.0.tgz#a882cd2fb4b992e5b3c2eb0251fd95f44b3dae44" + integrity sha512-gHqAMYpWkPhZLwqB3Yj83JKdL2Vsb64sryo8LN2UdpElpS+0fT4yjqSxKTfp7gkhN6TCIxF24HQgbPk5FMYJWw== + dependencies: + "@aws-sdk/core" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.873.0" + "@smithy/core" "^3.8.0" + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/middleware-user-agent@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.876.0.tgz#207f62b1e9f141ede0a562fd1f1bf90b948a2089" + integrity sha512-FR+8INfnbNv32QDQ5szxkWX6mB/QgezfNyx8LnAh1ErISZMmEFBxXXir+ZOfuV8vsmal1a6cy9qmnMNDaNnaNQ== + dependencies: + "@aws-sdk/core" "3.876.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.873.0" + "@smithy/core" "^3.8.0" + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/nested-clients@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.873.0.tgz#cf9c5c04e25666024d9fc805e768f7b6267ef877" + integrity sha512-yg8JkRHuH/xO65rtmLOWcd9XQhxX1kAonp2CliXT44eA/23OBds6XoheY44eZeHfCTgutDLTYitvy3k9fQY6ZA== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.873.0" + "@aws-sdk/middleware-host-header" "3.873.0" + "@aws-sdk/middleware-logger" "3.873.0" + "@aws-sdk/middleware-recursion-detection" "3.873.0" + "@aws-sdk/middleware-user-agent" "3.873.0" + "@aws-sdk/region-config-resolver" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.873.0" + "@aws-sdk/util-user-agent-browser" "3.873.0" + "@aws-sdk/util-user-agent-node" "3.873.0" + "@smithy/config-resolver" "^4.1.5" + "@smithy/core" "^3.8.0" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/hash-node" "^4.0.5" + "@smithy/invalid-dependency" "^4.0.5" + "@smithy/middleware-content-length" "^4.0.5" + "@smithy/middleware-endpoint" "^4.1.18" + "@smithy/middleware-retry" "^4.1.19" + "@smithy/middleware-serde" "^4.0.9" + "@smithy/middleware-stack" "^4.0.5" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.4.10" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.26" + "@smithy/util-defaults-mode-node" "^4.0.26" + "@smithy/util-endpoints" "^3.0.7" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-retry" "^4.0.7" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + +"@aws-sdk/nested-clients@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.876.0.tgz#73919a21b0d71d9b25b3eb2294f62fc812e79f31" + integrity sha512-R4TZrkM2gUElTsotk8mt3y7iLG8TNi1LL1wgVdEEWSLOYTaFyglGdoNBMtEeP7lmXilaTy00AbYF6BakJvSTHg== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.876.0" + "@aws-sdk/middleware-host-header" "3.873.0" + "@aws-sdk/middleware-logger" "3.876.0" + "@aws-sdk/middleware-recursion-detection" "3.873.0" + "@aws-sdk/middleware-user-agent" "3.876.0" + "@aws-sdk/region-config-resolver" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@aws-sdk/util-endpoints" "3.873.0" + "@aws-sdk/util-user-agent-browser" "3.873.0" + "@aws-sdk/util-user-agent-node" "3.876.0" + "@smithy/config-resolver" "^4.1.5" + "@smithy/core" "^3.8.0" + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/hash-node" "^4.0.5" + "@smithy/invalid-dependency" "^4.0.5" + "@smithy/middleware-content-length" "^4.0.5" + "@smithy/middleware-endpoint" "^4.1.18" + "@smithy/middleware-retry" "^4.1.19" + "@smithy/middleware-serde" "^4.0.9" + "@smithy/middleware-stack" "^4.0.5" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/protocol-http" "^5.1.3" + "@smithy/smithy-client" "^4.4.10" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-body-length-node" "^4.0.0" + "@smithy/util-defaults-mode-browser" "^4.0.26" + "@smithy/util-defaults-mode-node" "^4.0.26" + "@smithy/util-endpoints" "^3.0.7" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-retry" "^4.0.7" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + +"@aws-sdk/region-config-resolver@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.873.0.tgz#9a5ddf8aa5a068d1c728dda3ef7e5b31561f7419" + integrity sha512-q9sPoef+BBG6PJnc4x60vK/bfVwvRWsPgcoQyIra057S/QGjq5VkjvNk6H8xedf6vnKlXNBwq9BaANBXnldUJg== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/types" "^4.3.2" + "@smithy/util-config-provider" "^4.0.0" + "@smithy/util-middleware" "^4.0.5" + tslib "^2.6.2" + +"@aws-sdk/token-providers@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.873.0.tgz#43e239d1e6de7d1aa059accfe948a5d8d430b74b" + integrity sha512-BWOCeFeV/Ba8fVhtwUw/0Hz4wMm9fjXnMb4Z2a5he/jFlz5mt1/rr6IQ4MyKgzOaz24YrvqsJW2a0VUKOaYDvg== + dependencies: + "@aws-sdk/core" "3.873.0" + "@aws-sdk/nested-clients" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/token-providers@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.876.0.tgz#952d62cfe53c9964bb2a6db687698279fe22fd57" + integrity sha512-iU08kaQbhXnY0CC2TBcr7y/2PqPwZP2CTWX/Rbq0NvhOyteikfh7ASC+bRfLUp0XMSHKvSb+w2dh8a0lvx4oHg== + dependencies: + "@aws-sdk/core" "3.876.0" + "@aws-sdk/nested-clients" "3.876.0" + "@aws-sdk/types" "3.862.0" + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/types@3.862.0", "@aws-sdk/types@^3.222.0": + version "3.862.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.862.0.tgz#2f5622e1aa3a5281d4f419f5d2c90f87dd5ff0cf" + integrity sha512-Bei+RL0cDxxV+lW2UezLbCYYNeJm6Nzee0TpW0FfyTRBhH9C1XQh4+x+IClriXvgBnRquTMMYsmJfvx8iyLKrg== + dependencies: + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/util-endpoints@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.873.0.tgz#11afbcf503bdbcf10c0ed08c81696303b8bb5fb0" + integrity sha512-YByHrhjxYdjKRf/RQygRK1uh0As1FIi9+jXTcIEX/rBgN8mUByczr2u4QXBzw7ZdbdcOBMOkPnLRjNOWW1MkFg== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-endpoints" "^3.0.7" + tslib "^2.6.2" + +"@aws-sdk/util-locate-window@^3.0.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.873.0.tgz#cc10edef3b7aecf365943ec657116d6eb470d9cb" + integrity sha512-xcVhZF6svjM5Rj89T1WzkjQmrTF6dpR2UvIHPMTnSZoNe6CixejPZ6f0JJ2kAhO8H+dUHwNBlsUgOTIKiK/Syg== + dependencies: + tslib "^2.6.2" + +"@aws-sdk/util-user-agent-browser@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.873.0.tgz#0fcc3c1877ae74aa692cc0b4ad874bc9a6ee1ad6" + integrity sha512-AcRdbK6o19yehEcywI43blIBhOCSo6UgyWcuOJX5CFF8k39xm1ILCjQlRRjchLAxWrm0lU0Q7XV90RiMMFMZtA== + dependencies: + "@aws-sdk/types" "3.862.0" + "@smithy/types" "^4.3.2" + bowser "^2.11.0" + tslib "^2.6.2" + +"@aws-sdk/util-user-agent-node@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.873.0.tgz#3ebc4a7460eb10aba213d9ffc04aeb1bd613a7e1" + integrity sha512-9MivTP+q9Sis71UxuBaIY3h5jxH0vN3/ZWGxO8ADL19S2OIfknrYSAfzE5fpoKROVBu0bS4VifHOFq4PY1zsxw== + dependencies: + "@aws-sdk/middleware-user-agent" "3.873.0" + "@aws-sdk/types" "3.862.0" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/util-user-agent-node@3.876.0": + version "3.876.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.876.0.tgz#0300f97d6a67bb451c9bc7d59fc35976ad311189" + integrity sha512-/ZIaeUt60JBdI0mNc7sZ8v3Tuzp8Pbe4gIAYnppGyF4KV8QA+Yu8tp2bGHfkKn150t1uvQ6P/4CwFfoGF34dzg== + dependencies: + "@aws-sdk/middleware-user-agent" "3.876.0" + "@aws-sdk/types" "3.862.0" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@aws-sdk/xml-builder@3.873.0": + version "3.873.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.873.0.tgz#b5a3acfdeecfc1b7fee8a7773cb2a45590eb5701" + integrity sha512-kLO7k7cGJ6KaHiExSJWojZurF7SnGMDHXRuQunFnEoD0n1yB6Lqy/S/zHiQ7oJnBhPr9q0TW9qFkrsZb1Uc54w== + dependencies: + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" @@ -576,6 +1311,458 @@ dependencies: "@sinonjs/commons" "^3.0.0" +"@smithy/abort-controller@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-4.0.5.tgz#2872a12d0f11dfdcc4254b39566d5f24ab26a4ab" + integrity sha512-jcrqdTQurIrBbUm4W2YdLVMQDoL0sA9DTxYd2s+R/y+2U9NLOP7Xf/YqfSg1FZhlZIYEnvk2mwbyvIfdLEPo8g== + dependencies: + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/config-resolver@^4.1.5": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-4.1.5.tgz#3cb7cde8d13ca64630e5655812bac9ffe8182469" + integrity sha512-viuHMxBAqydkB0AfWwHIdwf/PRH2z5KHGUzqyRtS/Wv+n3IHI993Sk76VCA7dD/+GzgGOmlJDITfPcJC1nIVIw== + dependencies: + "@smithy/node-config-provider" "^4.1.4" + "@smithy/types" "^4.3.2" + "@smithy/util-config-provider" "^4.0.0" + "@smithy/util-middleware" "^4.0.5" + tslib "^2.6.2" + +"@smithy/core@^3.8.0": + version "3.8.0" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.8.0.tgz#321d03564b753025b92e4476579efcd5c505ab1f" + integrity sha512-EYqsIYJmkR1VhVE9pccnk353xhs+lB6btdutJEtsp7R055haMJp2yE16eSxw8fv+G0WUY6vqxyYOP8kOqawxYQ== + dependencies: + "@smithy/middleware-serde" "^4.0.9" + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-body-length-browser" "^4.0.0" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-stream" "^4.2.4" + "@smithy/util-utf8" "^4.0.0" + "@types/uuid" "^9.0.1" + tslib "^2.6.2" + uuid "^9.0.1" + +"@smithy/credential-provider-imds@^4.0.7": + version "4.0.7" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.7.tgz#d8bb566ffd8d9e556810b83d6e0b01b39036b810" + integrity sha512-dDzrMXA8d8riFNiPvytxn0mNwR4B3h8lgrQ5UjAGu6T9z/kRg/Xncf4tEQHE/+t25sY8IH3CowcmWi+1U5B1Gw== + dependencies: + "@smithy/node-config-provider" "^4.1.4" + "@smithy/property-provider" "^4.0.5" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + tslib "^2.6.2" + +"@smithy/eventstream-codec@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-4.0.5.tgz#e742a4badaaf985ac9abcf4283ff4c39d7e48438" + integrity sha512-miEUN+nz2UTNoRYRhRqVTJCx7jMeILdAurStT2XoS+mhokkmz1xAPp95DFW9Gxt4iF2VBqpeF9HbTQ3kY1viOA== + dependencies: + "@aws-crypto/crc32" "5.2.0" + "@smithy/types" "^4.3.2" + "@smithy/util-hex-encoding" "^4.0.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-browser@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.5.tgz#fbebe76edf542d656fe3b187ac6b1e47a63f735f" + integrity sha512-LCUQUVTbM6HFKzImYlSB9w4xafZmpdmZsOh9rIl7riPC3osCgGFVP+wwvYVw6pXda9PPT9TcEZxaq3XE81EdJQ== + dependencies: + "@smithy/eventstream-serde-universal" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/eventstream-serde-config-resolver@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.1.3.tgz#59a01611feaef9830da592bf726ee8eef4f2c11d" + integrity sha512-yTTzw2jZjn/MbHu1pURbHdpjGbCuMHWncNBpJnQAPxOVnFUAbSIUSwafiphVDjNV93TdBJWmeVAds7yl5QCkcA== + dependencies: + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/eventstream-serde-node@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.5.tgz#44f962898cfb3de806725ea5d88e904c7f3955d7" + integrity sha512-lGS10urI4CNzz6YlTe5EYG0YOpsSp3ra8MXyco4aqSkQDuyZPIw2hcaxDU82OUVtK7UY9hrSvgWtpsW5D4rb4g== + dependencies: + "@smithy/eventstream-serde-universal" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/eventstream-serde-universal@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.5.tgz#ec34b9999c7db3e057d67acb14ec0c8627c7ae2e" + integrity sha512-JFnmu4SU36YYw3DIBVao3FsJh4Uw65vVDIqlWT4LzR6gXA0F3KP0IXFKKJrhaVzCBhAuMsrUUaT5I+/4ZhF7aw== + dependencies: + "@smithy/eventstream-codec" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/fetch-http-handler@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.1.1.tgz#a444c99bffdf314deb447370429cc3e719f1a866" + integrity sha512-61WjM0PWmZJR+SnmzaKI7t7G0UkkNFboDpzIdzSoy7TByUzlxo18Qlh9s71qug4AY4hlH/CwXdubMtkcNEb/sQ== + dependencies: + "@smithy/protocol-http" "^5.1.3" + "@smithy/querystring-builder" "^4.0.5" + "@smithy/types" "^4.3.2" + "@smithy/util-base64" "^4.0.0" + tslib "^2.6.2" + +"@smithy/hash-node@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-4.0.5.tgz#16cf8efe42b8b611b1f56f78464b97b27ca6a3ec" + integrity sha512-cv1HHkKhpyRb6ahD8Vcfb2Hgz67vNIXEp2vnhzfxLFGRukLCNEA5QdsorbUEzXma1Rco0u3rx5VTqbM06GcZqQ== + dependencies: + "@smithy/types" "^4.3.2" + "@smithy/util-buffer-from" "^4.0.0" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + +"@smithy/invalid-dependency@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-4.0.5.tgz#ed88e209668266b09c4b501f9bd656728b5ece60" + integrity sha512-IVnb78Qtf7EJpoEVo7qJ8BEXQwgC4n3igeJNNKEj/MLYtapnx8A67Zt/J3RXAj2xSO1910zk0LdFiygSemuLow== + dependencies: + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/is-array-buffer@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" + integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== + dependencies: + tslib "^2.6.2" + +"@smithy/is-array-buffer@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz#55a939029321fec462bcc574890075cd63e94206" + integrity sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw== + dependencies: + tslib "^2.6.2" + +"@smithy/middleware-content-length@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-4.0.5.tgz#c5d6e47f5a9fbba20433602bec9bffaeeb821ff3" + integrity sha512-l1jlNZoYzoCC7p0zCtBDE5OBXZ95yMKlRlftooE5jPWQn4YBPLgsp+oeHp7iMHaTGoUdFqmHOPa8c9G3gBsRpQ== + dependencies: + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/middleware-endpoint@^4.1.18": + version "4.1.18" + resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.18.tgz#81b2f85e3c72b0f1a2d8776e01b0a2968af62c0a" + integrity sha512-ZhvqcVRPZxnZlokcPaTwb+r+h4yOIOCJmx0v2d1bpVlmP465g3qpVSf7wxcq5zZdu4jb0H4yIMxuPwDJSQc3MQ== + dependencies: + "@smithy/core" "^3.8.0" + "@smithy/middleware-serde" "^4.0.9" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + "@smithy/url-parser" "^4.0.5" + "@smithy/util-middleware" "^4.0.5" + tslib "^2.6.2" + +"@smithy/middleware-retry@^4.1.19": + version "4.1.19" + resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.1.19.tgz#19c013c1a548e1185cc1bfabfab3f498667c9e89" + integrity sha512-X58zx/NVECjeuUB6A8HBu4bhx72EoUz+T5jTMIyeNKx2lf+Gs9TmWPNNkH+5QF0COjpInP/xSpJGJ7xEnAklQQ== + dependencies: + "@smithy/node-config-provider" "^4.1.4" + "@smithy/protocol-http" "^5.1.3" + "@smithy/service-error-classification" "^4.0.7" + "@smithy/smithy-client" "^4.4.10" + "@smithy/types" "^4.3.2" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-retry" "^4.0.7" + "@types/uuid" "^9.0.1" + tslib "^2.6.2" + uuid "^9.0.1" + +"@smithy/middleware-serde@^4.0.9": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.0.9.tgz#71213158bb11c1d632829001ca3f233323fb2a7c" + integrity sha512-uAFFR4dpeoJPGz8x9mhxp+RPjo5wW0QEEIPPPbLXiRRWeCATf/Km3gKIVR5vaP8bN1kgsPhcEeh+IZvUlBv6Xg== + dependencies: + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/middleware-stack@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.0.5.tgz#577050d4c0afe816f1ea85f335b2ef64f73e4328" + integrity sha512-/yoHDXZPh3ocRVyeWQFvC44u8seu3eYzZRveCMfgMOBcNKnAmOvjbL9+Cp5XKSIi9iYA9PECUuW2teDAk8T+OQ== + dependencies: + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/node-config-provider@^4.1.4": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.1.4.tgz#42f231b7027e5a7ce003fd80180e586fe814944a" + integrity sha512-+UDQV/k42jLEPPHSn39l0Bmc4sB1xtdI9Gd47fzo/0PbXzJ7ylgaOByVjF5EeQIumkepnrJyfx86dPa9p47Y+w== + dependencies: + "@smithy/property-provider" "^4.0.5" + "@smithy/shared-ini-file-loader" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/node-http-handler@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.1.1.tgz#dd806d9e08b6e73125040dd0808ab56d16a178e9" + integrity sha512-RHnlHqFpoVdjSPPiYy/t40Zovf3BBHc2oemgD7VsVTFFZrU5erFFe0n52OANZZ/5sbshgD93sOh5r6I35Xmpaw== + dependencies: + "@smithy/abort-controller" "^4.0.5" + "@smithy/protocol-http" "^5.1.3" + "@smithy/querystring-builder" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/property-provider@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.0.5.tgz#d3b368b31d5b130f4c30cc0c91f9ebb28d9685fc" + integrity sha512-R/bswf59T/n9ZgfgUICAZoWYKBHcsVDurAGX88zsiUtOTA/xUAPyiT+qkNCPwFn43pZqN84M4MiUsbSGQmgFIQ== + dependencies: + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/protocol-http@^5.1.3": + version "5.1.3" + resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.1.3.tgz#86855b528c0e4cb9fa6fb4ed6ba3cdf5960f88f4" + integrity sha512-fCJd2ZR7D22XhDY0l+92pUag/7je2BztPRQ01gU5bMChcyI0rlly7QFibnYHzcxDvccMjlpM/Q1ev8ceRIb48w== + dependencies: + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/querystring-builder@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.0.5.tgz#158ae170f8ec2d8af6b84cdaf774205a7dfacf68" + integrity sha512-NJeSCU57piZ56c+/wY+AbAw6rxCCAOZLCIniRE7wqvndqxcKKDOXzwWjrY7wGKEISfhL9gBbAaWWgHsUGedk+A== + dependencies: + "@smithy/types" "^4.3.2" + "@smithy/util-uri-escape" "^4.0.0" + tslib "^2.6.2" + +"@smithy/querystring-parser@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.0.5.tgz#95706e56aa769f09dc8922d1b19ffaa06946e252" + integrity sha512-6SV7md2CzNG/WUeTjVe6Dj8noH32r4MnUeFKZrnVYsQxpGSIcphAanQMayi8jJLZAWm6pdM9ZXvKCpWOsIGg0w== + dependencies: + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/service-error-classification@^4.0.7": + version "4.0.7" + resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-4.0.7.tgz#24072198a8c110d29677762162a5096e29eb4862" + integrity sha512-XvRHOipqpwNhEjDf2L5gJowZEm5nsxC16pAZOeEcsygdjv9A2jdOh3YoDQvOXBGTsaJk6mNWtzWalOB9976Wlg== + dependencies: + "@smithy/types" "^4.3.2" + +"@smithy/shared-ini-file-loader@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.5.tgz#8d8a493276cd82a7229c755bef8d375256c5ebb9" + integrity sha512-YVVwehRDuehgoXdEL4r1tAAzdaDgaC9EQvhK0lEbfnbrd0bd5+CTQumbdPryX3J2shT7ZqQE+jPW4lmNBAB8JQ== + dependencies: + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/signature-v4@^5.1.3": + version "5.1.3" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.1.3.tgz#92a4f6e9ce66730eeb0d996cd0478c5cbaf5b3f5" + integrity sha512-mARDSXSEgllNzMw6N+mC+r1AQlEBO3meEAkR/UlfAgnMzJUB3goRBWgip1EAMG99wh36MDqzo86SfIX5Y+VEaw== + dependencies: + "@smithy/is-array-buffer" "^4.0.0" + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + "@smithy/util-hex-encoding" "^4.0.0" + "@smithy/util-middleware" "^4.0.5" + "@smithy/util-uri-escape" "^4.0.0" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + +"@smithy/smithy-client@^4.4.10": + version "4.4.10" + resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.4.10.tgz#c4b49c1d1ff9eb813f88f1e425a5dfac25a03180" + integrity sha512-iW6HjXqN0oPtRS0NK/zzZ4zZeGESIFcxj2FkWed3mcK8jdSdHzvnCKXSjvewESKAgGKAbJRA+OsaqKhkdYRbQQ== + dependencies: + "@smithy/core" "^3.8.0" + "@smithy/middleware-endpoint" "^4.1.18" + "@smithy/middleware-stack" "^4.0.5" + "@smithy/protocol-http" "^5.1.3" + "@smithy/types" "^4.3.2" + "@smithy/util-stream" "^4.2.4" + tslib "^2.6.2" + +"@smithy/types@^4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.3.2.tgz#66ac513e7057637de262e41ac15f70cf464c018a" + integrity sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw== + dependencies: + tslib "^2.6.2" + +"@smithy/url-parser@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.0.5.tgz#1824a9c108b85322c5a31f345f608d47d06f073a" + integrity sha512-j+733Um7f1/DXjYhCbvNXABV53NyCRRA54C7bNEIxNPs0YjfRxeMKjjgm2jvTYrciZyCjsicHwQ6Q0ylo+NAUw== + dependencies: + "@smithy/querystring-parser" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/util-base64@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-4.0.0.tgz#8345f1b837e5f636e5f8470c4d1706ae0c6d0358" + integrity sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg== + dependencies: + "@smithy/util-buffer-from" "^4.0.0" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + +"@smithy/util-body-length-browser@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz#965d19109a4b1e5fe7a43f813522cce718036ded" + integrity sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA== + dependencies: + tslib "^2.6.2" + +"@smithy/util-body-length-node@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz#3db245f6844a9b1e218e30c93305bfe2ffa473b3" + integrity sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg== + dependencies: + tslib "^2.6.2" + +"@smithy/util-buffer-from@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b" + integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA== + dependencies: + "@smithy/is-array-buffer" "^2.2.0" + tslib "^2.6.2" + +"@smithy/util-buffer-from@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz#b23b7deb4f3923e84ef50c8b2c5863d0dbf6c0b9" + integrity sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug== + dependencies: + "@smithy/is-array-buffer" "^4.0.0" + tslib "^2.6.2" + +"@smithy/util-config-provider@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz#e0c7c8124c7fba0b696f78f0bd0ccb060997d45e" + integrity sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w== + dependencies: + tslib "^2.6.2" + +"@smithy/util-defaults-mode-browser@^4.0.26": + version "4.0.26" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.26.tgz#fc04cd466bbb0d80e41930af8d6a8c33c48490f2" + integrity sha512-xgl75aHIS/3rrGp7iTxQAOELYeyiwBu+eEgAk4xfKwJJ0L8VUjhO2shsDpeil54BOFsqmk5xfdesiewbUY5tKQ== + dependencies: + "@smithy/property-provider" "^4.0.5" + "@smithy/smithy-client" "^4.4.10" + "@smithy/types" "^4.3.2" + bowser "^2.11.0" + tslib "^2.6.2" + +"@smithy/util-defaults-mode-node@^4.0.26": + version "4.0.26" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.26.tgz#adfee8c54301ec4cbabed58cd604995a81b4a8dc" + integrity sha512-z81yyIkGiLLYVDetKTUeCZQ8x20EEzvQjrqJtb/mXnevLq2+w3XCEWTJ2pMp401b6BkEkHVfXb/cROBpVauLMQ== + dependencies: + "@smithy/config-resolver" "^4.1.5" + "@smithy/credential-provider-imds" "^4.0.7" + "@smithy/node-config-provider" "^4.1.4" + "@smithy/property-provider" "^4.0.5" + "@smithy/smithy-client" "^4.4.10" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/util-endpoints@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.0.7.tgz#9d52f2e7e7a1ea4814ae284270a5f1d3930b3773" + integrity sha512-klGBP+RpBp6V5JbrY2C/VKnHXn3d5V2YrifZbmMY8os7M6m8wdYFoO6w/fe5VkP+YVwrEktW3IWYaSQVNZJ8oQ== + dependencies: + "@smithy/node-config-provider" "^4.1.4" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/util-hex-encoding@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz#dd449a6452cffb37c5b1807ec2525bb4be551e8d" + integrity sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw== + dependencies: + tslib "^2.6.2" + +"@smithy/util-middleware@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-4.0.5.tgz#405caf2a66e175ce8ca6c747fa1245b3f5386879" + integrity sha512-N40PfqsZHRSsByGB81HhSo+uvMxEHT+9e255S53pfBw/wI6WKDI7Jw9oyu5tJTLwZzV5DsMha3ji8jk9dsHmQQ== + dependencies: + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/util-retry@^4.0.7": + version "4.0.7" + resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-4.0.7.tgz#3169450193e917da170a87557fcbdfe0faa86779" + integrity sha512-TTO6rt0ppK70alZpkjwy+3nQlTiqNfoXja+qwuAchIEAIoSZW8Qyd76dvBv3I5bCpE38APafG23Y/u270NspiQ== + dependencies: + "@smithy/service-error-classification" "^4.0.7" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + +"@smithy/util-stream@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.2.4.tgz#fa9f0e2fd5a8a5adbd013066b475ea8f9d4f900f" + integrity sha512-vSKnvNZX2BXzl0U2RgCLOwWaAP9x/ddd/XobPK02pCbzRm5s55M53uwb1rl/Ts7RXZvdJZerPkA+en2FDghLuQ== + dependencies: + "@smithy/fetch-http-handler" "^5.1.1" + "@smithy/node-http-handler" "^4.1.1" + "@smithy/types" "^4.3.2" + "@smithy/util-base64" "^4.0.0" + "@smithy/util-buffer-from" "^4.0.0" + "@smithy/util-hex-encoding" "^4.0.0" + "@smithy/util-utf8" "^4.0.0" + tslib "^2.6.2" + +"@smithy/util-uri-escape@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz#a96c160c76f3552458a44d8081fade519d214737" + integrity sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg== + dependencies: + tslib "^2.6.2" + +"@smithy/util-utf8@^2.0.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5" + integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== + dependencies: + "@smithy/util-buffer-from" "^2.2.0" + tslib "^2.6.2" + +"@smithy/util-utf8@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-4.0.0.tgz#09ca2d9965e5849e72e347c130f2a29d5c0c863c" + integrity sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow== + dependencies: + "@smithy/util-buffer-from" "^4.0.0" + tslib "^2.6.2" + +"@smithy/util-waiter@^4.0.7": + version "4.0.7" + resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-4.0.7.tgz#c013cf6a5918c21f8b430b4a825dbac132163f4a" + integrity sha512-mYqtQXPmrwvUljaHyGxYUIIRI3qjBTEb/f5QFi3A6VlxhpmZd5mWXn9W+qUkf2pVE1Hv3SqxefiZOPGdxmO64A== + dependencies: + "@smithy/abort-controller" "^4.0.5" + "@smithy/types" "^4.3.2" + tslib "^2.6.2" + "@tsconfig/node10@^1.0.7": version "1.0.11" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" @@ -596,6 +1783,11 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== +"@types/aws-lambda@^8.10.152": + version "8.10.152" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.152.tgz#f68424a8175f0a54a2a941e65b76c3f51f3bd89d" + integrity sha512-soT/c2gYBnT5ygwiHPmd9a1bftj462NWVk2tKCc1PYHSIacB2UwbTS2zYG4jzag1mRDuzg/OjtxQjQ2NKRB6Rw== + "@types/babel__core@^7.1.14": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" @@ -700,6 +1892,11 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== +"@types/uuid@^9.0.1": + version "9.0.8" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" + integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== + "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" @@ -921,6 +2118,11 @@ bluebird@^3.7.2: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +bowser@^2.11.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.12.1.tgz#f9ad78d7aebc472feb63dd9635e3ce2337e0e2c1" + integrity sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw== + brace-expansion@^1.1.7: version "1.1.12" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" @@ -1394,6 +2596,13 @@ fast-uri@^3.0.1: resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== +fast-xml-parser@5.2.5: + version "5.2.5" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz#4809fdfb1310494e341098c25cb1341a01a9144a" + integrity sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ== + dependencies: + strnum "^2.1.0" + fb-watchman@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" @@ -3050,6 +4259,11 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strnum@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.1.1.tgz#cf2a6e0cf903728b8b2c4b971b7e36b4e82d46ab" + integrity sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw== + supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -3135,6 +4349,11 @@ ts-node@^10.9.2: v8-compile-cache-lib "^3.0.1" yn "3.1.1" +tslib@^2.6.2: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + type-detect@4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -3248,6 +4467,11 @@ update-browserslist-db@^1.1.3: escalade "^3.2.0" picocolors "^1.1.1" +uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" From 9e84c181a042eeac697784d340e90de1096b1f6b Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 26 Aug 2025 21:25:06 -0700 Subject: [PATCH 056/217] fix: SecretStringChanged --- cdk/lib/doorway_environment/restart-services-lambda.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts index cf93e0d16a8..b0a77910152 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -29,7 +29,7 @@ export class RestartServicesLambda { source: ["aws.secretsmanager"], detail: { "eventSource": ["secretsmanager.amazonaws.com"], - "eventName": ["PutSecretValue"], + "eventName": ["SecretStringChanged"], "secret-id": secretArns, }, From 64d3633dc9ebb757c3992d1460438ec98ea5b086 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 27 Aug 2025 09:16:45 -0700 Subject: [PATCH 057/217] fix: event pattern detail --- .../restart-services-lambda.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts index b0a77910152..ee2ae69ffaa 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -26,14 +26,18 @@ export class RestartServicesLambda { } const secretRule = new Rule(scope, `${id}-secret-change-rule`, { eventPattern: { - source: ["aws.secretsmanager"], - detail: { + "source": ["aws.cloudtrail"], + "detail": { "eventSource": ["secretsmanager.amazonaws.com"], - "eventName": ["SecretStringChanged"], - "secret-id": secretArns, + "eventName": ["PutSecretValue"], + "requestParameters": { + "secretId": [ + secretArns + ] + } + } + } - }, - }, }); const dwLambda = new DoorwayLambdaBaseClass(scope, `${id}-restart-lambda`, { name: `${id}-function`, From 67a396a5cde973fff04a37e0c29322c65261a0c2 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 27 Aug 2025 09:32:56 -0700 Subject: [PATCH 058/217] fix: don't need to put a list in a list! --- cdk/lib/doorway_environment/restart-services-lambda.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts index ee2ae69ffaa..a712a14929e 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -31,9 +31,8 @@ export class RestartServicesLambda { "eventSource": ["secretsmanager.amazonaws.com"], "eventName": ["PutSecretValue"], "requestParameters": { - "secretId": [ - secretArns - ] + "secretId": secretArns + } } } From 9aaff391dff2a3fddc56291c09acd65da5ce86df Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 27 Aug 2025 10:11:27 -0700 Subject: [PATCH 059/217] fix: Pipeline takes public and partners sites based on environments --- cdk/lib/build-pipeline/doorway-build-job-base.ts | 1 + cdk/lib/doorway_environment/doorway-public-site.ts | 12 ++---------- ci/buildspec/build_import_listings.yml | 4 ++-- ci/buildspec/build_public.yml | 7 ++++--- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-job-base.ts b/cdk/lib/build-pipeline/doorway-build-job-base.ts index 5bd96dabdfc..59aed0ba886 100644 --- a/cdk/lib/build-pipeline/doorway-build-job-base.ts +++ b/cdk/lib/build-pipeline/doorway-build-job-base.ts @@ -77,6 +77,7 @@ export class DoorwayBuildJobBase { ECR_ACCOUNT_ID: { value: Aws.ACCOUNT_ID }, ECR_NAMESPACE: { value: props.ecrNamespace || "doorway" }, PGDATABASE: { value: props.databaseName || "bloom" }, + ENVIRONMENT: { value: props.environment }, }, role: buildRole, } diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts index 4dd9a43cd42..6b5c4b24707 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -6,8 +6,8 @@ import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" -import { DoorwayService } from "./doorway_service" import { DoorwayProps } from "./doorway-props" +import { DoorwayService } from "./doorway_service" import { RestartServicesLambda } from "./restart-services-lambda" export class DoorwayPublicSite { @@ -89,7 +89,7 @@ export class DoorwayPublicSite { logGroup: props.logGroup, apiTargetDomainName: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, apiTargetPort: Number(process.env.BACKEND_API_PORT || 3000), - container: `doorway/public:run-${process.env.CODEBUILD_RESOLVED_SOURCE_VERSION?.substring(0, 8) || "candidate"}`, + container: `doorway/public:run-${process.env.ENVIRONMENT || "dev2"}-candidate`, securityGroup: privateSG }).service @@ -98,13 +98,5 @@ export class DoorwayPublicSite { secrets: secrets, environment: props.environment, }) - - - - - - - - } } diff --git a/ci/buildspec/build_import_listings.yml b/ci/buildspec/build_import_listings.yml index 59d1cd3572c..b5003854ee7 100644 --- a/ci/buildspec/build_import_listings.yml +++ b/ci/buildspec/build_import_listings.yml @@ -11,8 +11,8 @@ phases: - export QUALIFIED_IMAGE_NAME="${ECR_NAMESPACE}/${IMAGE_NAME}" - export TEST_IMAGE="${QUALIFIED_IMAGE_NAME}:test" - export RUN_IMAGE="${QUALIFIED_IMAGE_NAME}:run" - - export ECR_GIT_TAG="${ECR_URL}/${QUALIFIED_IMAGE_NAME}:run-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" - - export ECR_RUN_TAG="${ECR_URL}/${QUALIFIED_IMAGE_NAME}:run-candidate" + - export ECR_GIT_TAG="${ECR_URL}/${QUALIFIED_IMAGE_NAME}:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + - export ECR_RUN_TAG="${ECR_URL}/${QUALIFIED_IMAGE_NAME}:run-${ENVIRONMENT}-candidate" # Log in to ECR - aws ecr get-login-password --region "${ECR_REGION}" | docker login --username AWS --password-stdin "${ECR_URL}" diff --git a/ci/buildspec/build_public.yml b/ci/buildspec/build_public.yml index b4e30d0d22e..0be21d3b842 100644 --- a/ci/buildspec/build_public.yml +++ b/ci/buildspec/build_public.yml @@ -85,11 +85,12 @@ phases: --build-arg "GOOGLE_MAPS_MAP_ID=${GOOGLE_MAPS_MAP_ID}" --build-arg "SHOW_ALL_MAP_PINS=${SHOW_ALL_MAP_PINS}" --target run - -t sites/public:run-candidate + -t sites/public:run-${ENVIRONMENT}-candidate . # Tag the run image - - docker tag sites/public:run-candidate "${ECR_REPO}/public:run-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + - docker tag sites/public:run-candidate "${ECR_REPO}/public:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" # Push the run image to ECR - - docker push "${ECR_REPO}/public:run-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + - docker push "${ECR_REPO}/public:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + - docker push "${ECR_REPO}/public:run-${ENVIRONMENT}-candidate" From bcddf1dbe5c1f0f12a594a355e8dc1c402d09b2d Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 27 Aug 2025 10:53:45 -0700 Subject: [PATCH 060/217] fix: common lambda creation for all services --- cdk/lib/doorway_environment/doorway-backend-service.ts | 8 +------- cdk/lib/doorway_environment/doorway-public-site.ts | 9 +-------- cdk/lib/doorway_environment/doorway_service.ts | 6 ++++++ 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-backend-service.ts b/cdk/lib/doorway_environment/doorway-backend-service.ts index deecc1775ac..2d79cde7659 100644 --- a/cdk/lib/doorway_environment/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/doorway-backend-service.ts @@ -7,9 +7,8 @@ import * as secret from "aws-cdk-lib/aws-secretsmanager" import { EmailIdentity } from "aws-cdk-lib/aws-ses" import { Construct } from "constructs" -import { DoorwayService } from "./doorway_service" import { DoorwayProps } from "./doorway-props" -import { RestartServicesLambda } from "./restart-services-lambda" +import { DoorwayService } from "./doorway_service" export class DoorwayBackendService { public service: FargateService @@ -160,10 +159,5 @@ export class DoorwayBackendService { container: "doorway/backend:run-candidate", securityGroup: privateSG, }).service - new RestartServicesLambda(scope, `restart-backend-services-${props.environment}`, { - service: this.service, - secrets: secrets, - environment: props.environment, - }) } } diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts index 6b5c4b24707..8afbc078d14 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -8,7 +8,6 @@ import { Construct } from "constructs" import { DoorwayProps } from "./doorway-props" import { DoorwayService } from "./doorway_service" -import { RestartServicesLambda } from "./restart-services-lambda" export class DoorwayPublicSite { public service: FargateService @@ -45,8 +44,6 @@ export class DoorwayPublicSite { stringRepresentation: "inbound-backend", fromPort: inboundPort, toPort: inboundPort - - }), "Allow internal traffic from web app servers") const environmentVariables: { [key: string]: string } = { BACKEND_API_BASE: @@ -93,10 +90,6 @@ export class DoorwayPublicSite { securityGroup: privateSG }).service - new RestartServicesLambda(scope, `restart-public-services-${props.environment}`, { - service: this.service, - secrets: secrets, - environment: props.environment, - }) + } } diff --git a/cdk/lib/doorway_environment/doorway_service.ts b/cdk/lib/doorway_environment/doorway_service.ts index 0abb6947832..fea52145a9a 100644 --- a/cdk/lib/doorway_environment/doorway_service.ts +++ b/cdk/lib/doorway_environment/doorway_service.ts @@ -9,6 +9,7 @@ import { StringParameter } from "aws-cdk-lib/aws-ssm" import { Construct } from "constructs" import { DoorwayServiceProps } from "./doorway-props" +import { RestartServicesLambda } from "./restart-services-lambda" export class DoorwayService { public service: FargateService @@ -169,6 +170,11 @@ export class DoorwayService { serviceConnectConfiguration: serviceConnectProps, }) + new RestartServicesLambda(scope, `restart-public-services-${props.environment}`, { + service: this.service, + secrets: props.secrets, + environment: props.environment, + }) props.logGroup.grantWrite(props.executionRole) } From 355cd23afda9cc24da4d995e13bcf7c5d19cc5ae Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 27 Aug 2025 11:18:37 -0700 Subject: [PATCH 061/217] fix: trying with secret name --- cdk/lib/doorway_environment/restart-services-lambda.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts index a712a14929e..5e92cefee70 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -1,8 +1,8 @@ import { Rule } from "aws-cdk-lib/aws-events"; import { LambdaFunction } from "aws-cdk-lib/aws-events-targets"; import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam"; +import * as secretmgr from "aws-cdk-lib/aws-secretsmanager"; import { Construct } from "constructs"; - import { DoorwayServiceMonitorLambdaProps } from "./doorway-lambda-props"; import { DoorwayLambdaBaseClass } from "./lambda-base-class"; @@ -22,7 +22,9 @@ export class RestartServicesLambda { let secretArns: string[] = []; for (const [key, secret] of Object.entries(props.secrets)) { - secretArns.push(secret.arn); + // The service setup uses the secret definition from ECS. unfortunately it doesn't include the secret name, so we have to re-create the secret from the arn to get the name. + const secretName = secretmgr.Secret.fromSecretCompleteArn(scope, `${id}-secret-${key}`, secret.arn).secretName; + secretArns.push(secretName); } const secretRule = new Rule(scope, `${id}-secret-change-rule`, { eventPattern: { From ca374d20563766528e7c31e2e955932aa224f6f1 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 27 Aug 2025 11:53:13 -0700 Subject: [PATCH 062/217] fix: fixing docker tags --- ci/buildspec/build_partners.yml | 6 +++--- ci/buildspec/build_public.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ci/buildspec/build_partners.yml b/ci/buildspec/build_partners.yml index c6c8a6886d9..d80aa58ccbc 100644 --- a/ci/buildspec/build_partners.yml +++ b/ci/buildspec/build_partners.yml @@ -64,10 +64,10 @@ phases: --build-arg "LOTTERY_DAYS_TILL_EXPIRY=${LOTTERY_DAYS_TILL_EXPIRY}" --build-arg "LIMIT_CLOSED_LISTING_ACTIONS=${LIMIT_CLOSED_LISTING_ACTIONS}" --build-arg "USE_SECURE_DOWNLOAD_PATHWAY=${USE_SECURE_DOWNLOAD_PATHWAY}" - -t sites/partners:run-candidate + -t sites/partners:run-${ENVIRONMENT}-candidate # Tag the run image - - docker tag sites/partners:run-candidate "${ECR_REPO}/partners:run-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + - docker tag sites/partners:run-${ENVIRONMENT}-candidate "${ECR_REPO}/partners:run-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" # Push the run image to ECR - - docker push "${ECR_REPO}/partners:run-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + - docker push "${ECR_REPO}/partners:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" diff --git a/ci/buildspec/build_public.yml b/ci/buildspec/build_public.yml index 0be21d3b842..4931cf00e33 100644 --- a/ci/buildspec/build_public.yml +++ b/ci/buildspec/build_public.yml @@ -89,7 +89,7 @@ phases: . # Tag the run image - - docker tag sites/public:run-candidate "${ECR_REPO}/public:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + - docker tag sites/public:run-${ENVIRONMENT}-candidate "${ECR_REPO}/public:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" # Push the run image to ECR - docker push "${ECR_REPO}/public:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" From 032139133301669242024b7fb0782ae0b37b5925 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 27 Aug 2025 13:54:40 -0700 Subject: [PATCH 063/217] fix: pipeline and dockertag fixes --- cdk/lib/doorway_environment/doorway_service.ts | 2 +- cdk/lib/doorway_environment/restart-services-lambda.ts | 6 +++++- ci/buildspec/build_partners.yml | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway_service.ts b/cdk/lib/doorway_environment/doorway_service.ts index fea52145a9a..4035a7daeaf 100644 --- a/cdk/lib/doorway_environment/doorway_service.ts +++ b/cdk/lib/doorway_environment/doorway_service.ts @@ -170,7 +170,7 @@ export class DoorwayService { serviceConnectConfiguration: serviceConnectProps, }) - new RestartServicesLambda(scope, `restart-public-services-${props.environment}`, { + new RestartServicesLambda(scope, `restart-${id}-${props.environment}`, { service: this.service, secrets: props.secrets, environment: props.environment, diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts index 5e92cefee70..f82acca62c6 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -20,10 +20,14 @@ export interface ServiceRestartDetail { export class RestartServicesLambda { constructor(scope: Construct, id: string, props: DoorwayServiceMonitorLambdaProps) { + + let secretArns: string[] = []; + for (const [key, secret] of Object.entries(props.secrets)) { + console.log("Processing secret: ", key, secret); // The service setup uses the secret definition from ECS. unfortunately it doesn't include the secret name, so we have to re-create the secret from the arn to get the name. - const secretName = secretmgr.Secret.fromSecretCompleteArn(scope, `${id}-secret-${key}`, secret.arn).secretName; + const secretName = secretmgr.Secret.fromSecretPartialArn(scope, `${id}-secret-${key}`, secret.arn).secretName; secretArns.push(secretName); } const secretRule = new Rule(scope, `${id}-secret-change-rule`, { diff --git a/ci/buildspec/build_partners.yml b/ci/buildspec/build_partners.yml index d80aa58ccbc..cca62a97026 100644 --- a/ci/buildspec/build_partners.yml +++ b/ci/buildspec/build_partners.yml @@ -67,7 +67,7 @@ phases: -t sites/partners:run-${ENVIRONMENT}-candidate # Tag the run image - - docker tag sites/partners:run-${ENVIRONMENT}-candidate "${ECR_REPO}/partners:run-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + - docker tag sites/partners:run-${ENVIRONMENT}-candidate "${ECR_REPO}/partners:run${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" # Push the run image to ECR - docker push "${ECR_REPO}/partners:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" From 053eb9949a692780b9242e259da51417642e9da7 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 27 Aug 2025 14:27:05 -0700 Subject: [PATCH 064/217] fix: sigh more docker fixes --- cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 6 +++++- cdk/lib/build-pipeline/doorway-docker-build.ts | 2 ++ ci/buildspec/build_partners.yml | 5 ++++- ci/buildspec/build_public.yml | 1 + 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 4cb438e99b6..027dd508136 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -107,7 +107,7 @@ export class DoorwayBuildPipelineStack extends Stack { actions: [doorwaySource], }) pipeline.addStage({ - stageName: "Build", + stageName: "Build-Dev", actions: [ new DoorwayDockerBuild(this, `doorway-backend`, { buildspec: "../ci/buildspec/build_backend.yml", @@ -115,6 +115,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, + environment: "dev2" }).action, new DoorwayDockerBuild(this, "doorway-import-listings", { buildspec: "../ci/buildspec/build_import_listings.yml", @@ -122,6 +123,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, + environment: "dev2" }).action, new DoorwayDockerBuild(this, `doorway-partners`, { buildspec: "../ci/buildspec/build_partners.yml", @@ -129,6 +131,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, + environment: "dev2" }).action, new DoorwayDockerBuild(this, "doorway-public", { buildspec: "../ci/buildspec/build_public.yml", @@ -136,6 +139,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, + environment: "dev2" }).action, ], }) diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts index a6a11da75e8..49af97f938a 100644 --- a/cdk/lib/build-pipeline/doorway-docker-build.ts +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -14,6 +14,7 @@ export interface DoorwayDockerBuildProps { source: Artifact dockerHubSecret: string buildRole: Role + environment: string } export class DoorwayDockerBuild { @@ -40,6 +41,7 @@ export class DoorwayDockerBuild { ECR_NAMESPACE: { value: "doorway" }, IMAGE_NAME: { value: props.imageName }, DOCKER_HUB_SECRET_ARN: { value: props.dockerHubSecret }, + ENVIRONMENT: { value: props.environment }, }, role: props.buildRole, }) diff --git a/ci/buildspec/build_partners.yml b/ci/buildspec/build_partners.yml index cca62a97026..2edc6fa0a7d 100644 --- a/ci/buildspec/build_partners.yml +++ b/ci/buildspec/build_partners.yml @@ -67,7 +67,10 @@ phases: -t sites/partners:run-${ENVIRONMENT}-candidate # Tag the run image - - docker tag sites/partners:run-${ENVIRONMENT}-candidate "${ECR_REPO}/partners:run${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + - docker tag sites/partners:run-${ENVIRONMENT}-candidate "${ECR_REPO}/partners:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + - docker tag sites/partners:run-${ENVIRONMENT}-candidate "${ECR_REPO}/partners:run-${ENVIRONMENT}-candidate" + # Push the run image to ECR - docker push "${ECR_REPO}/partners:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + - docker push "${ECR_REPO}/partners:run-${ENVIRONMENT}-candidate" diff --git a/ci/buildspec/build_public.yml b/ci/buildspec/build_public.yml index 4931cf00e33..99398eb6069 100644 --- a/ci/buildspec/build_public.yml +++ b/ci/buildspec/build_public.yml @@ -90,6 +90,7 @@ phases: # Tag the run image - docker tag sites/public:run-${ENVIRONMENT}-candidate "${ECR_REPO}/public:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + - docker tag sites/public:run-${ENVIRONMENT}-candidate "${ECR_REPO}/public:run-${ENVIRONMENT}-candidate" # Push the run image to ECR - docker push "${ECR_REPO}/public:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" From 063bd3a4e34cfff7d24e0ae7b5f0cb401b7a154c Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 27 Aug 2025 15:01:44 -0700 Subject: [PATCH 065/217] fix: trying to trick codebuild into thinking something changed --- ci/buildspec/build_partners.yml | 3 +-- ci/buildspec/build_public.yml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ci/buildspec/build_partners.yml b/ci/buildspec/build_partners.yml index 2edc6fa0a7d..bf567b1c2ee 100644 --- a/ci/buildspec/build_partners.yml +++ b/ci/buildspec/build_partners.yml @@ -67,9 +67,8 @@ phases: -t sites/partners:run-${ENVIRONMENT}-candidate # Tag the run image - - docker tag sites/partners:run-${ENVIRONMENT}-candidate "${ECR_REPO}/partners:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" - docker tag sites/partners:run-${ENVIRONMENT}-candidate "${ECR_REPO}/partners:run-${ENVIRONMENT}-candidate" - + - docker tag sites/partners:run-${ENVIRONMENT}-candidate "${ECR_REPO}/partners:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8} # Push the run image to ECR - docker push "${ECR_REPO}/partners:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" diff --git a/ci/buildspec/build_public.yml b/ci/buildspec/build_public.yml index 99398eb6069..ec172d26152 100644 --- a/ci/buildspec/build_public.yml +++ b/ci/buildspec/build_public.yml @@ -93,5 +93,5 @@ phases: - docker tag sites/public:run-${ENVIRONMENT}-candidate "${ECR_REPO}/public:run-${ENVIRONMENT}-candidate" # Push the run image to ECR - - docker push "${ECR_REPO}/public:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" - docker push "${ECR_REPO}/public:run-${ENVIRONMENT}-candidate" + - docker push "${ECR_REPO}/public:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" From 27b61cc79eb04742caae0158d9cb7bd54a971dc9 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 27 Aug 2025 15:12:34 -0700 Subject: [PATCH 066/217] fix: lost quote --- ci/buildspec/build_partners.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/buildspec/build_partners.yml b/ci/buildspec/build_partners.yml index bf567b1c2ee..fbc4a683483 100644 --- a/ci/buildspec/build_partners.yml +++ b/ci/buildspec/build_partners.yml @@ -68,7 +68,7 @@ phases: # Tag the run image - docker tag sites/partners:run-${ENVIRONMENT}-candidate "${ECR_REPO}/partners:run-${ENVIRONMENT}-candidate" - - docker tag sites/partners:run-${ENVIRONMENT}-candidate "${ECR_REPO}/partners:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8} + - docker tag sites/partners:run-${ENVIRONMENT}-candidate "${ECR_REPO}/partners:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" # Push the run image to ECR - docker push "${ECR_REPO}/partners:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" From 331a3c5646e5e6000363697a6c1d3a108b6b420a Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 27 Aug 2025 15:56:26 -0700 Subject: [PATCH 067/217] fix: another attempt to fire it --- ci/buildspec/build_partners.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/buildspec/build_partners.yml b/ci/buildspec/build_partners.yml index fbc4a683483..835b84c961c 100644 --- a/ci/buildspec/build_partners.yml +++ b/ci/buildspec/build_partners.yml @@ -73,3 +73,4 @@ phases: # Push the run image to ECR - docker push "${ECR_REPO}/partners:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" - docker push "${ECR_REPO}/partners:run-${ENVIRONMENT}-candidate" + From 73d32a43e0f103142ecc12de4742b3b999fdc081 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 09:44:00 -0700 Subject: [PATCH 068/217] feat: adding partners site --- cdk/dev2.env | 4 +- .../doorway-build-pipeline-stack.ts | 3 +- .../doorway-app-environment-stack.ts | 8 ++ .../doorway-partners-site.ts | 90 +++++++++++++++++++ 4 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 cdk/lib/doorway_environment/doorway-partners-site.ts diff --git a/cdk/dev2.env b/cdk/dev2.env index 39e354711bf..201f9f7c936 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -8,7 +8,7 @@ ECR_ACCOUNT_ID=364076391763 ECR_NAMESPACE=doorway ECR_REGION=us-west-2 -MAPBOX_TOKEN=pk.eyJ1Ijoic2Vhbi1hbGJlcnQtZXh5Z3kiLCJhIjoiY2tybnY5amlyMDNraDJubXc4c3ZhOGswYyJ9.KE5FGb_Tch8BPrOYMvshXw + USE_SECURE_DOWNLOAD_PATHWAY=TRUE GTM_KEY=GTM-KF22FJP SOURCE_REPO_NAMESPACE=doorway @@ -23,7 +23,7 @@ PUBLIC_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY PARTNERS_PORTAL_PORT=3200 PARTNERS_PORTAL_DOMAIN=dev2.housingbayarea.mtc.ca.gov -PARTNERS_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY +PARTNERS_PORTAL_SECRETS=MAPBOX_TOKEN BACKEND_API_BASE=http://backend.dev2.housingbayarea.int:3100 BACKEND_API_PORT=3100 diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 027dd508136..c8e28cb1dd6 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -1,6 +1,6 @@ import { Stack, StackProps } from "aws-cdk-lib" import { Artifact, Pipeline } from "aws-cdk-lib/aws-codepipeline" -import { GitHubSourceAction } from "aws-cdk-lib/aws-codepipeline-actions" +import { GitHubSourceAction, GitHubTrigger } from "aws-cdk-lib/aws-codepipeline-actions" import { PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Secret } from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" @@ -101,6 +101,7 @@ export class DoorwayBuildPipelineStack extends Stack { repo: "doorway", branch: "feat/tf_to_cdk", output: sourceArtifact, + trigger: GitHubTrigger.WEBHOOK, }) pipeline.addStage({ stageName: "Source", diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 215f3f5a1b7..d8d49e88244 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -3,6 +3,7 @@ import { LogGroup } from "aws-cdk-lib/aws-logs" import { Construct } from "constructs" import dotenv from "dotenv" import path from "path" + import { DoorwayBackendService } from "./doorway-backend-service" import { DoorwayPublicLoadBalancer } from "./doorway-public-load-balancer" import { DoorwayPublicSite } from "./doorway-public-site" @@ -41,6 +42,13 @@ export class DoorwayAppEnvironmentStack extends Stack { publicSite.service.node.addDependency(api.service) publicSite.service.node.addDependency(lb.loadBalancer) lb.targetGroup.addTarget(publicSite.service) + const partnersSite = new DoorwayPublicSite(this, `doorway-partners-${environment}`, { + environment: environment, + logGroup: logGroup + }) + partnersSite.service.node.addDependency(api.service) + partnersSite.service.node.addDependency(lb.loadBalancer) + lb.targetGroup.addTarget(partnersSite.service) diff --git a/cdk/lib/doorway_environment/doorway-partners-site.ts b/cdk/lib/doorway_environment/doorway-partners-site.ts new file mode 100644 index 00000000000..4fb37fdd807 --- /dev/null +++ b/cdk/lib/doorway_environment/doorway-partners-site.ts @@ -0,0 +1,90 @@ +import { Fn } from "aws-cdk-lib" +import { ISecurityGroup, Port, Protocol, SecurityGroup } from "aws-cdk-lib/aws-ec2" +import { FargateService, Secret } from "aws-cdk-lib/aws-ecs" +import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" +import { Bucket } from "aws-cdk-lib/aws-s3" +import * as secret from "aws-cdk-lib/aws-secretsmanager" +import { Construct } from "constructs" + +import { DoorwayProps } from "./doorway-props" +import { DoorwayService } from "./doorway_service" + +export class DoorwayPartnersSite { + public service: FargateService + constructor(scope: Construct, id: string, props: DoorwayProps) { + const executionRole = new Role(scope, `executionRole-${id}`, { + assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), + }) + const publicUploads = Bucket.fromBucketArn( + scope, + `partnersUploadsBucket-${id}`, + Fn.importValue(`doorway-public-uploads-${props.environment}`), + ) + const secureUploads = Bucket.fromBucketArn( + scope, + `secureUploadsBucket-${id}`, + Fn.importValue(`doorway-secure-uploads-${props.environment}`), + ) + const port = Number(process.env.PARTNERS_PORTAL_PORT || "3000") + const appTierPrivateSGId = Fn.importValue(`doorway-app-sg-${props.environment}`) + const privateSG: ISecurityGroup = SecurityGroup.fromSecurityGroupId( + scope, + `appTierPrivateSG-${id}`, + appTierPrivateSGId, + ) + const publicSGId = Fn.importValue(`doorway-public-sg-${props.environment}`) + const publicSG: ISecurityGroup = SecurityGroup.fromSecurityGroupId( + scope, + `publicSG-${id}`, + publicSGId, + ) + const inboundPort = Number(process.env.BACKEND_API_PORT || 3000) + privateSG.addIngressRule(publicSG, new Port({ + protocol: Protocol.TCP, + stringRepresentation: "inbound-backend", + fromPort: inboundPort, + toPort: inboundPort + }), "Allow internal traffic from web app servers") + const environmentVariables: { [key: string]: string } = { + BACKEND_API_BASE: + process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, + FEATURE_LISTINGS_APPROVAL: + process.env.FEATURE_LISTINGS_APPROVAL || "TRUE", + LISTINGS_QUERY: process.env.LISTINGS_QUERY || "/listings", + LOG_LEVEL: process.env.LOG_LEVEL || "info", + NEXTJS_PORT: String(port), + MAPBOX_TOKEN: process.env.MAPBOX_TOKEN || "", + NODE_ENV: process.env.NODE_ENV || "development", + SHOW_DUPLICATES: process.env.SHOW_DUPLICATES || "FALSE", + SHOW_LM_LINKS: process.env.SHOW_LM_LINKS || "TRUE", + USE_SECURE_DOWNLOAD_PATHWAY: process.env.USE_SECURE_DOWNLOAD_PATHWAY || "TRUE", + } + const secretNames = (process.env.PARTNERS_PORTAL_SECRETS || "").split(",") + const secrets: { [key: string]: Secret } = {} + secretNames.forEach((secretName) => { + secrets[secretName] = Secret.fromSecretsManager( + secret.Secret.fromSecretNameV2(scope, `${secretName}-${id}`, `/doorway/${props.environment}/${secretName}`), + ) + }) + this.service = new DoorwayService(scope, `${id}-service`, { + ...props, + memory: Number(process.env.PARTNERS_PORTAL_MEMORY || 4096), + cpu: Number(process.env.PARTNERS_PORTAL_CPU || 2), + instances: Number(process.env.PARTNERS_PORTAL_INSTANCES || 3), + port: Number(process.env.PARTNERS_PORTAL_PORT || 3000), + secrets: secrets, + environmentVariables: environmentVariables, + serviceConnectServer: false, + domainName: process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov`, + executionRole: executionRole, + publicUploads: publicUploads, + secureUploads: secureUploads, + logGroup: props.logGroup, + apiTargetDomainName: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, + apiTargetPort: Number(process.env.BACKEND_API_PORT || 3000), + container: `doorway/partners:run-${process.env.ENVIRONMENT || "dev2"}-candidate`, + securityGroup: privateSG + }).service + + } +} From e7967508f5a4055f5d80af2daecb8fed9159d1b8 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 12:42:22 -0700 Subject: [PATCH 069/217] fix: adding parners portal to lb --- .../doorway-public-load-balancer.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index 7b6722ea4a0..61b1590a129 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -105,7 +105,21 @@ export class DoorwayPublicLoadBalancer { }); new ARecord(scope, "PublicARecord", { zone: dnsZone, - recordName: process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`, + recordName: process.env.PUBLIC_PORTAL_DOMAIN || `${props.environment}.housingbayarea.mtc.ca.gov`, + target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), + }); + new ApplicationListenerRule(scope, "PartnersDomainRule", { + listener: httpsListener, + priority: 200, + action: ListenerAction.forward([this.targetGroup]), + conditions: [ListenerCondition.hostHeaders([process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov`]) + ] + + + }); + new ARecord(scope, "PublicARecord", { + zone: dnsZone, + recordName: process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov`, target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), }); From 088f268e23130deaf92cfec43c3983d92f385c48 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 13:19:08 -0700 Subject: [PATCH 070/217] fix: duplicate name --- cdk/lib/doorway_environment/doorway-public-load-balancer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index 61b1590a129..4ed70472fa7 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -117,7 +117,7 @@ export class DoorwayPublicLoadBalancer { }); - new ARecord(scope, "PublicARecord", { + new ARecord(scope, "PartnersARecord", { zone: dnsZone, recordName: process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov`, target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), From 0c3b53a50fc729ee42dc9de99a4bae9633fb33fd Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 13:59:41 -0700 Subject: [PATCH 071/217] fix: dev2 url --- cdk/dev2.env | 2 +- cdk/lib/doorway_environment/restart-services-lambda.ts | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cdk/dev2.env b/cdk/dev2.env index 201f9f7c936..6bbf9fd5acc 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -22,7 +22,7 @@ PUBLIC_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY PARTNERS_PORTAL_PORT=3200 -PARTNERS_PORTAL_DOMAIN=dev2.housingbayarea.mtc.ca.gov +PARTNERS_PORTAL_DOMAIN=partners.dev2.housingbayarea.mtc.ca.gov PARTNERS_PORTAL_SECRETS=MAPBOX_TOKEN BACKEND_API_BASE=http://backend.dev2.housingbayarea.int:3100 diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts index f82acca62c6..cfee04e915a 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -3,6 +3,7 @@ import { LambdaFunction } from "aws-cdk-lib/aws-events-targets"; import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam"; import * as secretmgr from "aws-cdk-lib/aws-secretsmanager"; import { Construct } from "constructs"; + import { DoorwayServiceMonitorLambdaProps } from "./doorway-lambda-props"; import { DoorwayLambdaBaseClass } from "./lambda-base-class"; @@ -27,7 +28,7 @@ export class RestartServicesLambda { for (const [key, secret] of Object.entries(props.secrets)) { console.log("Processing secret: ", key, secret); // The service setup uses the secret definition from ECS. unfortunately it doesn't include the secret name, so we have to re-create the secret from the arn to get the name. - const secretName = secretmgr.Secret.fromSecretPartialArn(scope, `${id}-secret-${key}`, secret.arn).secretName; + const secretName = secretmgr.Secret.fromSecretPartialArn(scope, `${id}-secret-${key}`, secret.arn).secretArn; secretArns.push(secretName); } const secretRule = new Rule(scope, `${id}-secret-change-rule`, { @@ -37,12 +38,10 @@ export class RestartServicesLambda { "eventSource": ["secretsmanager.amazonaws.com"], "eventName": ["PutSecretValue"], "requestParameters": { - "secretId": secretArns - + "secretId": secretArns.map(arn => ({ "prefix": arn.split('-').slice(0, -1).join('-') })) } } } - }); const dwLambda = new DoorwayLambdaBaseClass(scope, `${id}-restart-lambda`, { name: `${id}-function`, From e0bc5bacae8e4429d5f9d879c114d10669956bb8 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 14:13:39 -0700 Subject: [PATCH 072/217] fix: new event format --- .../doorway_environment/restart-services-lambda.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts index cfee04e915a..87e934d965d 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -28,17 +28,20 @@ export class RestartServicesLambda { for (const [key, secret] of Object.entries(props.secrets)) { console.log("Processing secret: ", key, secret); // The service setup uses the secret definition from ECS. unfortunately it doesn't include the secret name, so we have to re-create the secret from the arn to get the name. - const secretName = secretmgr.Secret.fromSecretPartialArn(scope, `${id}-secret-${key}`, secret.arn).secretArn; - secretArns.push(secretName); + const secretArn = secretmgr.Secret.fromSecretPartialArn(scope, `${id}-secret-${key}`, secret.arn).secretArn; + secretArns.push(secretArn); } const secretRule = new Rule(scope, `${id}-secret-change-rule`, { + enabled: true, + description: `Rule to trigger a restart of the ${props.service.serviceName} service when a secret is changed`, eventPattern: { - "source": ["aws.cloudtrail"], + "source": ["aws.secretsmanager"], + "detailType": ["AWS API Call via CloudTrail"], "detail": { "eventSource": ["secretsmanager.amazonaws.com"], "eventName": ["PutSecretValue"], "requestParameters": { - "secretId": secretArns.map(arn => ({ "prefix": arn.split('-').slice(0, -1).join('-') })) + "secretId": secretArns } } } From 863539b55e329a4242a5af2810b6ad4f249a06fb Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 14:19:32 -0700 Subject: [PATCH 073/217] fix: serviceprincipal addition of events --- .../restart-services-lambda.ts | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts index 87e934d965d..f4bf558d66c 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -1,6 +1,6 @@ import { Rule } from "aws-cdk-lib/aws-events"; import { LambdaFunction } from "aws-cdk-lib/aws-events-targets"; -import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam"; +import { Effect, PolicyStatement, ServicePrincipal } from "aws-cdk-lib/aws-iam"; import * as secretmgr from "aws-cdk-lib/aws-secretsmanager"; import { Construct } from "constructs"; @@ -31,21 +31,7 @@ export class RestartServicesLambda { const secretArn = secretmgr.Secret.fromSecretPartialArn(scope, `${id}-secret-${key}`, secret.arn).secretArn; secretArns.push(secretArn); } - const secretRule = new Rule(scope, `${id}-secret-change-rule`, { - enabled: true, - description: `Rule to trigger a restart of the ${props.service.serviceName} service when a secret is changed`, - eventPattern: { - "source": ["aws.secretsmanager"], - "detailType": ["AWS API Call via CloudTrail"], - "detail": { - "eventSource": ["secretsmanager.amazonaws.com"], - "eventName": ["PutSecretValue"], - "requestParameters": { - "secretId": secretArns - } - } - } - }); + const dwLambda = new DoorwayLambdaBaseClass(scope, `${id}-restart-lambda`, { name: `${id}-function`, environment: props.environment, @@ -64,6 +50,26 @@ export class RestartServicesLambda { resources: [props.service.serviceArn], effect: Effect.ALLOW, })); + dwLambda.lambdaRole.addToPrincipalPolicy(new PolicyStatement({ + actions: ["sts:AssumeRole"], + principals: [new ServicePrincipal("events.amazonaws.com"), new ServicePrincipal("lambda.amazonaws.com")] + })); + const secretRule = new Rule(scope, `${id}-secret-change-rule`, { + role: dwLambda.lambdaRole, + enabled: true, + description: `Rule to trigger a restart of the ${props.service.serviceName} service when a secret is changed`, + eventPattern: { + "source": ["aws.secretsmanager"], + "detailType": ["AWS API Call via CloudTrail"], + "detail": { + "eventSource": ["secretsmanager.amazonaws.com"], + "eventName": ["PutSecretValue"], + "requestParameters": { + "secretId": secretArns + } + } + } + }); secretRule.addTarget(new LambdaFunction(dwLambda.lambdaFunction)); } } \ No newline at end of file From 34459a28bfb33ad69d91c3d213b5dcf44a8533fd Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 14:29:19 -0700 Subject: [PATCH 074/217] fix: cert validation --- .../doorway-public-load-balancer.ts | 28 +++++++++++++++---- .../restart-services-lambda.ts | 2 -- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index 4ed70472fa7..8f6b90b7774 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -11,7 +11,7 @@ import { DoorwayProps } from "./doorway-props"; export class DoorwayPublicLoadBalancer { public loadBalancer: ApplicationLoadBalancer - public targetGroup: ApplicationTargetGroup; + public targetGroups: ApplicationTargetGroup[] = []; constructor(scope: Construct, id: string, props: DoorwayProps) { // Get Network setup @@ -47,8 +47,7 @@ export class DoorwayPublicLoadBalancer { domainName: "housingbayarea.mtc.ca.gov", }); const cert = new Certificate(scope, "PublicCertificate", { - domainName: process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`, - + domainName: process.env.PUBLIC_PORTAL_DOMAIN != undefined ? `*.${process.env.PUBLIC_PORTAL_DOMAIN}` : `*.${props.environment}.housingbayarea.mtc.ca.gov`, validation: CertificateValidation.fromDns(dnsZone), }) const httpListener = this.loadBalancer.addListener("HttpListener", { @@ -76,7 +75,7 @@ export class DoorwayPublicLoadBalancer { messageBody: "Forbidden", }) }) - this.targetGroup = new ApplicationTargetGroup(scope, "PublicTargetGroup", { + const publicTargetGroup = new ApplicationTargetGroup(scope, "PublicTargetGroup", { port: Number(process.env.PUBLIC_PORTAL_PORT) || 3000, protocol: ApplicationProtocol.HTTP, vpc: vpc, @@ -94,10 +93,11 @@ export class DoorwayPublicLoadBalancer { unhealthyThresholdCount: 2, }, }) + this.targetGroups.push(publicTargetGroup) new ApplicationListenerRule(scope, "PublicDomainRule", { listener: httpsListener, priority: 100, - action: ListenerAction.forward([this.targetGroup]), + action: ListenerAction.forward([publicTargetGroup]), conditions: [ListenerCondition.hostHeaders([process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`]) ] @@ -108,10 +108,26 @@ export class DoorwayPublicLoadBalancer { recordName: process.env.PUBLIC_PORTAL_DOMAIN || `${props.environment}.housingbayarea.mtc.ca.gov`, target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), }); + const partnersTargetGroup = new ApplicationTargetGroup(scope, "PublicTargetGroup", { + port: Number(process.env.PUBLIC_PORTAL_PORT) || 3000, + protocol: ApplicationProtocol.HTTP, + vpc: vpc, + targetGroupName: `public-tg-${props.environment}`, + targetType: TargetType.IP, + healthCheck: { + enabled: true, + healthyThresholdCount: 2, + interval: Duration.seconds(10), + path: "/", + timeout: Duration.seconds(5), + unhealthyThresholdCount: 2, + }, + }) + this.targetGroups.push(partnersTargetGroup) new ApplicationListenerRule(scope, "PartnersDomainRule", { listener: httpsListener, priority: 200, - action: ListenerAction.forward([this.targetGroup]), + action: ListenerAction.forward([partnersTargetGroup]), conditions: [ListenerCondition.hostHeaders([process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov`]) ] diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts index f4bf558d66c..d14348247db 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -21,8 +21,6 @@ export interface ServiceRestartDetail { export class RestartServicesLambda { constructor(scope: Construct, id: string, props: DoorwayServiceMonitorLambdaProps) { - - let secretArns: string[] = []; for (const [key, secret] of Object.entries(props.secrets)) { From ce42e138ac79acc9b3894bdbb96178a4841e6c8e Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 14:39:27 -0700 Subject: [PATCH 075/217] fix: role setup mixup --- cdk/lib/doorway_environment/restart-services-lambda.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts index d14348247db..6255fa18d33 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -48,10 +48,9 @@ export class RestartServicesLambda { resources: [props.service.serviceArn], effect: Effect.ALLOW, })); - dwLambda.lambdaRole.addToPrincipalPolicy(new PolicyStatement({ - actions: ["sts:AssumeRole"], - principals: [new ServicePrincipal("events.amazonaws.com"), new ServicePrincipal("lambda.amazonaws.com")] - })); + dwLambda.lambdaRole.grantAssumeRole(new ServicePrincipal("events.amazonaws.com")); + dwLambda.lambdaFunction.grantInvoke(new ServicePrincipal("events.amazonaws.com")); + const secretRule = new Rule(scope, `${id}-secret-change-rule`, { role: dwLambda.lambdaRole, enabled: true, From 148121a66d72eee3193eb662f004be4bd2b01abf Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 18:16:01 -0700 Subject: [PATCH 076/217] fix: cert issues --- .../doorway-app-environment-stack.ts | 4 ++-- .../doorway-public-load-balancer.ts | 14 +++++++------- .../doorway_environment/restart-services-lambda.ts | 6 +++++- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index d8d49e88244..fe0c9eb426f 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -41,14 +41,14 @@ export class DoorwayAppEnvironmentStack extends Stack { }) publicSite.service.node.addDependency(api.service) publicSite.service.node.addDependency(lb.loadBalancer) - lb.targetGroup.addTarget(publicSite.service) + lb.publicTargetGroup.addTarget(publicSite.service) const partnersSite = new DoorwayPublicSite(this, `doorway-partners-${environment}`, { environment: environment, logGroup: logGroup }) partnersSite.service.node.addDependency(api.service) partnersSite.service.node.addDependency(lb.loadBalancer) - lb.targetGroup.addTarget(partnersSite.service) + lb.partnersTargetGroup.addTarget(partnersSite.service) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index 8f6b90b7774..6fc8e2e4a45 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -11,7 +11,8 @@ import { DoorwayProps } from "./doorway-props"; export class DoorwayPublicLoadBalancer { public loadBalancer: ApplicationLoadBalancer - public targetGroups: ApplicationTargetGroup[] = []; + public publicTargetGroup: ApplicationTargetGroup; + public partnersTargetGroup: ApplicationTargetGroup; constructor(scope: Construct, id: string, props: DoorwayProps) { // Get Network setup @@ -75,7 +76,7 @@ export class DoorwayPublicLoadBalancer { messageBody: "Forbidden", }) }) - const publicTargetGroup = new ApplicationTargetGroup(scope, "PublicTargetGroup", { + this.publicTargetGroup = new ApplicationTargetGroup(scope, "PublicTargetGroup", { port: Number(process.env.PUBLIC_PORTAL_PORT) || 3000, protocol: ApplicationProtocol.HTTP, vpc: vpc, @@ -93,11 +94,11 @@ export class DoorwayPublicLoadBalancer { unhealthyThresholdCount: 2, }, }) - this.targetGroups.push(publicTargetGroup) + new ApplicationListenerRule(scope, "PublicDomainRule", { listener: httpsListener, priority: 100, - action: ListenerAction.forward([publicTargetGroup]), + action: ListenerAction.forward([this.publicTargetGroup]), conditions: [ListenerCondition.hostHeaders([process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`]) ] @@ -108,7 +109,7 @@ export class DoorwayPublicLoadBalancer { recordName: process.env.PUBLIC_PORTAL_DOMAIN || `${props.environment}.housingbayarea.mtc.ca.gov`, target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), }); - const partnersTargetGroup = new ApplicationTargetGroup(scope, "PublicTargetGroup", { + this.partnersTargetGroup = new ApplicationTargetGroup(scope, "PartnersTargetGroup", { port: Number(process.env.PUBLIC_PORTAL_PORT) || 3000, protocol: ApplicationProtocol.HTTP, vpc: vpc, @@ -123,11 +124,10 @@ export class DoorwayPublicLoadBalancer { unhealthyThresholdCount: 2, }, }) - this.targetGroups.push(partnersTargetGroup) new ApplicationListenerRule(scope, "PartnersDomainRule", { listener: httpsListener, priority: 200, - action: ListenerAction.forward([partnersTargetGroup]), + action: ListenerAction.forward([this.partnersTargetGroup]), conditions: [ListenerCondition.hostHeaders([process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov`]) ] diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts index 6255fa18d33..ceae14affa3 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -48,7 +48,11 @@ export class RestartServicesLambda { resources: [props.service.serviceArn], effect: Effect.ALLOW, })); - dwLambda.lambdaRole.grantAssumeRole(new ServicePrincipal("events.amazonaws.com")); + dwLambda.lambdaRole.assumeRolePolicy!.addStatements(new PolicyStatement({ + actions: ["sts:AssumeRole"], + effect: Effect.ALLOW, + principals: [new ServicePrincipal("events.amazonaws.com")] + })) dwLambda.lambdaFunction.grantInvoke(new ServicePrincipal("events.amazonaws.com")); const secretRule = new Rule(scope, `${id}-secret-change-rule`, { From 8aec176a5ea78c3dc385fcb35b79e2fada63ed88 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 18:27:39 -0700 Subject: [PATCH 077/217] fix: reverting back to original arn pattern --- cdk/lib/doorway_environment/restart-services-lambda.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts index ceae14affa3..91917282fb8 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -1,7 +1,6 @@ import { Rule } from "aws-cdk-lib/aws-events"; import { LambdaFunction } from "aws-cdk-lib/aws-events-targets"; import { Effect, PolicyStatement, ServicePrincipal } from "aws-cdk-lib/aws-iam"; -import * as secretmgr from "aws-cdk-lib/aws-secretsmanager"; import { Construct } from "constructs"; import { DoorwayServiceMonitorLambdaProps } from "./doorway-lambda-props"; @@ -26,7 +25,7 @@ export class RestartServicesLambda { for (const [key, secret] of Object.entries(props.secrets)) { console.log("Processing secret: ", key, secret); // The service setup uses the secret definition from ECS. unfortunately it doesn't include the secret name, so we have to re-create the secret from the arn to get the name. - const secretArn = secretmgr.Secret.fromSecretPartialArn(scope, `${id}-secret-${key}`, secret.arn).secretArn; + const secretArn = secret.arn secretArns.push(secretArn); } From 98101ee886580685bdd0c8e2685d2e55bc64f1e2 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 18:33:02 -0700 Subject: [PATCH 078/217] fix: duplicate target groups --- cdk/lib/doorway_environment/doorway-public-load-balancer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index 6fc8e2e4a45..f1615f8a1de 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -113,7 +113,7 @@ export class DoorwayPublicLoadBalancer { port: Number(process.env.PUBLIC_PORTAL_PORT) || 3000, protocol: ApplicationProtocol.HTTP, vpc: vpc, - targetGroupName: `public-tg-${props.environment}`, + targetGroupName: `partners-tg-${props.environment}`, targetType: TargetType.IP, healthCheck: { enabled: true, From ce304e9d079e1bebc9c6f3fe287fc81e8eead9f1 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 19:11:05 -0700 Subject: [PATCH 079/217] fix: trying with secret prefix --- .../doorway_environment/restart-services-lambda.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/restart-services-lambda.ts index 91917282fb8..9a531b3fef8 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/restart-services-lambda.ts @@ -23,11 +23,12 @@ export class RestartServicesLambda { let secretArns: string[] = []; for (const [key, secret] of Object.entries(props.secrets)) { - console.log("Processing secret: ", key, secret); - // The service setup uses the secret definition from ECS. unfortunately it doesn't include the secret name, so we have to re-create the secret from the arn to get the name. - const secretArn = secret.arn - secretArns.push(secretArn); + console.log("Processing secret key:", key); + console.log("Secret ARN:", secret.arn); + console.log("Secret object:", JSON.stringify(secret, null, 2)); + secretArns.push(secret.arn); } + console.log("All secret ARNs:", secretArns); const dwLambda = new DoorwayLambdaBaseClass(scope, `${id}-restart-lambda`, { name: `${id}-function`, @@ -65,7 +66,7 @@ export class RestartServicesLambda { "eventSource": ["secretsmanager.amazonaws.com"], "eventName": ["PutSecretValue"], "requestParameters": { - "secretId": secretArns + "secretId": secretArns.map(arn => ({ "prefix": arn })) } } } From 7ef8a9158adb711f3046814f1ecf2f96dd246e11 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 19:19:39 -0700 Subject: [PATCH 080/217] fix: sigh...another missed public-partners --- cdk/lib/doorway_environment/doorway-app-environment-stack.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index fe0c9eb426f..694391830ef 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -5,6 +5,7 @@ import dotenv from "dotenv" import path from "path" import { DoorwayBackendService } from "./doorway-backend-service" +import { DoorwayPartnersSite } from "./doorway-partners-site" import { DoorwayPublicLoadBalancer } from "./doorway-public-load-balancer" import { DoorwayPublicSite } from "./doorway-public-site" @@ -42,7 +43,7 @@ export class DoorwayAppEnvironmentStack extends Stack { publicSite.service.node.addDependency(api.service) publicSite.service.node.addDependency(lb.loadBalancer) lb.publicTargetGroup.addTarget(publicSite.service) - const partnersSite = new DoorwayPublicSite(this, `doorway-partners-${environment}`, { + const partnersSite = new DoorwayPartnersSite(this, `doorway-partners-${environment}`, { environment: environment, logGroup: logGroup }) From 0fa037aaf12f64e3309cbfb938af8c3da674f4e3 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 28 Aug 2025 20:30:52 -0700 Subject: [PATCH 081/217] fix: dealing with empty secrets set --- cdk/dev2.env | 2 -- .../doorway-partners-site.ts | 2 +- .../doorway_environment/doorway_service.ts | 20 ++++++++----------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/cdk/dev2.env b/cdk/dev2.env index 6bbf9fd5acc..00be6ccf64d 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -23,8 +23,6 @@ PUBLIC_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY PARTNERS_PORTAL_PORT=3200 PARTNERS_PORTAL_DOMAIN=partners.dev2.housingbayarea.mtc.ca.gov -PARTNERS_PORTAL_SECRETS=MAPBOX_TOKEN - BACKEND_API_BASE=http://backend.dev2.housingbayarea.int:3100 BACKEND_API_PORT=3100 BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVDELIVERY_API_URL diff --git a/cdk/lib/doorway_environment/doorway-partners-site.ts b/cdk/lib/doorway_environment/doorway-partners-site.ts index 4fb37fdd807..744f49d2c22 100644 --- a/cdk/lib/doorway_environment/doorway-partners-site.ts +++ b/cdk/lib/doorway_environment/doorway-partners-site.ts @@ -59,7 +59,7 @@ export class DoorwayPartnersSite { SHOW_LM_LINKS: process.env.SHOW_LM_LINKS || "TRUE", USE_SECURE_DOWNLOAD_PATHWAY: process.env.USE_SECURE_DOWNLOAD_PATHWAY || "TRUE", } - const secretNames = (process.env.PARTNERS_PORTAL_SECRETS || "").split(",") + const secretNames = process.env.PARTNERS_PORTAL_SECRETS != undefined ? process.env.PARTNERS_PORTAL_SECRETS.split(",") : [] const secrets: { [key: string]: Secret } = {} secretNames.forEach((secretName) => { secrets[secretName] = Secret.fromSecretsManager( diff --git a/cdk/lib/doorway_environment/doorway_service.ts b/cdk/lib/doorway_environment/doorway_service.ts index 4035a7daeaf..8998ea83fb0 100644 --- a/cdk/lib/doorway_environment/doorway_service.ts +++ b/cdk/lib/doorway_environment/doorway_service.ts @@ -64,7 +64,7 @@ export class DoorwayService { streamPrefix: id, logGroup: props.logGroup, }), - secrets: props.secrets, + secrets: props.secrets || {}, environment: props.environmentVariables, entryPoint: [], portMappings: [ @@ -82,12 +82,10 @@ export class DoorwayService { // without a load balancer. let serviceConnectProps: ServiceConnectProps = { namespace: `doorway-${props.environment}-internal-api`, - logDriver: LogDrivers.awsLogs({ logGroup: props.logGroup, streamPrefix: `${id}-service-connect`, }), - } if (props.serviceConnectServer) { @@ -96,7 +94,6 @@ export class DoorwayService { `doorway-${props.environment}-internal-api-namespace`, { vpc: vpc, - name: `doorway-${props.environment}-internal-api`, description: `Private DNS namespace for the Doorway ${props.environment} internal API`, }, @@ -145,8 +142,6 @@ export class DoorwayService { port: props.port, }], - - } } @@ -157,7 +152,6 @@ export class DoorwayService { enable: true, rollback: true, }, - cluster: Cluster.fromClusterAttributes(scope, `ecsCluster-${id}`, { clusterName: Fn.importValue(`doorway-ecs-cluster-${props.environment}`), vpc: vpc, @@ -170,11 +164,13 @@ export class DoorwayService { serviceConnectConfiguration: serviceConnectProps, }) - new RestartServicesLambda(scope, `restart-${id}-${props.environment}`, { - service: this.service, - secrets: props.secrets, - environment: props.environment, - }) + if (Object.keys(props.secrets).length > 0) { + new RestartServicesLambda(scope, `restart-${id}-${props.environment}`, { + service: this.service, + secrets: props.secrets, + environment: props.environment, + }) + } props.logGroup.grantWrite(props.executionRole) } From 5bb3b4ce91e1be714711b741db829e4f1ee88933 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 29 Aug 2025 08:42:03 -0700 Subject: [PATCH 082/217] fix: no cors origins in env file --- cdk/dev2.env | 1 + cdk/lib/doorway_environment/doorway-public-load-balancer.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cdk/dev2.env b/cdk/dev2.env index 00be6ccf64d..7ed641fae31 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -7,6 +7,7 @@ FEATURE_LISTINGS_APPROVAL=TRUE ECR_ACCOUNT_ID=364076391763 ECR_NAMESPACE=doorway ECR_REGION=us-west-2 +CORS_ORIGINS=https://dev2.housingbayarea.mtc.ca.gov, https://partners.dev2.housingbayarea.mtc.ca.gov USE_SECURE_DOWNLOAD_PATHWAY=TRUE diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index f1615f8a1de..ff616e6a6f4 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -48,9 +48,11 @@ export class DoorwayPublicLoadBalancer { domainName: "housingbayarea.mtc.ca.gov", }); const cert = new Certificate(scope, "PublicCertificate", { - domainName: process.env.PUBLIC_PORTAL_DOMAIN != undefined ? `*.${process.env.PUBLIC_PORTAL_DOMAIN}` : `*.${props.environment}.housingbayarea.mtc.ca.gov`, + domainName: process.env.PUBLIC_PORTAL_DOMAIN || `${props.environment}.housingbayarea.mtc.ca.gov`, validation: CertificateValidation.fromDns(dnsZone), + subjectAlternativeNames: [process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov`] }) + const httpListener = this.loadBalancer.addListener("HttpListener", { port: 80, open: true, From f28c074b487a205627ea48be6bddb3294cd1ce63 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 29 Aug 2025 11:42:47 -0700 Subject: [PATCH 083/217] fix: debugging origin --- api/src/controllers/auth.controller.ts | 46 +++++++------------ .../doorway-backend-service.ts | 3 +- 2 files changed, 18 insertions(+), 31 deletions(-) diff --git a/api/src/controllers/auth.controller.ts b/api/src/controllers/auth.controller.ts index 38d7e95f07c..5df62e0bfb0 100644 --- a/api/src/controllers/auth.controller.ts +++ b/api/src/controllers/auth.controller.ts @@ -1,44 +1,30 @@ -import { - Controller, - Get, - Request, - Response, - Post, - UsePipes, - ValidationPipe, - Body, - BadRequestException, - Put, - UseGuards, -} from '@nestjs/common'; +import { BadRequestException, Body, Controller, Get, Post, Put, Request, Response, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common'; import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { - Response as ExpressResponse, - Request as ExpressRequest, -} from 'express'; -import { defaultValidationPipeOptions } from '../utilities/default-validation-pipe-options'; -import { SuccessDTO } from '../dtos/shared/success.dto'; -import { AuthService, REFRESH_COOKIE_NAME } from '../services/auth.service'; -import { RequestMfaCode } from '../dtos/mfa/request-mfa-code.dto'; -import { RequestMfaCodeResponse } from '../dtos/mfa/request-mfa-code-response.dto'; +import { Request as ExpressRequest, Response as ExpressResponse } from 'express'; + import { Confirm } from '../dtos/auth/confirm.dto'; -import { UpdatePassword } from '../dtos/auth/update-password.dto'; -import { MfaAuthGuard } from '../guards/mfa.guard'; -import { JwtAuthGuard } from '../guards/jwt.guard'; -import { OptionalAuthGuard } from '../guards/optional.guard'; +import { LoginViaSingleUseCode } from '../dtos/auth/login-single-use-code.dto'; import { Login } from '../dtos/auth/login.dto'; -import { mapTo } from '../utilities/mapTo'; +import { UpdatePassword } from '../dtos/auth/update-password.dto'; +import { RequestMfaCodeResponse } from '../dtos/mfa/request-mfa-code-response.dto'; +import { RequestMfaCode } from '../dtos/mfa/request-mfa-code.dto'; +import { SuccessDTO } from '../dtos/shared/success.dto'; import { User } from '../dtos/users/user.dto'; -import { LoginViaSingleUseCode } from '../dtos/auth/login-single-use-code.dto'; import { ApiKeyGuard } from '../guards/api-key.guard'; +import { JwtAuthGuard } from '../guards/jwt.guard'; +import { MfaAuthGuard } from '../guards/mfa.guard'; +import { OptionalAuthGuard } from '../guards/optional.guard'; import { SingleUseCodeAuthGuard } from '../guards/single-use-code.guard'; +import { AuthService, REFRESH_COOKIE_NAME } from '../services/auth.service'; +import { defaultValidationPipeOptions } from '../utilities/default-validation-pipe-options'; +import { mapTo } from '../utilities/mapTo'; @Controller('auth') @ApiTags('auth') @UsePipes(new ValidationPipe(defaultValidationPipeOptions)) @UseGuards(ApiKeyGuard) export class AuthController { - constructor(private readonly authService: AuthService) {} + constructor(private readonly authService: AuthService) { } @Post('login') @ApiOperation({ summary: 'Login', operationId: 'login' }) @@ -50,6 +36,8 @@ export class AuthController { @Response({ passthrough: true }) res: ExpressResponse, @Body() dto: Login, ): Promise { + console.log('Login request received for user:', dto.email); + console.log(`Origin: ${req.headers.origin}`) return await this.authService.setCredentials( res, mapTo(User, req['user']), diff --git a/cdk/lib/doorway_environment/doorway-backend-service.ts b/cdk/lib/doorway_environment/doorway-backend-service.ts index 2d79cde7659..def86ce86ab 100644 --- a/cdk/lib/doorway_environment/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/doorway-backend-service.ts @@ -66,8 +66,7 @@ export class DoorwayBackendService { AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS: process.env.AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS || "5", CORS_ORIGINS: - process.env.CORS_ORIGINS || - "https://housingbayarea.mtc.ca.gov, https://partners.housingbayarea.mtc.ca.gov", + process.env.CORS_ORIGINS || "*", DUPLICATES_CLOSE_DATE: process.env.DUPLICATES_CLOSE_DATE || "2024-10-08 00:00 -08:00", HTTPS_OFF: process.env.HTTPS_OFF || "TRUE", SAME_SITE: process.env.SAME_SITE || "false", From 90ddd8009c5c79f223bd7268e40e40e427a8344e Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 29 Aug 2025 12:30:10 -0700 Subject: [PATCH 084/217] fix: trying with logger --- api/src/controllers/auth.controller.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/api/src/controllers/auth.controller.ts b/api/src/controllers/auth.controller.ts index 5df62e0bfb0..9195f3764b6 100644 --- a/api/src/controllers/auth.controller.ts +++ b/api/src/controllers/auth.controller.ts @@ -15,6 +15,7 @@ import { JwtAuthGuard } from '../guards/jwt.guard'; import { MfaAuthGuard } from '../guards/mfa.guard'; import { OptionalAuthGuard } from '../guards/optional.guard'; import { SingleUseCodeAuthGuard } from '../guards/single-use-code.guard'; +import { instance } from '../logger/winston.logger'; import { AuthService, REFRESH_COOKIE_NAME } from '../services/auth.service'; import { defaultValidationPipeOptions } from '../utilities/default-validation-pipe-options'; import { mapTo } from '../utilities/mapTo'; @@ -36,8 +37,11 @@ export class AuthController { @Response({ passthrough: true }) res: ExpressResponse, @Body() dto: Login, ): Promise { - console.log('Login request received for user:', dto.email); - console.log(`Origin: ${req.headers.origin}`) + + instance.info(`Login attempt for user: ${dto.email}`); + instance.info(`Origin: ${req.headers.origin}`); + instance.info(`Headers: ${JSON.stringify(req.headers)}`) + return await this.authService.setCredentials( res, mapTo(User, req['user']), From a3026e1da3da8a7e2ed4c20b61aac7de08f22fef Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 29 Aug 2025 15:37:41 -0700 Subject: [PATCH 085/217] fix: using nestjs logger --- api/src/controllers/auth.controller.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/api/src/controllers/auth.controller.ts b/api/src/controllers/auth.controller.ts index 9195f3764b6..c8429af9b6e 100644 --- a/api/src/controllers/auth.controller.ts +++ b/api/src/controllers/auth.controller.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Body, Controller, Get, Post, Put, Request, Response, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common'; +import { BadRequestException, Body, Controller, Get, Logger, Post, Put, Request, Response, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common'; import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; import { Request as ExpressRequest, Response as ExpressResponse } from 'express'; @@ -15,7 +15,7 @@ import { JwtAuthGuard } from '../guards/jwt.guard'; import { MfaAuthGuard } from '../guards/mfa.guard'; import { OptionalAuthGuard } from '../guards/optional.guard'; import { SingleUseCodeAuthGuard } from '../guards/single-use-code.guard'; -import { instance } from '../logger/winston.logger'; + import { AuthService, REFRESH_COOKIE_NAME } from '../services/auth.service'; import { defaultValidationPipeOptions } from '../utilities/default-validation-pipe-options'; import { mapTo } from '../utilities/mapTo'; @@ -25,6 +25,7 @@ import { mapTo } from '../utilities/mapTo'; @UsePipes(new ValidationPipe(defaultValidationPipeOptions)) @UseGuards(ApiKeyGuard) export class AuthController { + private readonly logger = new Logger(AuthController.name); constructor(private readonly authService: AuthService) { } @Post('login') @@ -38,9 +39,9 @@ export class AuthController { @Body() dto: Login, ): Promise { - instance.info(`Login attempt for user: ${dto.email}`); - instance.info(`Origin: ${req.headers.origin}`); - instance.info(`Headers: ${JSON.stringify(req.headers)}`) + this.logger.log(`Login attempt for user: ${dto.email}`); + this.logger.log(`Origin: ${req.headers.origin}`); + this.logger.log(`Headers: ${JSON.stringify(req.headers)}`) return await this.authService.setCredentials( res, From 90a800494b1c509b99e2dd6db08893f33d522517 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 29 Aug 2025 16:04:03 -0700 Subject: [PATCH 086/217] fix: another debugging attempt --- api/src/main.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/api/src/main.ts b/api/src/main.ts index dd1e018bd6f..690ebb06aaa 100644 --- a/api/src/main.ts +++ b/api/src/main.ts @@ -54,6 +54,17 @@ async function bootstrap() { } cb(null, options); }); + // Debug middleware to capture headers + app.use((req, res, next) => { + console.log('=== RAW REQUEST DEBUG ==='); + console.log('Method:', req.method); + console.log('URL:', req.url); + console.log('Origin:', req.headers.origin); + console.log('Host:', req.headers.host); + console.log('All Headers:', JSON.stringify(req.headers, null, 2)); + console.log('========================'); + next(); + }); app.use(logger); app.use( cookieParser(), From a08e708cc7531cb2ad7210a50597b35c87975d6d Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 29 Aug 2025 17:03:27 -0700 Subject: [PATCH 087/217] fix: origins and docker versioning changes --- api/src/main.ts | 31 +++++++++++-------- cdk/dev2.env | 1 - .../doorway-backend-service.ts | 3 +- .../doorway-partners-site.ts | 6 ++-- cdk/lib/doorway_environment/doorway-props.ts | 1 + .../doorway-public-site.ts | 7 +++-- .../doorway_environment/doorway_service.ts | 2 ++ 7 files changed, 32 insertions(+), 19 deletions(-) diff --git a/api/src/main.ts b/api/src/main.ts index 690ebb06aaa..079d6bbd27f 100644 --- a/api/src/main.ts +++ b/api/src/main.ts @@ -45,26 +45,31 @@ async function bootstrap() { origin: false, }; + const origin = req.header('Origin'); + const appUrl = req.header('appurl'); // Service Connect may strip Origin, check appurl + if ( process.env.DISABLE_CORS === 'TRUE' || - allowList.indexOf(req.header('Origin')) !== -1 || - regexAllowList.some((regex) => regex.test(req.header('Origin'))) + (origin && (allowList.indexOf(origin) !== -1 || regexAllowList.some((regex) => regex.test(origin)))) || + (appUrl && (allowList.indexOf(appUrl) !== -1 || regexAllowList.some((regex) => regex.test(appUrl)))) ) { options.origin = true; } cb(null, options); }); - // Debug middleware to capture headers - app.use((req, res, next) => { - console.log('=== RAW REQUEST DEBUG ==='); - console.log('Method:', req.method); - console.log('URL:', req.url); - console.log('Origin:', req.headers.origin); - console.log('Host:', req.headers.host); - console.log('All Headers:', JSON.stringify(req.headers, null, 2)); - console.log('========================'); - next(); - }); + // Debug middleware to capture headers (only when DEBUG_HEADERS=true) + if (process.env.DEBUG_HEADERS === 'true') { + app.use((req, res, next) => { + inUselogger.debug('=== RAW REQUEST DEBUG ==='); + inUselogger.debug(`Method: ${req.method}`); + inUselogger.debug(`URL: ${req.url}`); + inUselogger.debug(`Origin: ${req.headers.origin}`); + inUselogger.debug(`Host: ${req.headers.host}`); + inUselogger.debug(`All Headers: ${JSON.stringify(req.headers, null, 2)}`); + inUselogger.debug('========================'); + next(); + }); + } app.use(logger); app.use( cookieParser(), diff --git a/cdk/dev2.env b/cdk/dev2.env index 7ed641fae31..f75205fb124 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -9,7 +9,6 @@ ECR_NAMESPACE=doorway ECR_REGION=us-west-2 CORS_ORIGINS=https://dev2.housingbayarea.mtc.ca.gov, https://partners.dev2.housingbayarea.mtc.ca.gov - USE_SECURE_DOWNLOAD_PATHWAY=TRUE GTM_KEY=GTM-KF22FJP SOURCE_REPO_NAMESPACE=doorway diff --git a/cdk/lib/doorway_environment/doorway-backend-service.ts b/cdk/lib/doorway_environment/doorway-backend-service.ts index def86ce86ab..fcc811a1862 100644 --- a/cdk/lib/doorway_environment/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/doorway-backend-service.ts @@ -13,6 +13,7 @@ import { DoorwayService } from "./doorway_service" export class DoorwayBackendService { public service: FargateService public constructor(scope: Construct, id: string, props: DoorwayProps) { + const gitHash = process.env.CODEBUILD_RESOLVED_SOURCE_VERSION?.substring(0, 8) || "candidate" const executionRole = new Role(scope, `executionRole-${id}`, { assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), }) @@ -155,7 +156,7 @@ export class DoorwayBackendService { secureUploads: secureUploads, environment: props.environment, logGroup: props.logGroup, - container: "doorway/backend:run-candidate", + container: `doorway/backend:run-${gitHash}`, securityGroup: privateSG, }).service } diff --git a/cdk/lib/doorway_environment/doorway-partners-site.ts b/cdk/lib/doorway_environment/doorway-partners-site.ts index 744f49d2c22..8f279f900a6 100644 --- a/cdk/lib/doorway_environment/doorway-partners-site.ts +++ b/cdk/lib/doorway_environment/doorway-partners-site.ts @@ -12,9 +12,11 @@ import { DoorwayService } from "./doorway_service" export class DoorwayPartnersSite { public service: FargateService constructor(scope: Construct, id: string, props: DoorwayProps) { + const gitHash = process.env.CODEBUILD_RESOLVED_SOURCE_VERSION?.substring(0, 8) || "candidate" const executionRole = new Role(scope, `executionRole-${id}`, { assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), }) + const publicUploads = Bucket.fromBucketArn( scope, `partnersUploadsBucket-${id}`, @@ -82,8 +84,8 @@ export class DoorwayPartnersSite { logGroup: props.logGroup, apiTargetDomainName: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, apiTargetPort: Number(process.env.BACKEND_API_PORT || 3000), - container: `doorway/partners:run-${process.env.ENVIRONMENT || "dev2"}-candidate`, - securityGroup: privateSG + container: `doorway/partners:run-${process.env.ENVIRONMENT || "dev2"}-${gitHash}`, + securityGroup: privateSG, }).service } diff --git a/cdk/lib/doorway_environment/doorway-props.ts b/cdk/lib/doorway_environment/doorway-props.ts index 60f17e6f48d..63d8700acc8 100644 --- a/cdk/lib/doorway_environment/doorway-props.ts +++ b/cdk/lib/doorway_environment/doorway-props.ts @@ -25,6 +25,7 @@ export interface DoorwayServiceProps extends DoorwayProps { secureUploads: IBucket container: string securityGroup: ISecurityGroup + } export interface DoorwayLoadBalancerProps extends DoorwayProps { publicService: FargateService, diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts index 8afbc078d14..b2e885f7d61 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -12,9 +12,11 @@ import { DoorwayService } from "./doorway_service" export class DoorwayPublicSite { public service: FargateService constructor(scope: Construct, id: string, props: DoorwayProps) { + const gitHash = process.env.CODEBUILD_RESOLVED_SOURCE_VERSION?.substring(0, 8) || "candidate" const executionRole = new Role(scope, `executionRole-${id}`, { assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), }) + const publicUploads = Bucket.fromBucketArn( scope, `publicUploadsBucket-${id}`, @@ -86,8 +88,9 @@ export class DoorwayPublicSite { logGroup: props.logGroup, apiTargetDomainName: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, apiTargetPort: Number(process.env.BACKEND_API_PORT || 3000), - container: `doorway/public:run-${process.env.ENVIRONMENT || "dev2"}-candidate`, - securityGroup: privateSG + container: `doorway/public:run-${process.env.ENVIRONMENT || "dev2"}-${gitHash}`, + securityGroup: privateSG, + }).service diff --git a/cdk/lib/doorway_environment/doorway_service.ts b/cdk/lib/doorway_environment/doorway_service.ts index 8998ea83fb0..868873d1620 100644 --- a/cdk/lib/doorway_environment/doorway_service.ts +++ b/cdk/lib/doorway_environment/doorway_service.ts @@ -13,10 +13,12 @@ import { RestartServicesLambda } from "./restart-services-lambda" export class DoorwayService { public service: FargateService + constructor(scope: Construct, id: string, props: DoorwayServiceProps) { // Initial Execution Role Setup // Create the execution role for the doorway API service + props.executionRole.addManagedPolicy( ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy"), ) From 6106b48da301f2a226cf6c0075426fab9f08e252 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 29 Aug 2025 18:03:08 -0700 Subject: [PATCH 088/217] fix: moving serviceconnect logs out of main log group --- cdk/dev2.env | 1 + .../doorway-app-environment-stack.ts | 13 ++++++++++--- .../doorway_environment/doorway-backend-service.ts | 2 +- cdk/lib/doorway_environment/doorway-props.ts | 1 + cdk/lib/doorway_environment/doorway_service.ts | 2 +- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/cdk/dev2.env b/cdk/dev2.env index f75205fb124..8720b154ac3 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -31,3 +31,4 @@ BACKEND_API_CPUS=2 BACKEND_API_INSTANCES=3 API_PASS_KEY=dev-key +DEBUG_HEADERS=true \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 694391830ef..459ad0c8e24 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -26,26 +26,33 @@ export class DoorwayAppEnvironmentStack extends Stack { removalPolicy: RemovalPolicy.DESTROY, retention: aws_logs.RetentionDays.ONE_WEEK, }) + const scLogGroup = new LogGroup(this, `${id}-service-connect-log-group`, { + logGroupName: `/doorway/${environment}/service-connect` + }) const api = new DoorwayBackendService(this, `doorway-api-service-${environment}`, { environment: environment, logGroup: logGroup, + serviceConnectLogGroup: scLogGroup }) const lb = new DoorwayPublicLoadBalancer(this, `doorway-public-lb-${environment}`, { environment: environment, - logGroup: logGroup + logGroup: logGroup, + serviceConnectLogGroup: scLogGroup }); const publicSite = new DoorwayPublicSite(this, `doorway-public-${environment}`, { environment: environment, - logGroup: logGroup + logGroup: logGroup, + serviceConnectLogGroup: scLogGroup }) publicSite.service.node.addDependency(api.service) publicSite.service.node.addDependency(lb.loadBalancer) lb.publicTargetGroup.addTarget(publicSite.service) const partnersSite = new DoorwayPartnersSite(this, `doorway-partners-${environment}`, { environment: environment, - logGroup: logGroup + logGroup: logGroup, + serviceConnectLogGroup: scLogGroup }) partnersSite.service.node.addDependency(api.service) partnersSite.service.node.addDependency(lb.loadBalancer) diff --git a/cdk/lib/doorway_environment/doorway-backend-service.ts b/cdk/lib/doorway_environment/doorway-backend-service.ts index fcc811a1862..181e90fb21e 100644 --- a/cdk/lib/doorway_environment/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/doorway-backend-service.ts @@ -143,6 +143,7 @@ export class DoorwayBackendService { }), "Allow internal traffic from web app servers") executionRole.addToPolicy(policy) this.service = new DoorwayService(scope, `doorway-api-service-${props.environment}`, { + ...props, memory: Number(process.env.BACKEND_MEMORY || 4096), cpu: Number(process.env.BACKEND_CPU || 2), instances: Number(process.env.BACKEND_INSTANCES || 3), @@ -155,7 +156,6 @@ export class DoorwayBackendService { publicUploads: publicUploads, secureUploads: secureUploads, environment: props.environment, - logGroup: props.logGroup, container: `doorway/backend:run-${gitHash}`, securityGroup: privateSG, }).service diff --git a/cdk/lib/doorway_environment/doorway-props.ts b/cdk/lib/doorway_environment/doorway-props.ts index 63d8700acc8..36503a71575 100644 --- a/cdk/lib/doorway_environment/doorway-props.ts +++ b/cdk/lib/doorway_environment/doorway-props.ts @@ -7,6 +7,7 @@ import { IBucket } from "aws-cdk-lib/aws-s3" export interface DoorwayProps { environment: string, logGroup: LogGroup + serviceConnectLogGroup: LogGroup } export interface DoorwayServiceProps extends DoorwayProps { diff --git a/cdk/lib/doorway_environment/doorway_service.ts b/cdk/lib/doorway_environment/doorway_service.ts index 868873d1620..e440ab77153 100644 --- a/cdk/lib/doorway_environment/doorway_service.ts +++ b/cdk/lib/doorway_environment/doorway_service.ts @@ -85,7 +85,7 @@ export class DoorwayService { let serviceConnectProps: ServiceConnectProps = { namespace: `doorway-${props.environment}-internal-api`, logDriver: LogDrivers.awsLogs({ - logGroup: props.logGroup, + logGroup: props.serviceConnectLogGroup, streamPrefix: `${id}-service-connect`, }), } From ff3788e20526aade7b92e019140225ea643bc1d9 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 29 Aug 2025 20:25:24 -0700 Subject: [PATCH 089/217] fix: yet more debugging --- api/src/main.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/api/src/main.ts b/api/src/main.ts index 079d6bbd27f..bedd46c1068 100644 --- a/api/src/main.ts +++ b/api/src/main.ts @@ -5,18 +5,18 @@ try { } catch { // Pass } -import { NestFactory, HttpAdapterHost } from '@nestjs/core'; import { Logger } from '@nestjs/common'; -import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { ConfigService } from '@nestjs/config'; -import cookieParser from 'cookie-parser'; +import { HttpAdapterHost, NestFactory } from '@nestjs/core'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import compression from 'compression'; +import cookieParser from 'cookie-parser'; import { json } from 'express'; -import { AppModule } from './modules/app.module'; -import { CustomExceptionFilter } from './utilities/custom-exception-filter'; -import { logger } from './middleware/logger.middleware'; import { WinstonModule } from 'nest-winston'; import { instance } from './logger/winston.logger'; +import { logger } from './middleware/logger.middleware'; +import { AppModule } from './modules/app.module'; +import { CustomExceptionFilter } from './utilities/custom-exception-filter'; async function bootstrap() { const app = await NestFactory.create(AppModule, { @@ -25,8 +25,8 @@ async function bootstrap() { process.env.NODE_ENV === 'development' ? ['log'] : WinstonModule.createLogger({ - instance: instance, - }), + instance: instance, + }), }); const allowList = process.env.CORS_ORIGINS || []; const allowListRegex = process.env.CORS_REGEX @@ -47,7 +47,9 @@ async function bootstrap() { const origin = req.header('Origin'); const appUrl = req.header('appurl'); // Service Connect may strip Origin, check appurl - + inUselogger.debug(`AppUrl: ${appUrl}, Origin: ${origin}`); + inUselogger.debug(`Allow List: ${allowList}`); + if ( process.env.DISABLE_CORS === 'TRUE' || (origin && (allowList.indexOf(origin) !== -1 || regexAllowList.some((regex) => regex.test(origin)))) || From 0e0296888859646903076c075d437160cca0776d Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 29 Aug 2025 21:29:39 -0700 Subject: [PATCH 090/217] fix: more time in debugging hell --- api/src/main.ts | 1 + cdk/dev2.env | 3 ++- cdk/lib/doorway_environment/doorway-backend-service.ts | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/api/src/main.ts b/api/src/main.ts index bedd46c1068..3d1c62e2bdc 100644 --- a/api/src/main.ts +++ b/api/src/main.ts @@ -55,6 +55,7 @@ async function bootstrap() { (origin && (allowList.indexOf(origin) !== -1 || regexAllowList.some((regex) => regex.test(origin)))) || (appUrl && (allowList.indexOf(appUrl) !== -1 || regexAllowList.some((regex) => regex.test(appUrl)))) ) { + inUselogger.debug(`CORS enabled for origin: ${origin || appUrl}`); options.origin = true; } cb(null, options); diff --git a/cdk/dev2.env b/cdk/dev2.env index 8720b154ac3..208470cb5ee 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -31,4 +31,5 @@ BACKEND_API_CPUS=2 BACKEND_API_INSTANCES=3 API_PASS_KEY=dev-key -DEBUG_HEADERS=true \ No newline at end of file +DEBUG_HEADERS=true +LOG_LEVEL=debug \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-backend-service.ts b/cdk/lib/doorway_environment/doorway-backend-service.ts index 181e90fb21e..85a5fb01cf4 100644 --- a/cdk/lib/doorway_environment/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/doorway-backend-service.ts @@ -72,7 +72,8 @@ export class DoorwayBackendService { HTTPS_OFF: process.env.HTTPS_OFF || "TRUE", SAME_SITE: process.env.SAME_SITE || "false", API_PASS_KEY: process.env.API_PASS_KEY || "doorway-api-key", - APP_SECRET: process.env.APP_SECRET || "" + APP_SECRET: process.env.APP_SECRET || "", + DEBUG_HEADERS: process.env.DEBUG_HEADERS || "false", } const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) const dbSecret = secret.Secret.fromSecretCompleteArn(scope, "dbSecret", dbSecretArn) From 7c4a7efccfb543db6a2e1e410f8f39f4c31d65c4 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sat, 30 Aug 2025 10:04:42 -0700 Subject: [PATCH 091/217] fix trying with DISABLE_CORS again --- api/src/main.ts | 35 ++++++++----------- cdk/dev2.env | 4 +-- .../doorway-backend-service.ts | 2 +- 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/api/src/main.ts b/api/src/main.ts index 3d1c62e2bdc..3bfe20a6a3b 100644 --- a/api/src/main.ts +++ b/api/src/main.ts @@ -45,34 +45,27 @@ async function bootstrap() { origin: false, }; - const origin = req.header('Origin'); - const appUrl = req.header('appurl'); // Service Connect may strip Origin, check appurl - inUselogger.debug(`AppUrl: ${appUrl}, Origin: ${origin}`); - inUselogger.debug(`Allow List: ${allowList}`); - if ( process.env.DISABLE_CORS === 'TRUE' || - (origin && (allowList.indexOf(origin) !== -1 || regexAllowList.some((regex) => regex.test(origin)))) || - (appUrl && (allowList.indexOf(appUrl) !== -1 || regexAllowList.some((regex) => regex.test(appUrl)))) + allowList.indexOf(req.header('Origin')) !== -1 || + regexAllowList.some((regex) => regex.test(req.header('Origin'))) ) { - inUselogger.debug(`CORS enabled for origin: ${origin || appUrl}`); options.origin = true; } cb(null, options); }); - // Debug middleware to capture headers (only when DEBUG_HEADERS=true) - if (process.env.DEBUG_HEADERS === 'true') { - app.use((req, res, next) => { - inUselogger.debug('=== RAW REQUEST DEBUG ==='); - inUselogger.debug(`Method: ${req.method}`); - inUselogger.debug(`URL: ${req.url}`); - inUselogger.debug(`Origin: ${req.headers.origin}`); - inUselogger.debug(`Host: ${req.headers.host}`); - inUselogger.debug(`All Headers: ${JSON.stringify(req.headers, null, 2)}`); - inUselogger.debug('========================'); - next(); - }); - } + + app.use((req, res, next) => { + inUselogger.debug('=== RAW REQUEST DEBUG ==='); + inUselogger.debug(`Method: ${req.method}`); + inUselogger.debug(`URL: ${req.url}`); + inUselogger.debug(`Origin: ${req.headers.origin}`); + inUselogger.debug(`Host: ${req.headers.host}`); + inUselogger.debug(`All Headers: ${JSON.stringify(req.headers, null, 2)}`); + inUselogger.debug('========================'); + next(); + }); + app.use(logger); app.use( cookieParser(), diff --git a/cdk/dev2.env b/cdk/dev2.env index 208470cb5ee..a801642972b 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -31,5 +31,5 @@ BACKEND_API_CPUS=2 BACKEND_API_INSTANCES=3 API_PASS_KEY=dev-key -DEBUG_HEADERS=true -LOG_LEVEL=debug \ No newline at end of file +LOG_LEVEL=debug +DISABLE_CORS=TRUE \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-backend-service.ts b/cdk/lib/doorway_environment/doorway-backend-service.ts index 85a5fb01cf4..3df924dcd6c 100644 --- a/cdk/lib/doorway_environment/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/doorway-backend-service.ts @@ -73,7 +73,7 @@ export class DoorwayBackendService { SAME_SITE: process.env.SAME_SITE || "false", API_PASS_KEY: process.env.API_PASS_KEY || "doorway-api-key", APP_SECRET: process.env.APP_SECRET || "", - DEBUG_HEADERS: process.env.DEBUG_HEADERS || "false", + DISABLE_CORS: process.env.DISABLE_CORS || "FALSE", } const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) const dbSecret = secret.Secret.fromSecretCompleteArn(scope, "dbSecret", dbSecretArn) From 6663ce240f3053c21a1e9f649d3dc16fd6f98351 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sat, 30 Aug 2025 10:58:35 -0700 Subject: [PATCH 092/217] fix: ok lets try this the ham handed way --- api/src/main.ts | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/api/src/main.ts b/api/src/main.ts index 3bfe20a6a3b..5ce852a6ea7 100644 --- a/api/src/main.ts +++ b/api/src/main.ts @@ -39,22 +39,26 @@ async function bootstrap() { const { httpAdapter } = app.get(HttpAdapterHost); const inUselogger: Logger = app.get(Logger); app.useGlobalFilters(new CustomExceptionFilter(httpAdapter, inUselogger)); - app.enableCors((req, cb) => { - const options = { - credentials: true, - origin: false, - }; + if (process.env.DISABLE_CORS === 'TRUE') { + inUselogger.warn('CORS is disabled'); + } else { - if ( - process.env.DISABLE_CORS === 'TRUE' || - allowList.indexOf(req.header('Origin')) !== -1 || - regexAllowList.some((regex) => regex.test(req.header('Origin'))) - ) { - options.origin = true; - } - cb(null, options); - }); + app.enableCors((req, cb) => { + const options = { + credentials: true, + origin: false, + }; + + if ( + allowList.indexOf(req.header('Origin')) !== -1 || + regexAllowList.some((regex) => regex.test(req.header('Origin'))) + ) { + options.origin = true; + } + cb(null, options); + }); + } app.use((req, res, next) => { inUselogger.debug('=== RAW REQUEST DEBUG ==='); inUselogger.debug(`Method: ${req.method}`); From dff3cf67115849b28546abf5760435f5d4cd26cb Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sat, 30 Aug 2025 11:46:51 -0700 Subject: [PATCH 093/217] fix: changing db migration to reseed --- cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index c8e28cb1dd6..ad42b45b58e 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -146,7 +146,7 @@ export class DoorwayBuildPipelineStack extends Stack { }) const dbmigrate = new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev", { environment: "dev2", - buildspec: "./ci/buildspec/migrate.yml", + buildspec: "./ci/buildspec/migrate_stop_backend.yml", source: sourceArtifact, buildRole: buildRole, }).action From 89aea6e0348c8a0128412f415eb8a77e564dbcb1 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sat, 30 Aug 2025 11:47:46 -0700 Subject: [PATCH 094/217] fix: turning off cors disable --- cdk/dev2.env | 1 - 1 file changed, 1 deletion(-) diff --git a/cdk/dev2.env b/cdk/dev2.env index a801642972b..1e6e8ab20db 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -32,4 +32,3 @@ BACKEND_API_INSTANCES=3 API_PASS_KEY=dev-key LOG_LEVEL=debug -DISABLE_CORS=TRUE \ No newline at end of file From edaed2617cdd0b00863dfc9eb9aaeaedc0dbd6f1 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 12:31:27 -0700 Subject: [PATCH 095/217] fix: renaming services for dbmigrat --- cdk/dev2.env | 1 + .../doorway-build-pipeline-stack.ts | 3 +- .../doorway-database-migrate.ts | 7 ++-- .../doorway-app-environment-stack.ts | 35 ++++++++----------- .../doorway-backend-service.ts | 3 ++ .../doorway-partners-site.ts | 2 ++ cdk/lib/doorway_environment/doorway-props.ts | 6 ++++ .../doorway-public-site.ts | 4 +-- .../doorway_environment/doorway_service.ts | 5 +-- 9 files changed, 38 insertions(+), 28 deletions(-) diff --git a/cdk/dev2.env b/cdk/dev2.env index 1e6e8ab20db..af1d751e563 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -32,3 +32,4 @@ BACKEND_API_INSTANCES=3 API_PASS_KEY=dev-key LOG_LEVEL=debug + diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index ad42b45b58e..35166b991f9 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -159,7 +159,8 @@ export class DoorwayBuildPipelineStack extends Stack { pipeline.addStage({ stageName: "Dev", - actions: [dbmigrate, ecsDeploy], + // actions: [dbmigrate, ecsDeploy], + actions: [ecsDeploy], }) } } diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts index f8902d2bded..8298d401649 100644 --- a/cdk/lib/build-pipeline/doorway-database-migrate.ts +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -9,6 +9,7 @@ import { Construct } from "constructs" // Removed fs and YAML imports since we're using BuildSpec.fromSourceFilename + export interface DoorwayDatabaseMigrateProps { ecrNamespace?: string databaseName?: string @@ -21,6 +22,8 @@ export interface DoorwayDatabaseMigrateProps { export class DoorwayDatabaseMigrate { public readonly action: CodeBuildAction constructor(scope: Construct, id: string, props: DoorwayDatabaseMigrateProps) { + const ecsClusterName = `doorway-ecs-cluster-${props.environment}` + const backendServiceName = `doorway-backend-${props.environment}` const vpcId = Fn.importValue(`doorway-vpc-id-${props.environment}`) const subnetId = Fn.importValue(`doorway-app-subnet-1-${props.environment}`) const securityGroupId = Fn.importValue(`doorway-app-sg-${props.environment}`) @@ -57,10 +60,10 @@ export class DoorwayDatabaseMigrate { LANGUAGES: { value: "en,es,zh,vi,tl" }, IDLE_TIMEOUT: { value: "5" }, ECS_BACKEND_SERVICE: { - value: `doorway-${props.environment}-internal-api`, + value: backendServiceName, }, ECS_BACKEND_CLUSTER: { - value: `doorway-ecs-cluster-${props.environment}`, + value: ecsClusterName, }, }, role: props.buildRole, diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 459ad0c8e24..12726413400 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -1,4 +1,4 @@ -import { aws_logs, RemovalPolicy, Stack } from "aws-cdk-lib" +import { aws_logs, Fn, RemovalPolicy, Stack } from "aws-cdk-lib" import { LogGroup } from "aws-cdk-lib/aws-logs" import { Construct } from "constructs" import dotenv from "dotenv" @@ -6,6 +6,7 @@ import path from "path" import { DoorwayBackendService } from "./doorway-backend-service" import { DoorwayPartnersSite } from "./doorway-partners-site" +import { DoorwayProps } from "./doorway-props" import { DoorwayPublicLoadBalancer } from "./doorway-public-load-balancer" import { DoorwayPublicSite } from "./doorway-public-site" @@ -29,31 +30,23 @@ export class DoorwayAppEnvironmentStack extends Stack { const scLogGroup = new LogGroup(this, `${id}-service-connect-log-group`, { logGroupName: `/doorway/${environment}/service-connect` }) - - const api = new DoorwayBackendService(this, `doorway-api-service-${environment}`, { - environment: environment, - logGroup: logGroup, - serviceConnectLogGroup: scLogGroup - - }) - const lb = new DoorwayPublicLoadBalancer(this, `doorway-public-lb-${environment}`, { + const props: DoorwayProps = { environment: environment, + serviceConnectLogGroup: scLogGroup, logGroup: logGroup, - serviceConnectLogGroup: scLogGroup - }); - const publicSite = new DoorwayPublicSite(this, `doorway-public-${environment}`, { - environment: environment, - logGroup: logGroup, - serviceConnectLogGroup: scLogGroup - }) + backendServiceName: `doorway-backend-${environment}`, + publicServiceName: `doorway-public-${environment}`, + partnersServiceName: `doorway-partners-${environment}`, + clusterName: Fn.importValue(`doorway-ecs-cluster-${environment}`), + } + + const api = new DoorwayBackendService(this, `doorway-api-service-${environment}`, props) + const lb = new DoorwayPublicLoadBalancer(this, `doorway-public-lb-${environment}`, props); + const publicSite = new DoorwayPublicSite(this, `doorway-public-${environment}`, props) publicSite.service.node.addDependency(api.service) publicSite.service.node.addDependency(lb.loadBalancer) lb.publicTargetGroup.addTarget(publicSite.service) - const partnersSite = new DoorwayPartnersSite(this, `doorway-partners-${environment}`, { - environment: environment, - logGroup: logGroup, - serviceConnectLogGroup: scLogGroup - }) + const partnersSite = new DoorwayPartnersSite(this, `doorway-partners-${environment}`, props) partnersSite.service.node.addDependency(api.service) partnersSite.service.node.addDependency(lb.loadBalancer) lb.partnersTargetGroup.addTarget(partnersSite.service) diff --git a/cdk/lib/doorway_environment/doorway-backend-service.ts b/cdk/lib/doorway_environment/doorway-backend-service.ts index 3df924dcd6c..a5cc7b81bd1 100644 --- a/cdk/lib/doorway_environment/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/doorway-backend-service.ts @@ -159,6 +159,9 @@ export class DoorwayBackendService { environment: props.environment, container: `doorway/backend:run-${gitHash}`, securityGroup: privateSG, + serviceName: props.backendServiceName, + clusterName: props.clusterName + }).service } } diff --git a/cdk/lib/doorway_environment/doorway-partners-site.ts b/cdk/lib/doorway_environment/doorway-partners-site.ts index 8f279f900a6..26ed05ffb73 100644 --- a/cdk/lib/doorway_environment/doorway-partners-site.ts +++ b/cdk/lib/doorway_environment/doorway-partners-site.ts @@ -86,6 +86,8 @@ export class DoorwayPartnersSite { apiTargetPort: Number(process.env.BACKEND_API_PORT || 3000), container: `doorway/partners:run-${process.env.ENVIRONMENT || "dev2"}-${gitHash}`, securityGroup: privateSG, + serviceName: props.partnersServiceName, + clusterName: props.clusterName }).service } diff --git a/cdk/lib/doorway_environment/doorway-props.ts b/cdk/lib/doorway_environment/doorway-props.ts index 36503a71575..422babc070a 100644 --- a/cdk/lib/doorway_environment/doorway-props.ts +++ b/cdk/lib/doorway_environment/doorway-props.ts @@ -8,6 +8,10 @@ export interface DoorwayProps { environment: string, logGroup: LogGroup serviceConnectLogGroup: LogGroup + clusterName: string + backendServiceName: string + publicServiceName: string + partnersServiceName: string } export interface DoorwayServiceProps extends DoorwayProps { @@ -26,6 +30,8 @@ export interface DoorwayServiceProps extends DoorwayProps { secureUploads: IBucket container: string securityGroup: ISecurityGroup + serviceName: string + clusterName: string } export interface DoorwayLoadBalancerProps extends DoorwayProps { diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/doorway-public-site.ts index b2e885f7d61..68cca12d27c 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/doorway-public-site.ts @@ -90,8 +90,8 @@ export class DoorwayPublicSite { apiTargetPort: Number(process.env.BACKEND_API_PORT || 3000), container: `doorway/public:run-${process.env.ENVIRONMENT || "dev2"}-${gitHash}`, securityGroup: privateSG, - - + serviceName: props.publicServiceName, + clusterName: props.clusterName }).service } diff --git a/cdk/lib/doorway_environment/doorway_service.ts b/cdk/lib/doorway_environment/doorway_service.ts index e440ab77153..b267a13dad2 100644 --- a/cdk/lib/doorway_environment/doorway_service.ts +++ b/cdk/lib/doorway_environment/doorway_service.ts @@ -48,6 +48,7 @@ export class DoorwayService { }) const task = new TaskDefinition(scope, `${id}-task`, { + compatibility: Compatibility.FARGATE, executionRole: props.executionRole, taskRole: props.executionRole, @@ -140,7 +141,7 @@ export class DoorwayService { }, portMappingName: `${id}-port-mapping`, dnsName: props.domainName, - discoveryName: `doorway-${props.environment}-internal-api`, + discoveryName: props.serviceName, port: props.port, }], @@ -149,7 +150,7 @@ export class DoorwayService { this.service = new FargateService(scope, `${id}-fargate-service`, { taskDefinition: task, - serviceName: `${id}-service`, + serviceName: props.serviceName, circuitBreaker: { enable: true, rollback: true, From 7aa581d7ab09eb9406817692465bcfa0147a5643 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 13:40:08 -0700 Subject: [PATCH 096/217] fix: applying migration cmd --- build/docker/entrypoint-migrate.sh | 2 +- cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh index 66d37cde7d3..0626caa66cd 100644 --- a/build/docker/entrypoint-migrate.sh +++ b/build/docker/entrypoint-migrate.sh @@ -1,5 +1,5 @@ #!/bin/sh export DATABASE_URL="postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" if [ "$SKIP_MIGRATIONS" = TRUE ]; then yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ - else yarn db:migration:run; \ + else yarn ${MIGRATION_CMD}; \ fi \ No newline at end of file diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 35166b991f9..ad42b45b58e 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -159,8 +159,7 @@ export class DoorwayBuildPipelineStack extends Stack { pipeline.addStage({ stageName: "Dev", - // actions: [dbmigrate, ecsDeploy], - actions: [ecsDeploy], + actions: [dbmigrate, ecsDeploy], }) } } From 8d390e89560b69c951fc4a2f5907b7df2a0cab5c Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 14:00:13 -0700 Subject: [PATCH 097/217] fix: trying with entrypoint --- ci/buildspec/migrate_stop_backend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 4a1184952fb..e949bd46a4a 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -59,4 +59,4 @@ phases: --env PARTNERS_BASE_URL="${PARTNERS_BASE_URL:-http://localhost:3001/not-used}" --env PARTNERS_PORTAL_URL="${PARTNERS_PORTAL_URL:-http://localhost:3001/not-used}" --env SKIP_MIGRATIONS=FALSE - "${MIGRATION_IMAGE}" sh -c "yarn ${MIGRATION_CMD}" + "${MIGRATION_IMAGE}" sh --entrypoint "yarn ${MIGRATION_CMD}" From 16588833c23d5a06f170a910c39bf6e8d577672e Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 14:14:59 -0700 Subject: [PATCH 098/217] fix: reverting to old format --- build/docker/Dockerfile.backend | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index 04657690584..11d44607b0b 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -162,6 +162,7 @@ COPY --from=source --chown=node:node /app/api/scripts ./scripts ## This CMD should be run as the node user USER node -COPY --chown=node:node ./build/docker/entrypoint-migrate.sh /app/ -RUN chmod 755 /app/entrypoint-migrate.sh -ENTRYPOINT /app/entrypoint-migrate.sh +# We need to skip all migrations that are just for local development (changes already exist in deployed environment) +CMD if [ "$SKIP_MIGRATIONS" = TRUE ]; then yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ + else yarn db:migration:run; \ + fi \ No newline at end of file From 3567b3ae692e597e56832f7c5dbbb93d72fdde63 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 14:17:14 -0700 Subject: [PATCH 099/217] fixing docker cmd --- ci/buildspec/migrate_stop_backend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index e949bd46a4a..4a1184952fb 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -59,4 +59,4 @@ phases: --env PARTNERS_BASE_URL="${PARTNERS_BASE_URL:-http://localhost:3001/not-used}" --env PARTNERS_PORTAL_URL="${PARTNERS_PORTAL_URL:-http://localhost:3001/not-used}" --env SKIP_MIGRATIONS=FALSE - "${MIGRATION_IMAGE}" sh --entrypoint "yarn ${MIGRATION_CMD}" + "${MIGRATION_IMAGE}" sh -c "yarn ${MIGRATION_CMD}" From ec37db24d52e659aa96ad4e10960b39f25c2f57f Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 14:45:15 -0700 Subject: [PATCH 100/217] trying to get back to initial docker build for migration --- build/docker/Dockerfile.backend | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index 11d44607b0b..11e84497135 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -130,9 +130,8 @@ RUN chmod 777 ./src/temp USER node RUN yarn prisma generate -COPY --chown=node:node ./build/docker/entrypoint-run.sh /app/ -RUN chmod 755 /app/entrypoint-run.sh -ENTRYPOINT /app/entrypoint-run.sh + +CMD yarn start:prod ##################### ##### NEW STAGE ##### @@ -162,7 +161,8 @@ COPY --from=source --chown=node:node /app/api/scripts ./scripts ## This CMD should be run as the node user USER node + # We need to skip all migrations that are just for local development (changes already exist in deployed environment) CMD if [ "$SKIP_MIGRATIONS" = TRUE ]; then yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ else yarn db:migration:run; \ - fi \ No newline at end of file + fi From df1e51c1329dfe0fbdc681564ee4a33942a23df6 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 17:15:32 -0700 Subject: [PATCH 101/217] fix: changing db url --- ci/buildspec/migrate.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index 630a65e8088..3a4c34d58fa 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -29,6 +29,7 @@ phases: # Default to db:migration:run - export MIGRATION_CMD="${MIGRATION_CMD:-db:migration:run}" + - export DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" # Execute migration # Note that many of these vars are required by the app but not used for migration @@ -39,7 +40,7 @@ phases: --env PGHOST="${PGHOST}" --env PGDATABASE=template1 --env PGPORT="${PGPORT}" - --env DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" + --env DATABASE_URL=${DATABASE_URL} --env CLOUDINARY_CLOUD_NAME="${CLOUDINARY_CLOUD_NAME}" --env LISTINGS_QUERY="${LISTINGS_QUERY}" --env FILE_SERVICE="${FILE_SERVICE:-cloudinary}" From 2122d6b083c804a9ba8122180720f3e113a13d3a Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 17:47:09 -0700 Subject: [PATCH 102/217] fix: hard coding port --- ci/buildspec/migrate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index 3a4c34d58fa..b41addb51d0 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -29,7 +29,7 @@ phases: # Default to db:migration:run - export MIGRATION_CMD="${MIGRATION_CMD:-db:migration:run}" - - export DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" + - export DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:5432/${PGDATABASE}" # Execute migration # Note that many of these vars are required by the app but not used for migration From 10c64c808885ae5bb7ab37f80dd634023919ebe5 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 17:51:56 -0700 Subject: [PATCH 103/217] fix: temporary image change --- ci/buildspec/migrate.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index b41addb51d0..1216f29cba4 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -21,8 +21,10 @@ phases: build: commands: - export ECR_REPO="${ECR_ACCOUNT_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com/${ECR_NAMESPACE}" + #FIXME: switch back to migration image once we have migrations to run + - export MIGRATION_IMAGE="${ECR_REPO}/backend:migrate-candidate" + #- export MIGRATION_IMAGE="${ECR_REPO}/backend:migrate-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" - - export MIGRATION_IMAGE="${ECR_REPO}/backend:migrate-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" # Pull down migration candidate image - docker pull "${MIGRATION_IMAGE}" From 2b46779b69b854e163553ff2354c6ee82908b645 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 18:11:56 -0700 Subject: [PATCH 104/217] fix: moving secrets stuff to main --- ci/buildspec/migrate.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index 1216f29cba4..68ff3c6eb41 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -7,12 +7,6 @@ version: 0.2 env: shell: bash - secrets-manager: - PGUSER: "${DB_CREDS_ARN}:username" - PGPASSWORD: "${DB_CREDS_ARN}:password" - PGHOST: "${DB_CREDS_ARN}:host" - PGPORT: "${DB_CREDS_ARN}:port" - PGDATABASE: "${DB_CREDS_ARN}:dbname" phases: pre_build: @@ -24,7 +18,11 @@ phases: #FIXME: switch back to migration image once we have migrations to run - export MIGRATION_IMAGE="${ECR_REPO}/backend:migrate-candidate" #- export MIGRATION_IMAGE="${ECR_REPO}/backend:migrate-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" - + - export PGUSER=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN|jq ."SecretString"|jq -r|jq ."username") + - export PGPASSWORD=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN|jq ."SecretString"|jq -r|jq ."password") + - export PGPORT=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN|jq ."SecretString"|jq -r|jq ."port") + - export PGHOST=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN|jq ."SecretString"|jq -r|jq ."host") + - export PGDATABASE=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN|jq ."SecretString"|jq -r|jq ."dbname") # Pull down migration candidate image - docker pull "${MIGRATION_IMAGE}" From 49fe1092ad87c553ce22b226dc8a0cef1dee636f Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 18:16:00 -0700 Subject: [PATCH 105/217] fix: missing bracket --- ci/buildspec/migrate.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index 68ff3c6eb41..19c7d73e480 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -18,11 +18,11 @@ phases: #FIXME: switch back to migration image once we have migrations to run - export MIGRATION_IMAGE="${ECR_REPO}/backend:migrate-candidate" #- export MIGRATION_IMAGE="${ECR_REPO}/backend:migrate-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" - - export PGUSER=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN|jq ."SecretString"|jq -r|jq ."username") - - export PGPASSWORD=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN|jq ."SecretString"|jq -r|jq ."password") - - export PGPORT=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN|jq ."SecretString"|jq -r|jq ."port") - - export PGHOST=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN|jq ."SecretString"|jq -r|jq ."host") - - export PGDATABASE=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN|jq ."SecretString"|jq -r|jq ."dbname") + - export PGUSER=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq ."username") + - export PGPASSWORD=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq ."password") + - export PGPORT=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq ."port") + - export PGHOST=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq ."host") + - export PGDATABASE=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq ."dbname") # Pull down migration candidate image - docker pull "${MIGRATION_IMAGE}" From 541a98f301527b1ff346d44314a53278799d8cb4 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 18:19:48 -0700 Subject: [PATCH 106/217] fix: update migrate.yml --- ci/buildspec/migrate.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index 19c7d73e480..3dff6a1b64d 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -18,11 +18,11 @@ phases: #FIXME: switch back to migration image once we have migrations to run - export MIGRATION_IMAGE="${ECR_REPO}/backend:migrate-candidate" #- export MIGRATION_IMAGE="${ECR_REPO}/backend:migrate-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" - - export PGUSER=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq ."username") - - export PGPASSWORD=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq ."password") - - export PGPORT=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq ."port") - - export PGHOST=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq ."host") - - export PGDATABASE=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq ."dbname") + - export PGUSER=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."username") + - export PGPASSWORD=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."password") + - export PGPORT=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."port") + - export PGHOST=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."host") + - export PGDATABASE=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."dbname") # Pull down migration candidate image - docker pull "${MIGRATION_IMAGE}" From 66e61e7cfbd84d89f3b80cb804cdbdc6e3ae5fac Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 18:23:48 -0700 Subject: [PATCH 107/217] fix: debugging --- ci/buildspec/migrate.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index 3dff6a1b64d..b44e890ebf7 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -30,6 +30,7 @@ phases: # Default to db:migration:run - export MIGRATION_CMD="${MIGRATION_CMD:-db:migration:run}" - export DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:5432/${PGDATABASE}" + - echo "DATABASE_URL=${DATABASE_URL}" # Execute migration # Note that many of these vars are required by the app but not used for migration From e849636af6c1f9408454eb540abed6171aad7da9 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 18:27:27 -0700 Subject: [PATCH 108/217] fix: more debugging --- ci/buildspec/migrate.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index b44e890ebf7..f4ef2d251b2 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -30,7 +30,9 @@ phases: # Default to db:migration:run - export MIGRATION_CMD="${MIGRATION_CMD:-db:migration:run}" - export DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:5432/${PGDATABASE}" - - echo "DATABASE_URL=${DATABASE_URL}" + - echo $PGHOST + - echo $PGDATABASE + - echo $DATABASE_URL # Execute migration # Note that many of these vars are required by the app but not used for migration From 6277b134b80276c2837f67959131e764184637fd Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 18:34:27 -0700 Subject: [PATCH 109/217] fix: trying without DATABASE_URL --- ci/buildspec/migrate.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index f4ef2d251b2..efe56914f6f 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -29,10 +29,7 @@ phases: # Default to db:migration:run - export MIGRATION_CMD="${MIGRATION_CMD:-db:migration:run}" - - export DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:5432/${PGDATABASE}" - - echo $PGHOST - - echo $PGDATABASE - - echo $DATABASE_URL + # Execute migration # Note that many of these vars are required by the app but not used for migration @@ -43,7 +40,6 @@ phases: --env PGHOST="${PGHOST}" --env PGDATABASE=template1 --env PGPORT="${PGPORT}" - --env DATABASE_URL=${DATABASE_URL} --env CLOUDINARY_CLOUD_NAME="${CLOUDINARY_CLOUD_NAME}" --env LISTINGS_QUERY="${LISTINGS_QUERY}" --env FILE_SERVICE="${FILE_SERVICE:-cloudinary}" From ec8586744f549d8bfa44fce2b5d7771b67dc40e3 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 18:40:33 -0700 Subject: [PATCH 110/217] yet another attempt --- ci/buildspec/migrate.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index efe56914f6f..44c6894c4f4 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -29,7 +29,11 @@ phases: # Default to db:migration:run - export MIGRATION_CMD="${MIGRATION_CMD:-db:migration:run}" - + + # URL encode the password to handle special characters + - export ENCODED_PASSWORD=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${PGPASSWORD}', safe=''))") + - export DATABASE_URL="postgres://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" + - echo "Database URL (password masked): postgres://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" # Execute migration # Note that many of these vars are required by the app but not used for migration @@ -38,8 +42,9 @@ phases: --env PGUSER="${PGUSER}" --env PGPASSWORD="${PGPASSWORD}" --env PGHOST="${PGHOST}" - --env PGDATABASE=template1 + --env PGDATABASE="${PGDATABASE}" --env PGPORT="${PGPORT}" + --env DATABASE_URL="${DATABASE_URL}" --env CLOUDINARY_CLOUD_NAME="${CLOUDINARY_CLOUD_NAME}" --env LISTINGS_QUERY="${LISTINGS_QUERY}" --env FILE_SERVICE="${FILE_SERVICE:-cloudinary}" From 525926c0ec0e5be8917a300d233f50810509d671 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 18:58:16 -0700 Subject: [PATCH 111/217] fix: once more --- ci/buildspec/migrate.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index 44c6894c4f4..03c5ea00810 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -29,11 +29,10 @@ phases: # Default to db:migration:run - export MIGRATION_CMD="${MIGRATION_CMD:-db:migration:run}" - # URL encode the password to handle special characters - export ENCODED_PASSWORD=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${PGPASSWORD}', safe=''))") - export DATABASE_URL="postgres://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" - - echo "Database URL (password masked): postgres://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" + - echo "Database URL (password masked):postgres://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" # Execute migration # Note that many of these vars are required by the app but not used for migration From 5bb41447377bb2454e4647ae2a51689e865744c4 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 19:49:57 -0700 Subject: [PATCH 112/217] fix: lets do this bad boy --- .../doorway-build-pipeline-stack.ts | 1 + .../build-pipeline/doorway-database-migrate.ts | 4 ++++ ci/buildspec/migrate.yml | 4 ++-- ci/buildspec/migrate_stop_backend.yml | 17 ++++++++++------- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index ad42b45b58e..9e1fde1193a 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -144,6 +144,7 @@ export class DoorwayBuildPipelineStack extends Stack { }).action, ], }) + const dbmigrate = new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev", { environment: "dev2", buildspec: "./ci/buildspec/migrate_stop_backend.yml", diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts index 8298d401649..1d6d4259210 100644 --- a/cdk/lib/build-pipeline/doorway-database-migrate.ts +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -59,12 +59,16 @@ export class DoorwayDatabaseMigrate { CACHE_REVALIDATE: { value: "60" }, LANGUAGES: { value: "en,es,zh,vi,tl" }, IDLE_TIMEOUT: { value: "5" }, + PGPORT: { value: "5432" }, ECS_BACKEND_SERVICE: { value: backendServiceName, }, ECS_BACKEND_CLUSTER: { value: ecsClusterName, }, + MIGRATION_CMD: { + value: "db:reseed:ci", + }, }, role: props.buildRole, }) diff --git a/ci/buildspec/migrate.yml b/ci/buildspec/migrate.yml index 03c5ea00810..c9c42c1d855 100644 --- a/ci/buildspec/migrate.yml +++ b/ci/buildspec/migrate.yml @@ -16,8 +16,8 @@ phases: commands: - export ECR_REPO="${ECR_ACCOUNT_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com/${ECR_NAMESPACE}" #FIXME: switch back to migration image once we have migrations to run - - export MIGRATION_IMAGE="${ECR_REPO}/backend:migrate-candidate" - #- export MIGRATION_IMAGE="${ECR_REPO}/backend:migrate-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + #- export MIGRATION_IMAGE="${ECR_REPO}/backend:migrate-candidate" + - export MIGRATION_IMAGE="${ECR_REPO}/backend:migrate-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" - export PGUSER=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."username") - export PGPASSWORD=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."password") - export PGPORT=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."port") diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 4a1184952fb..5cc9f3d9998 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -5,13 +5,6 @@ version: 0.2 env: shell: bash - secrets-manager: - PGUSER: "${DB_CREDS_ARN}:username" - PGPASSWORD: "${DB_CREDS_ARN}:password" - PGHOST: "${DB_CREDS_ARN}:host" - PGPORT: "${DB_CREDS_ARN}:port" - PGDATABASE: "${DB_CREDS_ARN}:dbname" - phases: pre_build: commands: @@ -41,6 +34,16 @@ phases: # Execute migration # Note that many of these vars are required by the app but not used for migration - > + - export PGUSER=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."username") + - export PGPASSWORD=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."password") + - export PGPORT=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."port") + - export PGHOST=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."host") + - export PGDATABASE=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."dbname") + # URL encode the password to handle special characters + - export ENCODED_PASSWORD=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${PGPASSWORD}', safe=''))") + - export DATABASE_URL="postgres://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" + - echo "Database URL (password masked):postgres://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" + docker run --env PGUSER="${PGUSER}" --env PGPASSWORD="${PGPASSWORD}" From 53fdf8c142f19a100ba2635c488e84c4e2feef39 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 21:38:40 -0700 Subject: [PATCH 113/217] fix: adding the entrypoint run --- build/docker/Dockerfile.backend | 8 ++++++-- cdk/lib/doorway_environment/doorway_service.ts | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index 11e84497135..bd5f36848bc 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -116,6 +116,7 @@ COPY --from=source --chown=node:node \ /app/api/yarn*.lock \ /app/api/prisma ./ + # Add only dependencies required to run the application COPY --from=optimize --chown=node:node /app/node_modules ./node_modules RUN yarn prisma generate @@ -130,8 +131,8 @@ RUN chmod 777 ./src/temp USER node RUN yarn prisma generate - -CMD yarn start:prod +COPY build/docker/entrypoint-run.sh /app/api/entrypoint-run.sh +ENTRYPOINT [ "entrypoint-run.sh" ] ##################### ##### NEW STAGE ##### @@ -162,6 +163,9 @@ COPY --from=source --chown=node:node /app/api/scripts ./scripts ## This CMD should be run as the node user USER node +# Override the inherited ENTRYPOINT for migrations +ENTRYPOINT [] + # We need to skip all migrations that are just for local development (changes already exist in deployed environment) CMD if [ "$SKIP_MIGRATIONS" = TRUE ]; then yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ else yarn db:migration:run; \ diff --git a/cdk/lib/doorway_environment/doorway_service.ts b/cdk/lib/doorway_environment/doorway_service.ts index b267a13dad2..fedfe3f105a 100644 --- a/cdk/lib/doorway_environment/doorway_service.ts +++ b/cdk/lib/doorway_environment/doorway_service.ts @@ -109,6 +109,7 @@ export class DoorwayService { assumedBy: new CompositePrincipal( new ServicePrincipal("ecs.amazonaws.com"), new ServicePrincipal("ecs-tasks.amazonaws.com"), + new ServicePrincipal("ecs.application-autoscaling.amazonaws.com") ), managedPolicies: [ ManagedPolicy.fromAwsManagedPolicyName( @@ -125,6 +126,7 @@ export class DoorwayService { ], }), }, + description: "Service Connect TLS role for ECS service communication", }) props.logGroup.grantWrite(scRole) serviceConnectProps = { From cefec6a4a2007fc29f87dc4eee434df8e1bb28aa Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 21:39:01 -0700 Subject: [PATCH 114/217] fix: missed one --- build/docker/Dockerfile.backend | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index bd5f36848bc..fe30d9b5f9a 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -132,7 +132,9 @@ USER node RUN yarn prisma generate COPY build/docker/entrypoint-run.sh /app/api/entrypoint-run.sh -ENTRYPOINT [ "entrypoint-run.sh" ] +RUN chmod +x /app/api/entrypoint-run.sh +RUN chown node:node /app/api/entrypoint-run.sh +ENTRYPOINT [ "/app/api/entrypoint-run.sh" ] ##################### ##### NEW STAGE ##### @@ -163,8 +165,6 @@ COPY --from=source --chown=node:node /app/api/scripts ./scripts ## This CMD should be run as the node user USER node -# Override the inherited ENTRYPOINT for migrations -ENTRYPOINT [] # We need to skip all migrations that are just for local development (changes already exist in deployed environment) CMD if [ "$SKIP_MIGRATIONS" = TRUE ]; then yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ From 8d258d8eaab4186b201920fd4aee12302356cee5 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 21:44:00 -0700 Subject: [PATCH 115/217] fix: moving commands around --- build/docker/Dockerfile.backend | 3 ++- cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index fe30d9b5f9a..19b8e0adda9 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -132,8 +132,9 @@ USER node RUN yarn prisma generate COPY build/docker/entrypoint-run.sh /app/api/entrypoint-run.sh -RUN chmod +x /app/api/entrypoint-run.sh + RUN chown node:node /app/api/entrypoint-run.sh +RUN chmod 755 /app/api/entrypoint-run.sh ENTRYPOINT [ "/app/api/entrypoint-run.sh" ] ##################### diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 9e1fde1193a..082c1ec8d21 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -78,6 +78,7 @@ export class DoorwayBuildPipelineStack extends Stack { }, }) + const dockerSecret = Secret.fromSecretNameV2( this, "dockerSecret", @@ -151,6 +152,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, buildRole: buildRole, }).action + const ecsDeploy = new DoorwayECSDeploy(this, "doorway-ecs-deploy-dev", { buildspec: "./ci/buildspec/update_ecs.yml", source: sourceArtifact, @@ -160,7 +162,7 @@ export class DoorwayBuildPipelineStack extends Stack { pipeline.addStage({ stageName: "Dev", - actions: [dbmigrate, ecsDeploy], + actions: [ecsDeploy], }) } } From d03691152dcc3b27b4a58d6b5a1809567770bc95 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 21:51:14 -0700 Subject: [PATCH 116/217] fix: more moving --- build/docker/Dockerfile.backend | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index 19b8e0adda9..a05bee25a52 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -127,14 +127,15 @@ RUN mkdir ./src RUN mkdir ./src/temp RUN chmod 777 ./src/temp +# Copy and set permissions for entrypoint script as root +COPY build/docker/entrypoint-run.sh /app/api/entrypoint-run.sh +RUN chown node:node /app/api/entrypoint-run.sh +RUN chmod 755 /app/api/entrypoint-run.sh + # When running this image, make sure to use the built-in node user USER node RUN yarn prisma generate -COPY build/docker/entrypoint-run.sh /app/api/entrypoint-run.sh - -RUN chown node:node /app/api/entrypoint-run.sh -RUN chmod 755 /app/api/entrypoint-run.sh ENTRYPOINT [ "/app/api/entrypoint-run.sh" ] ##################### From 6129a24371bfe4d5371bef2864ba114c7beb68a0 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Sun, 31 Aug 2025 22:27:37 -0700 Subject: [PATCH 117/217] moving prisma generate --- build/docker/Dockerfile.backend | 2 +- build/docker/entrypoint-run.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index a05bee25a52..6b7d350f999 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -135,7 +135,7 @@ RUN chmod 755 /app/api/entrypoint-run.sh # When running this image, make sure to use the built-in node user USER node -RUN yarn prisma generate + ENTRYPOINT [ "/app/api/entrypoint-run.sh" ] ##################### diff --git a/build/docker/entrypoint-run.sh b/build/docker/entrypoint-run.sh index 5ee03b55933..6371acc54e2 100644 --- a/build/docker/entrypoint-run.sh +++ b/build/docker/entrypoint-run.sh @@ -1,3 +1,3 @@ #!/bin/sh export DATABASE_URL="postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" -yarn start:prod +yarn prisma generate&&yarn start:prod From fb3e9a2348f58b406120a8a80212c83bd09d4697 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 09:11:59 -0700 Subject: [PATCH 118/217] fix: debugging docker --- build/docker/entrypoint-run.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/docker/entrypoint-run.sh b/build/docker/entrypoint-run.sh index 6371acc54e2..ecba992bef2 100644 --- a/build/docker/entrypoint-run.sh +++ b/build/docker/entrypoint-run.sh @@ -1,3 +1,5 @@ #!/bin/sh export DATABASE_URL="postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" +echo "DATABASE_URL: $DATABASE_URL" +echo "Running migrations..." yarn prisma generate&&yarn start:prod From a2f803fe3fd42a17fe64cb015073ae5da17b9504 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 10:35:19 -0700 Subject: [PATCH 119/217] fix: encoding password --- build/docker/entrypoint-run.sh | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/build/docker/entrypoint-run.sh b/build/docker/entrypoint-run.sh index ecba992bef2..b0f8c7cd3cb 100644 --- a/build/docker/entrypoint-run.sh +++ b/build/docker/entrypoint-run.sh @@ -1,5 +1,23 @@ #!/bin/sh -export DATABASE_URL="postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" -echo "DATABASE_URL: $DATABASE_URL" + +# Debug the individual secret values +echo "PGUSER: $PGUSER" +echo "PGHOST: $PGHOST" +echo "PGPORT: $PGPORT" +echo "PGDATABASE: $PGDATABASE" +echo "PGPASSWORD length: ${#PGPASSWORD}" + +# Validate port is numeric +if ! echo "$PGPORT" | grep -qE '^[0-9]+$'; then + echo "ERROR: PGPORT is not a valid number: '$PGPORT'" + exit 1 +fi + +# URL encode the password to handle special characters +ENCODED_PASSWORD=$(node -e "console.log(encodeURIComponent('$PGPASSWORD'))") + +export DATABASE_URL="postgresql://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" +echo "DATABASE_URL (password masked): postgresql://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" + echo "Running migrations..." -yarn prisma generate&&yarn start:prod +yarn prisma generate && yarn start:prod From a078402e2a75592cd9e976a697207645094eb481 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 11:00:54 -0700 Subject: [PATCH 120/217] getting rid of API key --- cdk/dev2.env | 2 -- cdk/lib/doorway_environment/doorway-backend-service.ts | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/cdk/dev2.env b/cdk/dev2.env index af1d751e563..0c19299dd71 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -29,7 +29,5 @@ BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID BACKEND_API_MEMORY=4096 BACKEND_API_CPUS=2 BACKEND_API_INSTANCES=3 -API_PASS_KEY=dev-key - LOG_LEVEL=debug diff --git a/cdk/lib/doorway_environment/doorway-backend-service.ts b/cdk/lib/doorway_environment/doorway-backend-service.ts index a5cc7b81bd1..ac6a778fecd 100644 --- a/cdk/lib/doorway_environment/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/doorway-backend-service.ts @@ -7,8 +7,8 @@ import * as secret from "aws-cdk-lib/aws-secretsmanager" import { EmailIdentity } from "aws-cdk-lib/aws-ses" import { Construct } from "constructs" -import { DoorwayProps } from "./doorway-props" import { DoorwayService } from "./doorway_service" +import { DoorwayProps } from "./doorway-props" export class DoorwayBackendService { public service: FargateService @@ -71,7 +71,6 @@ export class DoorwayBackendService { DUPLICATES_CLOSE_DATE: process.env.DUPLICATES_CLOSE_DATE || "2024-10-08 00:00 -08:00", HTTPS_OFF: process.env.HTTPS_OFF || "TRUE", SAME_SITE: process.env.SAME_SITE || "false", - API_PASS_KEY: process.env.API_PASS_KEY || "doorway-api-key", APP_SECRET: process.env.APP_SECRET || "", DISABLE_CORS: process.env.DISABLE_CORS || "FALSE", } From 5caeda151ccab36a0a4264cec0a079c22ff08cd4 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 12:06:40 -0700 Subject: [PATCH 121/217] fix: moving things around and fixing PGPORT --- .../doorway-app-environment-stack.ts | 7 +++---- .../doorway-lambda-props.ts | 20 ------------------- cdk/lib/doorway_environment/doorway-props.ts | 20 +++++++++++++++++++ .../doorway-public-load-balancer.ts | 2 +- .../{ => services}/doorway-backend-service.ts | 2 +- .../{ => services}/doorway-partners-site.ts | 3 +-- .../{ => services}/doorway-public-site.ts | 2 +- .../{ => services}/doorway_service.ts | 4 ++-- .../lambda-base-class.ts | 2 +- .../restart-services-lambda.ts | 2 +- .../restart-services.handler.ts | 0 ci/buildspec/migrate_stop_backend.yml | 1 + 12 files changed, 32 insertions(+), 33 deletions(-) delete mode 100644 cdk/lib/doorway_environment/doorway-lambda-props.ts rename cdk/lib/doorway_environment/{ => services}/doorway-backend-service.ts (99%) rename cdk/lib/doorway_environment/{ => services}/doorway-partners-site.ts (98%) rename cdk/lib/doorway_environment/{ => services}/doorway-public-site.ts (98%) rename cdk/lib/doorway_environment/{ => services}/doorway_service.ts (97%) rename cdk/lib/doorway_environment/{ => utility_lambdas}/lambda-base-class.ts (97%) rename cdk/lib/doorway_environment/{ => utility_lambdas}/restart-services-lambda.ts (97%) rename cdk/lib/doorway_environment/{ => utility_lambdas}/restart-services.handler.ts (100%) diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 12726413400..b8e515112e0 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -3,12 +3,11 @@ import { LogGroup } from "aws-cdk-lib/aws-logs" import { Construct } from "constructs" import dotenv from "dotenv" import path from "path" - -import { DoorwayBackendService } from "./doorway-backend-service" -import { DoorwayPartnersSite } from "./doorway-partners-site" import { DoorwayProps } from "./doorway-props" import { DoorwayPublicLoadBalancer } from "./doorway-public-load-balancer" -import { DoorwayPublicSite } from "./doorway-public-site" +import { DoorwayBackendService } from "./services/doorway-backend-service" +import { DoorwayPartnersSite } from "./services/doorway-partners-site" +import { DoorwayPublicSite } from "./services/doorway-public-site" export class DoorwayAppEnvironmentStack extends Stack { constructor(scope: Construct, id: string) { diff --git a/cdk/lib/doorway_environment/doorway-lambda-props.ts b/cdk/lib/doorway_environment/doorway-lambda-props.ts deleted file mode 100644 index 96754945ed5..00000000000 --- a/cdk/lib/doorway_environment/doorway-lambda-props.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { FargateService, Secret } from "aws-cdk-lib/aws-ecs"; - -export interface DoorwayLambdaProps { - environment: string; - name: string; - timeoutInMinutes: number; - handler: string; - memory?: number; - storage?: number; - bundling: {}; - - environmentVariables?: { [key: string]: string }; - -} -export interface DoorwayServiceMonitorLambdaProps { - secrets: { [key: string]: Secret }; - service: FargateService; - environment: string - -} \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-props.ts b/cdk/lib/doorway_environment/doorway-props.ts index 422babc070a..443350286f1 100644 --- a/cdk/lib/doorway_environment/doorway-props.ts +++ b/cdk/lib/doorway_environment/doorway-props.ts @@ -38,3 +38,23 @@ export interface DoorwayLoadBalancerProps extends DoorwayProps { publicService: FargateService, partnersService: FargateService, } + + +export interface DoorwayLambdaProps { + environment: string; + name: string; + timeoutInMinutes: number; + handler: string; + memory?: number; + storage?: number; + bundling: {}; + + environmentVariables?: { [key: string]: string }; + +} +export interface DoorwayServiceMonitorLambdaProps { + secrets: { [key: string]: Secret }; + service: FargateService; + environment: string + +} \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index ff616e6a6f4..0d9fb111c51 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -1,10 +1,10 @@ +import { StringParameter } from "aws import { aws_ec2, Duration, Fn } from "aws-cdk-lib"; import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2"; import { ApplicationListener, ApplicationListenerRule, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup, ListenerAction, ListenerCondition, TargetType } from "aws-cdk-lib/aws-elasticloadbalancingv2"; import { ARecord, HostedZone, RecordTarget } from "aws-cdk-lib/aws-route53"; import { LoadBalancerTarget } from "aws-cdk-lib/aws-route53-targets"; -import { StringParameter } from "aws-cdk-lib/aws-ssm"; import { Construct } from "constructs"; import { DoorwayProps } from "./doorway-props"; diff --git a/cdk/lib/doorway_environment/doorway-backend-service.ts b/cdk/lib/doorway_environment/services/doorway-backend-service.ts similarity index 99% rename from cdk/lib/doorway_environment/doorway-backend-service.ts rename to cdk/lib/doorway_environment/services/doorway-backend-service.ts index ac6a778fecd..6e87b0e43ee 100644 --- a/cdk/lib/doorway_environment/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/services/doorway-backend-service.ts @@ -7,8 +7,8 @@ import * as secret from "aws-cdk-lib/aws-secretsmanager" import { EmailIdentity } from "aws-cdk-lib/aws-ses" import { Construct } from "constructs" +import { DoorwayProps } from "../doorway-props" import { DoorwayService } from "./doorway_service" -import { DoorwayProps } from "./doorway-props" export class DoorwayBackendService { public service: FargateService diff --git a/cdk/lib/doorway_environment/doorway-partners-site.ts b/cdk/lib/doorway_environment/services/doorway-partners-site.ts similarity index 98% rename from cdk/lib/doorway_environment/doorway-partners-site.ts rename to cdk/lib/doorway_environment/services/doorway-partners-site.ts index 26ed05ffb73..70f4d2c31df 100644 --- a/cdk/lib/doorway_environment/doorway-partners-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-partners-site.ts @@ -5,8 +5,7 @@ import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" - -import { DoorwayProps } from "./doorway-props" +import { DoorwayProps } from "../doorway-props" import { DoorwayService } from "./doorway_service" export class DoorwayPartnersSite { diff --git a/cdk/lib/doorway_environment/doorway-public-site.ts b/cdk/lib/doorway_environment/services/doorway-public-site.ts similarity index 98% rename from cdk/lib/doorway_environment/doorway-public-site.ts rename to cdk/lib/doorway_environment/services/doorway-public-site.ts index 68cca12d27c..4cbdb10d74c 100644 --- a/cdk/lib/doorway_environment/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-public-site.ts @@ -6,7 +6,7 @@ import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" -import { DoorwayProps } from "./doorway-props" +import { DoorwayProps } from "../doorway-props" import { DoorwayService } from "./doorway_service" export class DoorwayPublicSite { diff --git a/cdk/lib/doorway_environment/doorway_service.ts b/cdk/lib/doorway_environment/services/doorway_service.ts similarity index 97% rename from cdk/lib/doorway_environment/doorway_service.ts rename to cdk/lib/doorway_environment/services/doorway_service.ts index fedfe3f105a..1474785f622 100644 --- a/cdk/lib/doorway_environment/doorway_service.ts +++ b/cdk/lib/doorway_environment/services/doorway_service.ts @@ -8,8 +8,8 @@ import { PrivateDnsNamespace } from "aws-cdk-lib/aws-servicediscovery" import { StringParameter } from "aws-cdk-lib/aws-ssm" import { Construct } from "constructs" -import { DoorwayServiceProps } from "./doorway-props" -import { RestartServicesLambda } from "./restart-services-lambda" +import { DoorwayServiceProps } from "../doorway-props" +import { RestartServicesLambda } from "../utility_lambdas/restart-services-lambda" export class DoorwayService { public service: FargateService diff --git a/cdk/lib/doorway_environment/lambda-base-class.ts b/cdk/lib/doorway_environment/utility_lambdas/lambda-base-class.ts similarity index 97% rename from cdk/lib/doorway_environment/lambda-base-class.ts rename to cdk/lib/doorway_environment/utility_lambdas/lambda-base-class.ts index 6a34f291d04..e18a24cf00c 100644 --- a/cdk/lib/doorway_environment/lambda-base-class.ts +++ b/cdk/lib/doorway_environment/utility_lambdas/lambda-base-class.ts @@ -5,7 +5,7 @@ import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; import { Construct } from 'constructs'; -import { DoorwayLambdaProps } from './doorway-lambda-props'; +import { DoorwayLambdaProps } from '../doorway-props'; export class DoorwayLambdaBaseClass extends Construct { lambdaFunction: Function; diff --git a/cdk/lib/doorway_environment/restart-services-lambda.ts b/cdk/lib/doorway_environment/utility_lambdas/restart-services-lambda.ts similarity index 97% rename from cdk/lib/doorway_environment/restart-services-lambda.ts rename to cdk/lib/doorway_environment/utility_lambdas/restart-services-lambda.ts index 9a531b3fef8..81613e7f697 100644 --- a/cdk/lib/doorway_environment/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/utility_lambdas/restart-services-lambda.ts @@ -3,7 +3,7 @@ import { LambdaFunction } from "aws-cdk-lib/aws-events-targets"; import { Effect, PolicyStatement, ServicePrincipal } from "aws-cdk-lib/aws-iam"; import { Construct } from "constructs"; -import { DoorwayServiceMonitorLambdaProps } from "./doorway-lambda-props"; +import { DoorwayServiceMonitorLambdaProps } from "../doorway-props"; import { DoorwayLambdaBaseClass } from "./lambda-base-class"; export interface ServiceRestartDetail { diff --git a/cdk/lib/doorway_environment/restart-services.handler.ts b/cdk/lib/doorway_environment/utility_lambdas/restart-services.handler.ts similarity index 100% rename from cdk/lib/doorway_environment/restart-services.handler.ts rename to cdk/lib/doorway_environment/utility_lambdas/restart-services.handler.ts diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 5cc9f3d9998..8e0305a8bb4 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -49,6 +49,7 @@ phases: --env PGPASSWORD="${PGPASSWORD}" --env PGHOST="${PGHOST}" --env PGDATABASE=${PGDATABASE} + --env PGPORT=${PGPORT} --env DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" --env CLOUDINARY_CLOUD_NAME="${CLOUDINARY_CLOUD_NAME}" --env LISTINGS_QUERY="${LISTINGS_QUERY}" From c73ce2ce6ff724f65811388f3bf75068847320af Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 12:09:45 -0700 Subject: [PATCH 122/217] fix: fixing cruft from moving things around --- cdk/lib/doorway_environment/doorway-public-load-balancer.ts | 2 +- .../utility_lambdas/restart-services-lambda.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index 0d9fb111c51..ff616e6a6f4 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -1,10 +1,10 @@ -import { StringParameter } from "aws import { aws_ec2, Duration, Fn } from "aws-cdk-lib"; import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2"; import { ApplicationListener, ApplicationListenerRule, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup, ListenerAction, ListenerCondition, TargetType } from "aws-cdk-lib/aws-elasticloadbalancingv2"; import { ARecord, HostedZone, RecordTarget } from "aws-cdk-lib/aws-route53"; import { LoadBalancerTarget } from "aws-cdk-lib/aws-route53-targets"; +import { StringParameter } from "aws-cdk-lib/aws-ssm"; import { Construct } from "constructs"; import { DoorwayProps } from "./doorway-props"; diff --git a/cdk/lib/doorway_environment/utility_lambdas/restart-services-lambda.ts b/cdk/lib/doorway_environment/utility_lambdas/restart-services-lambda.ts index 81613e7f697..607b4337a32 100644 --- a/cdk/lib/doorway_environment/utility_lambdas/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/utility_lambdas/restart-services-lambda.ts @@ -40,7 +40,7 @@ export class RestartServicesLambda { timeoutInMinutes: 15, memory: 1024, storage: 512, - handler: "lib/doorway_environment/restart-services.handler.ts", + handler: "lib/doorway_environment/utility_lambdas/restart-services.handler.ts", bundling: {} }) dwLambda.lambdaRole.addToPolicy(new PolicyStatement({ From 14937ac220e5a876092e42e155859edf92748528 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 12:24:22 -0700 Subject: [PATCH 123/217] fix: fixing migrate dockerfile --- build/docker/Dockerfile.backend | 9 +++++---- build/docker/entrypoint-migrate.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index 6b7d350f999..b7fca6ce450 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -164,11 +164,12 @@ COPY --from=source --chown=node:node /app/api/src ./src COPY --from=source --chown=node:node /app/api/prisma ./prisma COPY --from=source --chown=node:node /app/api/scripts ./scripts + +COPY build/docker/entrypoint-migrate.sh /app/api/entrypoint-migrate.sh +RUN chown node:node /app/api/entrypoint-migrate.sh +RUN chmod 755 /app/api/entrypoint-migrate.sh ## This CMD should be run as the node user USER node -# We need to skip all migrations that are just for local development (changes already exist in deployed environment) -CMD if [ "$SKIP_MIGRATIONS" = TRUE ]; then yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ - else yarn db:migration:run; \ - fi +ENTRYPOINT [ "/app/api/entrypoint-migrate.sh" ] diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh index 0626caa66cd..d389345d51c 100644 --- a/build/docker/entrypoint-migrate.sh +++ b/build/docker/entrypoint-migrate.sh @@ -1,5 +1,31 @@ #!/bin/sh export DATABASE_URL="postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" + + + + +#!/bin/sh + +# Debug the individual secret values +echo "PGUSER: $PGUSER" +echo "PGHOST: $PGHOST" +echo "PGPORT: $PGPORT" +echo "PGDATABASE: $PGDATABASE" +echo "PGPASSWORD length: ${#PGPASSWORD}" + +# Validate port is numeric +if ! echo "$PGPORT" | grep -qE '^[0-9]+$'; then + echo "ERROR: PGPORT is not a valid number: '$PGPORT'" + exit 1 +fi + +# URL encode the password to handle special characters +ENCODED_PASSWORD=$(node -e "console.log(encodeURIComponent('$PGPASSWORD'))") + +export DATABASE_URL="postgresql://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" +echo "DATABASE_URL (password masked): postgresql://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" + +echo "Running migrations..." if [ "$SKIP_MIGRATIONS" = TRUE ]; then yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ else yarn ${MIGRATION_CMD}; \ fi \ No newline at end of file From fd5ac0324b07d89a7c614e281b49cd9506ec583f Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 13:13:18 -0700 Subject: [PATCH 124/217] fix: more debugging --- build/docker/entrypoint-migrate.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh index d389345d51c..8a8247efb4c 100644 --- a/build/docker/entrypoint-migrate.sh +++ b/build/docker/entrypoint-migrate.sh @@ -25,7 +25,7 @@ ENCODED_PASSWORD=$(node -e "console.log(encodeURIComponent('$PGPASSWORD'))") export DATABASE_URL="postgresql://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" echo "DATABASE_URL (password masked): postgresql://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" -echo "Running migrations..." -if [ "$SKIP_MIGRATIONS" = TRUE ]; then yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ - else yarn ${MIGRATION_CMD}; \ + +if [ "$SKIP_MIGRATIONS" = TRUE ]; then echo "Skipping Migrations"&&yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ + else echo "Running migrations..."&&yarn ${MIGRATION_CMD}; \ fi \ No newline at end of file From feda3a2bdfa0a28797c407abbaf4ee8a4f4d5c16 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 13:41:12 -0700 Subject: [PATCH 125/217] fix: tweeking CORS settings --- api/src/services/auth.service.ts | 10 +++++----- cdk/dev2.env | 2 ++ .../services/doorway-backend-service.ts | 6 ++++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/api/src/services/auth.service.ts b/api/src/services/auth.service.ts index fda7344e9d7..64e5a91182e 100644 --- a/api/src/services/auth.service.ts +++ b/api/src/services/auth.service.ts @@ -31,7 +31,7 @@ let sameSite: boolean | 'strict' | 'lax' | 'none' = 'none'; if (process.env.NODE_ENV === 'development') { sameSite = 'strict'; } else if (process.env.SAME_SITE === 'true') { - sameSite = 'lax'; + sameSite = 'none'; } const TOKEN_COOKIE_MAXAGE = 86400000; // 24 hours @@ -65,7 +65,7 @@ export class AuthService { private userService: UserService, private smsService: SmsService, private emailsService: EmailService, - ) {} + ) { } /* generates a signed token for a user willBeRefreshToken changes the TTL of the token with true being longer and false being shorter @@ -300,9 +300,9 @@ export class AuthService { return dto.mfaType === MfaType.email ? { email: user.email, phoneNumberVerified: user.phoneNumberVerified } : { - phoneNumber: user.phoneNumber, - phoneNumberVerified: user.phoneNumberVerified, - }; + phoneNumber: user.phoneNumber, + phoneNumberVerified: user.phoneNumberVerified, + }; } /* updates a user's password and logs them in diff --git a/cdk/dev2.env b/cdk/dev2.env index 0c19299dd71..e684e686653 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -19,6 +19,8 @@ APP_SECRET="" PUBLIC_PORTAL_PORT=3000 PUBLIC_PORTAL_DOMAIN=dev2.housingbayarea.mtc.ca.gov PUBLIC_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY +DISABLE_CORS=TRUE +SAME_SITE=true PARTNERS_PORTAL_PORT=3200 diff --git a/cdk/lib/doorway_environment/services/doorway-backend-service.ts b/cdk/lib/doorway_environment/services/doorway-backend-service.ts index 6e87b0e43ee..07f66102abd 100644 --- a/cdk/lib/doorway_environment/services/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/services/doorway-backend-service.ts @@ -69,10 +69,12 @@ export class DoorwayBackendService { CORS_ORIGINS: process.env.CORS_ORIGINS || "*", DUPLICATES_CLOSE_DATE: process.env.DUPLICATES_CLOSE_DATE || "2024-10-08 00:00 -08:00", + // Doorway has started using serviceconnect which uses TLS internally, so we can turn HTTPS_OFF on for all environments. HTTPS_OFF: process.env.HTTPS_OFF || "TRUE", - SAME_SITE: process.env.SAME_SITE || "false", + // We can also turn off same site cookies and CORS since we're using serviceconnect because the incoming traffic looks like it's coming from localhost + SAME_SITE: process.env.SAME_SITE || "true", + DISABLE_CORS: process.env.DISABLE_CORS || "TRUE", APP_SECRET: process.env.APP_SECRET || "", - DISABLE_CORS: process.env.DISABLE_CORS || "FALSE", } const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) const dbSecret = secret.Secret.fromSecretCompleteArn(scope, "dbSecret", dbSecretArn) From 78202b5e091973555cc3e7d0087ca8cd4e9dea21 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 13:43:24 -0700 Subject: [PATCH 126/217] fix: samesite lax --- api/src/services/auth.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/services/auth.service.ts b/api/src/services/auth.service.ts index 64e5a91182e..64214fcae50 100644 --- a/api/src/services/auth.service.ts +++ b/api/src/services/auth.service.ts @@ -31,7 +31,7 @@ let sameSite: boolean | 'strict' | 'lax' | 'none' = 'none'; if (process.env.NODE_ENV === 'development') { sameSite = 'strict'; } else if (process.env.SAME_SITE === 'true') { - sameSite = 'none'; + sameSite = 'lax'; } const TOKEN_COOKIE_MAXAGE = 86400000; // 24 hours From aaf52e30195cb7680536be3423575c54da6a42f2 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 13:50:54 -0700 Subject: [PATCH 127/217] fix: more more debugging --- build/docker/entrypoint-migrate.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh index 8a8247efb4c..406cc9430c1 100644 --- a/build/docker/entrypoint-migrate.sh +++ b/build/docker/entrypoint-migrate.sh @@ -26,6 +26,10 @@ export DATABASE_URL="postgresql://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPO echo "DATABASE_URL (password masked): postgresql://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" -if [ "$SKIP_MIGRATIONS" = TRUE ]; then echo "Skipping Migrations"&&yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; \ - else echo "Running migrations..."&&yarn ${MIGRATION_CMD}; \ - fi \ No newline at end of file +if [ "$SKIP_MIGRATIONS" = TRUE ];then + echo "Skipping Migrations" + yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; +else + echo "Running migrations...${MIGRATION_CMD}"; + yarn ${MIGRATION_CMD}; +fi \ No newline at end of file From e1903e24400c128c244851d0bc033d79565e9751 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 14:21:48 -0700 Subject: [PATCH 128/217] fix: changing cookiedomain to localhost --- api/src/services/auth.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/services/auth.service.ts b/api/src/services/auth.service.ts index 64214fcae50..b15e7bf97dd 100644 --- a/api/src/services/auth.service.ts +++ b/api/src/services/auth.service.ts @@ -43,7 +43,7 @@ export const AUTH_COOKIE_OPTIONS: CookieOptions = { secure, sameSite, maxAge: TOKEN_COOKIE_MAXAGE / 8, // access token should last 3 hr - domain: process.env.COOKIE_DOMAIN ?? undefined, + domain: process.env.COOKIE_DOMAIN ?? "localhost", }; export const REFRESH_COOKIE_OPTIONS: CookieOptions = { ...AUTH_COOKIE_OPTIONS, From 6582c9a764c7b3908b644686c58059b011769909 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 14:28:01 -0700 Subject: [PATCH 129/217] fix: bad env variable --- cdk/dev2.env | 1 + cdk/lib/doorway_environment/services/doorway-backend-service.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cdk/dev2.env b/cdk/dev2.env index e684e686653..e075838ba57 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -21,6 +21,7 @@ PUBLIC_PORTAL_DOMAIN=dev2.housingbayarea.mtc.ca.gov PUBLIC_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY DISABLE_CORS=TRUE SAME_SITE=true +HTTPS_OFF=true PARTNERS_PORTAL_PORT=3200 diff --git a/cdk/lib/doorway_environment/services/doorway-backend-service.ts b/cdk/lib/doorway_environment/services/doorway-backend-service.ts index 07f66102abd..3b80a34fcd7 100644 --- a/cdk/lib/doorway_environment/services/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/services/doorway-backend-service.ts @@ -70,7 +70,7 @@ export class DoorwayBackendService { process.env.CORS_ORIGINS || "*", DUPLICATES_CLOSE_DATE: process.env.DUPLICATES_CLOSE_DATE || "2024-10-08 00:00 -08:00", // Doorway has started using serviceconnect which uses TLS internally, so we can turn HTTPS_OFF on for all environments. - HTTPS_OFF: process.env.HTTPS_OFF || "TRUE", + HTTPS_OFF: process.env.HTTPS_OFF || "true", // We can also turn off same site cookies and CORS since we're using serviceconnect because the incoming traffic looks like it's coming from localhost SAME_SITE: process.env.SAME_SITE || "true", DISABLE_CORS: process.env.DISABLE_CORS || "TRUE", From 6aefb6dc99b7e667c7b598ca997f68ba6c241590 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 15:12:31 -0700 Subject: [PATCH 130/217] changing -c to entrypoint --- ci/buildspec/migrate_stop_backend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 8e0305a8bb4..f70ae269851 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -63,4 +63,4 @@ phases: --env PARTNERS_BASE_URL="${PARTNERS_BASE_URL:-http://localhost:3001/not-used}" --env PARTNERS_PORTAL_URL="${PARTNERS_PORTAL_URL:-http://localhost:3001/not-used}" --env SKIP_MIGRATIONS=FALSE - "${MIGRATION_IMAGE}" sh -c "yarn ${MIGRATION_CMD}" + "${MIGRATION_IMAGE}" sh --entrypoint "yarn ${MIGRATION_CMD}" From 959fee7f36f975be1ac59332dc9d188e8e4e7e3f Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 15:52:34 -0700 Subject: [PATCH 131/217] fix: more debugging --- api/src/controllers/auth.controller.ts | 3 +++ api/src/main.ts | 4 +++- api/src/services/auth.service.ts | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/api/src/controllers/auth.controller.ts b/api/src/controllers/auth.controller.ts index c8429af9b6e..8ad28fb610a 100644 --- a/api/src/controllers/auth.controller.ts +++ b/api/src/controllers/auth.controller.ts @@ -42,6 +42,9 @@ export class AuthController { this.logger.log(`Login attempt for user: ${dto.email}`); this.logger.log(`Origin: ${req.headers.origin}`); this.logger.log(`Headers: ${JSON.stringify(req.headers)}`) + this.logger.log(`Cookies: ${JSON.stringify(req.cookies)}`) + this.logger.log(`Body: ${JSON.stringify(req.body)}`) + return await this.authService.setCredentials( res, diff --git a/api/src/main.ts b/api/src/main.ts index 5ce852a6ea7..9eb5f3bd5d8 100644 --- a/api/src/main.ts +++ b/api/src/main.ts @@ -65,7 +65,9 @@ async function bootstrap() { inUselogger.debug(`URL: ${req.url}`); inUselogger.debug(`Origin: ${req.headers.origin}`); inUselogger.debug(`Host: ${req.headers.host}`); - inUselogger.debug(`All Headers: ${JSON.stringify(req.headers, null, 2)}`); + inUselogger.debug(`All Headers: ${JSON.stringify(req.headers, null, 2)}`) + inUselogger.debug(`Cookies: ${JSON.stringify(req.cookies, null, 2)}`) + inUselogger.debug(`Body: ${JSON.stringify(req.body, null, 2)}`); inUselogger.debug('========================'); next(); }); diff --git a/api/src/services/auth.service.ts b/api/src/services/auth.service.ts index b15e7bf97dd..9050f3e58f5 100644 --- a/api/src/services/auth.service.ts +++ b/api/src/services/auth.service.ts @@ -43,7 +43,7 @@ export const AUTH_COOKIE_OPTIONS: CookieOptions = { secure, sameSite, maxAge: TOKEN_COOKIE_MAXAGE / 8, // access token should last 3 hr - domain: process.env.COOKIE_DOMAIN ?? "localhost", + domain: process.env.COOKIE_DOMAIN ?? "", }; export const REFRESH_COOKIE_OPTIONS: CookieOptions = { ...AUTH_COOKIE_OPTIONS, From 33ee0129d40d3ac743ad6480aa4dd0676cfd5ae0 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 16:04:05 -0700 Subject: [PATCH 132/217] fix: changing entrypoint --- ci/buildspec/migrate_stop_backend.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index f70ae269851..3961fd4c916 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -63,4 +63,5 @@ phases: --env PARTNERS_BASE_URL="${PARTNERS_BASE_URL:-http://localhost:3001/not-used}" --env PARTNERS_PORTAL_URL="${PARTNERS_PORTAL_URL:-http://localhost:3001/not-used}" --env SKIP_MIGRATIONS=FALSE - "${MIGRATION_IMAGE}" sh --entrypoint "yarn ${MIGRATION_CMD}" + --entrypoint sh + "${MIGRATION_IMAGE}" -c "yarn ${MIGRATION_CMD}" From e643be88952fcfb85b7fd348072bf650dbf54ac8 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 16:51:32 -0700 Subject: [PATCH 133/217] fix: trying without the command line parm --- ci/buildspec/migrate_stop_backend.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 3961fd4c916..202ca02fcb7 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -63,5 +63,4 @@ phases: --env PARTNERS_BASE_URL="${PARTNERS_BASE_URL:-http://localhost:3001/not-used}" --env PARTNERS_PORTAL_URL="${PARTNERS_PORTAL_URL:-http://localhost:3001/not-used}" --env SKIP_MIGRATIONS=FALSE - --entrypoint sh - "${MIGRATION_IMAGE}" -c "yarn ${MIGRATION_CMD}" + "${MIGRATION_IMAGE} From 5dff3297bfd40324697de53b900e13180e1a4941 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 17:04:21 -0700 Subject: [PATCH 134/217] fix: fixing entrypoint --- build/docker/entrypoint-migrate.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh index 406cc9430c1..4ece10bc960 100644 --- a/build/docker/entrypoint-migrate.sh +++ b/build/docker/entrypoint-migrate.sh @@ -1,9 +1,3 @@ -#!/bin/sh -export DATABASE_URL="postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" - - - - #!/bin/sh # Debug the individual secret values @@ -24,8 +18,6 @@ ENCODED_PASSWORD=$(node -e "console.log(encodeURIComponent('$PGPASSWORD'))") export DATABASE_URL="postgresql://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" echo "DATABASE_URL (password masked): postgresql://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" - - if [ "$SKIP_MIGRATIONS" = TRUE ];then echo "Skipping Migrations" yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; From b1b1901221ecf5b789ed66e974929ccaaa7bdf26 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 18:48:38 -0700 Subject: [PATCH 135/217] fix: trying another edit of the script --- build/docker/entrypoint-migrate.sh | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh index 4ece10bc960..f44b7fc864f 100644 --- a/build/docker/entrypoint-migrate.sh +++ b/build/docker/entrypoint-migrate.sh @@ -1,18 +1,4 @@ #!/bin/sh - -# Debug the individual secret values -echo "PGUSER: $PGUSER" -echo "PGHOST: $PGHOST" -echo "PGPORT: $PGPORT" -echo "PGDATABASE: $PGDATABASE" -echo "PGPASSWORD length: ${#PGPASSWORD}" - -# Validate port is numeric -if ! echo "$PGPORT" | grep -qE '^[0-9]+$'; then - echo "ERROR: PGPORT is not a valid number: '$PGPORT'" - exit 1 -fi - # URL encode the password to handle special characters ENCODED_PASSWORD=$(node -e "console.log(encodeURIComponent('$PGPASSWORD'))") From 7afff4dde1b7a9cae72dd8c76425ec044f408eef Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 19:03:55 -0700 Subject: [PATCH 136/217] fix: more futzing with the entrypoint --- build/docker/entrypoint-migrate.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh index f44b7fc864f..84883d3ba9e 100644 --- a/build/docker/entrypoint-migrate.sh +++ b/build/docker/entrypoint-migrate.sh @@ -3,7 +3,6 @@ ENCODED_PASSWORD=$(node -e "console.log(encodeURIComponent('$PGPASSWORD'))") export DATABASE_URL="postgresql://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" -echo "DATABASE_URL (password masked): postgresql://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" if [ "$SKIP_MIGRATIONS" = TRUE ];then echo "Skipping Migrations" yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; From 3ab60b8aa7b08c8d88341e67733063b92d9cda97 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 19:17:25 -0700 Subject: [PATCH 137/217] fix: adding debug logging --- api/src/main.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/main.ts b/api/src/main.ts index 9eb5f3bd5d8..e0b8f0c0c33 100644 --- a/api/src/main.ts +++ b/api/src/main.ts @@ -23,7 +23,7 @@ async function bootstrap() { // In local development use the built in logger for better readability logger: process.env.NODE_ENV === 'development' - ? ['log'] + ? ['log', 'debug', 'error', 'warn'] : WinstonModule.createLogger({ instance: instance, }), @@ -65,8 +65,8 @@ async function bootstrap() { inUselogger.debug(`URL: ${req.url}`); inUselogger.debug(`Origin: ${req.headers.origin}`); inUselogger.debug(`Host: ${req.headers.host}`); - inUselogger.debug(`All Headers: ${JSON.stringify(req.headers, null, 2)}`) - inUselogger.debug(`Cookies: ${JSON.stringify(req.cookies, null, 2)}`) + inUselogger.debug(`All Headers: ${JSON.stringify(req.headers, null, 2)}`); + inUselogger.debug(`Cookies: ${JSON.stringify(req.cookies, null, 2)}`); inUselogger.debug(`Body: ${JSON.stringify(req.body, null, 2)}`); inUselogger.debug('========================'); next(); From f4b4f16694e9c46e455cbd197943d6d81d1e24c0 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 19:52:48 -0700 Subject: [PATCH 138/217] fix: more debugging! --- ci/buildspec/build_backend.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/buildspec/build_backend.yml b/ci/buildspec/build_backend.yml index f35060a3e2e..ef680c610e5 100644 --- a/ci/buildspec/build_backend.yml +++ b/ci/buildspec/build_backend.yml @@ -20,6 +20,8 @@ phases: commands: - export ECR_HOST="${ECR_ACCOUNT_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com" - export ECR_REPO="${ECR_HOST}/${ECR_NAMESPACE}" + - echo "ECR_REPO is ${ECR_REPO}" + - cat build/docker/entrypoint-migrate.sh # Skip the test step as it is handle before merge. But re-add if you want more verification # - docker build . -f build/docker/Dockerfile.backend --target test -t api:test From 438ce6db9d28c7d2c46f4dc3026b1a4a6371e8fe Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 19:58:18 -0700 Subject: [PATCH 139/217] fix: running a test --- ci/buildspec/build_backend.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/buildspec/build_backend.yml b/ci/buildspec/build_backend.yml index ef680c610e5..e86d877bbb6 100644 --- a/ci/buildspec/build_backend.yml +++ b/ci/buildspec/build_backend.yml @@ -21,7 +21,8 @@ phases: - export ECR_HOST="${ECR_ACCOUNT_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com" - export ECR_REPO="${ECR_HOST}/${ECR_NAMESPACE}" - echo "ECR_REPO is ${ECR_REPO}" - - cat build/docker/entrypoint-migrate.sh + - ls -la build/docker/ + - test -f build/docker/entrypoint-migrate.sh && cat build/docker/entrypoint-migrate.sh || echo "File not found: build/docker/entrypoint-migrate.sh" # Skip the test step as it is handle before merge. But re-add if you want more verification # - docker build . -f build/docker/Dockerfile.backend --target test -t api:test From 61d05e4724965fb9582cd6cdc9a600fe4f1c12ea Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 2 Sep 2025 20:01:46 -0700 Subject: [PATCH 140/217] fix: another test --- ci/buildspec/build_backend.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/buildspec/build_backend.yml b/ci/buildspec/build_backend.yml index e86d877bbb6..e3dff37bef2 100644 --- a/ci/buildspec/build_backend.yml +++ b/ci/buildspec/build_backend.yml @@ -18,6 +18,7 @@ phases: build: commands: + - echo "BUILD PHASE STARTING - DEBUG TEST" - export ECR_HOST="${ECR_ACCOUNT_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com" - export ECR_REPO="${ECR_HOST}/${ECR_NAMESPACE}" - echo "ECR_REPO is ${ECR_REPO}" From 8692717b102ef2aeb7672ddcc4a1a240f628e44d Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 09:53:50 -0700 Subject: [PATCH 141/217] fix: caching pipeline --- cdk/lib/build-pipeline/doorway-build-job-base.ts | 3 +++ cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 1 + cdk/lib/build-pipeline/doorway-database-migrate.ts | 3 ++- cdk/lib/build-pipeline/doorway-docker-build.ts | 3 ++- cdk/lib/build-pipeline/doorway-ecs-deploy.ts | 3 ++- ci/buildspec/build_backend.yml | 7 +++++-- 6 files changed, 15 insertions(+), 5 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-job-base.ts b/cdk/lib/build-pipeline/doorway-build-job-base.ts index 59aed0ba886..d251df5e28a 100644 --- a/cdk/lib/build-pipeline/doorway-build-job-base.ts +++ b/cdk/lib/build-pipeline/doorway-build-job-base.ts @@ -1,6 +1,7 @@ import { Aws, Fn, Stack } from "aws-cdk-lib" import { BuildSpec, + Cache, ComputeType, LinuxBuildImage, PipelineProject, @@ -80,6 +81,7 @@ export class DoorwayBuildJobBase { ENVIRONMENT: { value: props.environment }, }, role: buildRole, + cache: Cache.none(), } if (props.buildInVpc) { const vpcId = Fn.importValue(`doorway-vpc-id-${props.environment}`) @@ -107,6 +109,7 @@ export class DoorwayBuildJobBase { stack, `doorway-dbMigrate-${props.environment}`, projectProps, + ) // Create the CodeBuild action this.action = new CodeBuildAction({ diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 082c1ec8d21..25fc3e91a13 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -103,6 +103,7 @@ export class DoorwayBuildPipelineStack extends Stack { branch: "feat/tf_to_cdk", output: sourceArtifact, trigger: GitHubTrigger.WEBHOOK, + variablesNamespace: "SourceVariables", }) pipeline.addStage({ stageName: "Source", diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts index 1d6d4259210..ac8695f6507 100644 --- a/cdk/lib/build-pipeline/doorway-database-migrate.ts +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -1,5 +1,5 @@ import { Aws, Fn } from "aws-cdk-lib" -import { BuildSpec, ComputeType, LinuxBuildImage, PipelineProject } from "aws-cdk-lib/aws-codebuild" +import { BuildSpec, Cache, ComputeType, LinuxBuildImage, PipelineProject } from "aws-cdk-lib/aws-codebuild" import { Artifact } from "aws-cdk-lib/aws-codepipeline" import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" @@ -71,6 +71,7 @@ export class DoorwayDatabaseMigrate { }, }, role: props.buildRole, + cache: Cache.none(), }) // Create the CodeBuild action this.action = new CodeBuildAction({ diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts index 49af97f938a..002a0862091 100644 --- a/cdk/lib/build-pipeline/doorway-docker-build.ts +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -1,5 +1,5 @@ import { Aws } from "aws-cdk-lib" -import { BuildSpec, ComputeType, PipelineProject } from "aws-cdk-lib/aws-codebuild" +import { BuildSpec, Cache, ComputeType, PipelineProject } from "aws-cdk-lib/aws-codebuild" import { Artifact } from "aws-cdk-lib/aws-codepipeline" import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" import { Repository } from "aws-cdk-lib/aws-ecr" @@ -44,6 +44,7 @@ export class DoorwayDockerBuild { ENVIRONMENT: { value: props.environment }, }, role: props.buildRole, + cache: Cache.none(), }) // Create the CodeBuild action diff --git a/cdk/lib/build-pipeline/doorway-ecs-deploy.ts b/cdk/lib/build-pipeline/doorway-ecs-deploy.ts index 3fd630a04cd..1ceeaff407e 100644 --- a/cdk/lib/build-pipeline/doorway-ecs-deploy.ts +++ b/cdk/lib/build-pipeline/doorway-ecs-deploy.ts @@ -1,4 +1,4 @@ -import { BuildSpec, ComputeType, LinuxBuildImage, PipelineProject } from "aws-cdk-lib/aws-codebuild" +import { BuildSpec, Cache, ComputeType, LinuxBuildImage, PipelineProject } from "aws-cdk-lib/aws-codebuild" import { Artifact } from "aws-cdk-lib/aws-codepipeline" import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" import { Role } from "aws-cdk-lib/aws-iam" @@ -25,6 +25,7 @@ export class DoorwayECSDeploy { }, role: props.buildRole, + cache: Cache.none(), }) // Create the CodeBuild action this.action = new CodeBuildAction({ diff --git a/ci/buildspec/build_backend.yml b/ci/buildspec/build_backend.yml index e3dff37bef2..0f74aa6190f 100644 --- a/ci/buildspec/build_backend.yml +++ b/ci/buildspec/build_backend.yml @@ -19,6 +19,9 @@ phases: build: commands: - echo "BUILD PHASE STARTING - DEBUG TEST" + - echo "Cleaning workspace..." + - git clean -fdx + - git reset --hard HEAD - export ECR_HOST="${ECR_ACCOUNT_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com" - export ECR_REPO="${ECR_HOST}/${ECR_NAMESPACE}" - echo "ECR_REPO is ${ECR_REPO}" @@ -28,8 +31,8 @@ phases: # Skip the test step as it is handle before merge. But re-add if you want more verification # - docker build . -f build/docker/Dockerfile.backend --target test -t api:test # - docker run api:test yarn test - - docker build . -f build/docker/Dockerfile.backend --build-arg ECR_REGION=${ECR_REGION} --build-arg ECR_ACCOUNT_ID=${ECR_ACCOUNT_ID} --target run -t api:run-candidate - - docker build . -f build/docker/Dockerfile.backend --build-arg ECR_REGION=${ECR_REGION} --build-arg ECR_ACCOUNT_ID=${ECR_ACCOUNT_ID} --target migrate -t api:migrate-candidate + - docker build --no-cache . -f build/docker/Dockerfile.backend --build-arg ECR_REGION=${ECR_REGION} --build-arg ECR_ACCOUNT_ID=${ECR_ACCOUNT_ID} --build-arg CACHE_BUST=$(date +%s) --target run -t api:run-candidate + - docker build --no-cache . -f build/docker/Dockerfile.backend --build-arg ECR_REGION=${ECR_REGION} --build-arg ECR_ACCOUNT_ID=${ECR_ACCOUNT_ID} --build-arg CACHE_BUST=$(date +%s) --target migrate -t api:migrate-candidate # Add hash and candidate tags to migration image - docker tag api:migrate-candidate "${ECR_REPO}/backend:migrate-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" From 9a96489899492435d39efc94297aaaa6a4d81f6a Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 10:00:36 -0700 Subject: [PATCH 142/217] fix: removing line --- ci/buildspec/build_backend.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/ci/buildspec/build_backend.yml b/ci/buildspec/build_backend.yml index 0f74aa6190f..5129d5fce24 100644 --- a/ci/buildspec/build_backend.yml +++ b/ci/buildspec/build_backend.yml @@ -26,7 +26,6 @@ phases: - export ECR_REPO="${ECR_HOST}/${ECR_NAMESPACE}" - echo "ECR_REPO is ${ECR_REPO}" - ls -la build/docker/ - - test -f build/docker/entrypoint-migrate.sh && cat build/docker/entrypoint-migrate.sh || echo "File not found: build/docker/entrypoint-migrate.sh" # Skip the test step as it is handle before merge. But re-add if you want more verification # - docker build . -f build/docker/Dockerfile.backend --target test -t api:test From f0abcb62e6c86d3d90fcff7330ec02d686c47d11 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 10:06:44 -0700 Subject: [PATCH 143/217] fix: removing more code --- ci/buildspec/build_backend.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/ci/buildspec/build_backend.yml b/ci/buildspec/build_backend.yml index 5129d5fce24..bc67445dcd1 100644 --- a/ci/buildspec/build_backend.yml +++ b/ci/buildspec/build_backend.yml @@ -19,9 +19,6 @@ phases: build: commands: - echo "BUILD PHASE STARTING - DEBUG TEST" - - echo "Cleaning workspace..." - - git clean -fdx - - git reset --hard HEAD - export ECR_HOST="${ECR_ACCOUNT_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com" - export ECR_REPO="${ECR_HOST}/${ECR_NAMESPACE}" - echo "ECR_REPO is ${ECR_REPO}" From 6c8036341802b8ceb73f512e38ec3372420d7b62 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 10:57:26 -0700 Subject: [PATCH 144/217] fix: adding dbmigrate back --- cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 25fc3e91a13..a07ecfd5037 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -163,7 +163,7 @@ export class DoorwayBuildPipelineStack extends Stack { pipeline.addStage({ stageName: "Dev", - actions: [ecsDeploy], + actions: [dbmigrate, ecsDeploy], }) } } From f2cfad7e606ecd87be1379bbf60dfc597ecc2209 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 11:17:45 -0700 Subject: [PATCH 145/217] fix: fixing buildspec --- build/docker/Dockerfile.backend | 4 ++-- ci/buildspec/migrate_stop_backend.yml | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index b7fca6ce450..a7458bed620 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -170,6 +170,6 @@ RUN chown node:node /app/api/entrypoint-migrate.sh RUN chmod 755 /app/api/entrypoint-migrate.sh ## This CMD should be run as the node user USER node +RUN yarn prisma generate - -ENTRYPOINT [ "/app/api/entrypoint-migrate.sh" ] +ENTRYPOINT [ "yarn ${MIGRATION_CMD}" ] diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 202ca02fcb7..c9661d4b129 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -31,9 +31,6 @@ phases: - if [[ ! -z "${ECS_PRIVATE_BACKEND_SERVICE}" ]]; then aws ecs wait services-stable --cluster "${ECS_BACKEND_CLUSTER}" --services "${ECS_PRIVATE_BACKEND_SERVICE}"; fi # Default to db:reseed:ci - export MIGRATION_CMD="${MIGRATION_CMD:-db:reseed:ci}" - # Execute migration - # Note that many of these vars are required by the app but not used for migration - - > - export PGUSER=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."username") - export PGPASSWORD=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."password") - export PGPORT=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."port") @@ -44,7 +41,7 @@ phases: - export DATABASE_URL="postgres://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" - echo "Database URL (password masked):postgres://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" - docker run + - docker run --env PGUSER="${PGUSER}" --env PGPASSWORD="${PGPASSWORD}" --env PGHOST="${PGHOST}" From fd5cbf9ddb4a6a93d5dd6627595d7f219650e864 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 11:18:42 -0700 Subject: [PATCH 146/217] fix: adding migration cmd --- ci/buildspec/migrate_stop_backend.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index c9661d4b129..6e9f406173c 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -57,6 +57,7 @@ phases: --env CLOUDINARY_SECRET="${CLOUDINARY_SECRET:-dummy_secret}" --env CLOUDINARY_KEY="${CLOUDINARY_KEY:-dummy_key}" --env ADMIN_ACCOUNTS="${ADMIN_ACCOUNTS:-100}" + --env MIGRATION_CMD="${MIGRATION_CMD}" --env PARTNERS_BASE_URL="${PARTNERS_BASE_URL:-http://localhost:3001/not-used}" --env PARTNERS_PORTAL_URL="${PARTNERS_PORTAL_URL:-http://localhost:3001/not-used}" --env SKIP_MIGRATIONS=FALSE From dbbc7c174bdac3fb75126f8217a05554aa5fecdc Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 11:32:05 -0700 Subject: [PATCH 147/217] fix: missed quote --- ci/buildspec/migrate_stop_backend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 6e9f406173c..10319ec268c 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -61,4 +61,4 @@ phases: --env PARTNERS_BASE_URL="${PARTNERS_BASE_URL:-http://localhost:3001/not-used}" --env PARTNERS_PORTAL_URL="${PARTNERS_PORTAL_URL:-http://localhost:3001/not-used}" --env SKIP_MIGRATIONS=FALSE - "${MIGRATION_IMAGE} + "${MIGRATION_IMAGE}" From 646945f7c76c158dc64d70cd347eeee2d47ec502 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 11:48:42 -0700 Subject: [PATCH 148/217] fix: hard coding entrypoint --- build/docker/Dockerfile.backend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index a7458bed620..63333d9b3dc 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -172,4 +172,4 @@ RUN chmod 755 /app/api/entrypoint-migrate.sh USER node RUN yarn prisma generate -ENTRYPOINT [ "yarn ${MIGRATION_CMD}" ] +ENTRYPOINT [ "yarn db:reseed:ci" ] From e5f74291519fff361ca0eaa8c2a5bed475f43485 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 12:06:08 -0700 Subject: [PATCH 149/217] fix: trying with script --- build/docker/Dockerfile.backend | 2 +- build/docker/entrypoint-migrate.sh | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index 63333d9b3dc..96fcfc32763 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -172,4 +172,4 @@ RUN chmod 755 /app/api/entrypoint-migrate.sh USER node RUN yarn prisma generate -ENTRYPOINT [ "yarn db:reseed:ci" ] +ENTRYPOINT [ "/app/api/entrypoint-run.sh" ] diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh index 84883d3ba9e..3262c8f55dc 100644 --- a/build/docker/entrypoint-migrate.sh +++ b/build/docker/entrypoint-migrate.sh @@ -1,8 +1,4 @@ #!/bin/sh -# URL encode the password to handle special characters -ENCODED_PASSWORD=$(node -e "console.log(encodeURIComponent('$PGPASSWORD'))") - -export DATABASE_URL="postgresql://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" if [ "$SKIP_MIGRATIONS" = TRUE ];then echo "Skipping Migrations" yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; From 209a182f054d32261011a7db62898dc425e7abb6 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 12:29:16 -0700 Subject: [PATCH 150/217] fix: fixing migrate entrypoint and some echoing in the run script --- build/docker/Dockerfile.backend | 2 +- build/docker/entrypoint-run.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/docker/Dockerfile.backend b/build/docker/Dockerfile.backend index 96fcfc32763..5fe50c66a7c 100644 --- a/build/docker/Dockerfile.backend +++ b/build/docker/Dockerfile.backend @@ -172,4 +172,4 @@ RUN chmod 755 /app/api/entrypoint-migrate.sh USER node RUN yarn prisma generate -ENTRYPOINT [ "/app/api/entrypoint-run.sh" ] +ENTRYPOINT [ "/app/api/entrypoint-migrate.sh" ] diff --git a/build/docker/entrypoint-run.sh b/build/docker/entrypoint-run.sh index b0f8c7cd3cb..d930b533e6c 100644 --- a/build/docker/entrypoint-run.sh +++ b/build/docker/entrypoint-run.sh @@ -19,5 +19,5 @@ ENCODED_PASSWORD=$(node -e "console.log(encodeURIComponent('$PGPASSWORD'))") export DATABASE_URL="postgresql://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" echo "DATABASE_URL (password masked): postgresql://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" -echo "Running migrations..." -yarn prisma generate && yarn start:prod +echo "Starting Service..." +yarn start:prod From 4061a4a04ca410bbeb9370264f5ac262e22d0729 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 12:45:58 -0700 Subject: [PATCH 151/217] fix: setting db url in entrypoint --- build/docker/entrypoint-migrate.sh | 20 ++++++++++++++++++++ ci/buildspec/migrate_stop_backend.yml | 7 ++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/build/docker/entrypoint-migrate.sh b/build/docker/entrypoint-migrate.sh index 3262c8f55dc..aef4fdd9e53 100644 --- a/build/docker/entrypoint-migrate.sh +++ b/build/docker/entrypoint-migrate.sh @@ -1,4 +1,24 @@ #!/bin/sh +# Debug the individual secret values +echo "PGUSER: $PGUSER" +echo "PGHOST: $PGHOST" +echo "PGPORT: $PGPORT" +echo "PGDATABASE: $PGDATABASE" +echo "PGPASSWORD length: ${#PGPASSWORD}" + +# Validate port is numeric +if ! echo "$PGPORT" | grep -qE '^[0-9]+$'; then + echo "ERROR: PGPORT is not a valid number: '$PGPORT'" + exit 1 +fi + +# URL encode the password to handle special characters +ENCODED_PASSWORD=$(node -e "console.log(encodeURIComponent('$PGPASSWORD'))") + +export DATABASE_URL="postgresql://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" +echo "DATABASE_URL (password masked): postgresql://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" + + if [ "$SKIP_MIGRATIONS" = TRUE ];then echo "Skipping Migrations" yarn db:migration:skip 00_init || true && yarn db:migration:skip 02_hba_to_prisma || true && yarn db:migration:skip 03_0_external_listing || true && yarn db:migration:run; diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 10319ec268c..8b757313d7d 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -36,10 +36,7 @@ phases: - export PGPORT=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."port") - export PGHOST=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."host") - export PGDATABASE=$(aws secretsmanager get-secret-value --secret-id ${DB_CREDS_ARN}|jq ."SecretString"|jq -r|jq -r ."dbname") - # URL encode the password to handle special characters - - export ENCODED_PASSWORD=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${PGPASSWORD}', safe=''))") - - export DATABASE_URL="postgres://${PGUSER}:${ENCODED_PASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" - - echo "Database URL (password masked):postgres://${PGUSER}:***@${PGHOST}:${PGPORT}/${PGDATABASE}" + - docker run --env PGUSER="${PGUSER}" @@ -47,7 +44,7 @@ phases: --env PGHOST="${PGHOST}" --env PGDATABASE=${PGDATABASE} --env PGPORT=${PGPORT} - --env DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}" + --env CLOUDINARY_CLOUD_NAME="${CLOUDINARY_CLOUD_NAME}" --env LISTINGS_QUERY="${LISTINGS_QUERY}" --env FILE_SERVICE="${FILE_SERVICE:-cloudinary}" From e8ddef197ed1044f1819165bbb90de713972c38c Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 13:04:32 -0700 Subject: [PATCH 152/217] fix: accidental newline --- ci/buildspec/migrate_stop_backend.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/ci/buildspec/migrate_stop_backend.yml b/ci/buildspec/migrate_stop_backend.yml index 8b757313d7d..6b6579523ce 100644 --- a/ci/buildspec/migrate_stop_backend.yml +++ b/ci/buildspec/migrate_stop_backend.yml @@ -44,7 +44,6 @@ phases: --env PGHOST="${PGHOST}" --env PGDATABASE=${PGDATABASE} --env PGPORT=${PGPORT} - --env CLOUDINARY_CLOUD_NAME="${CLOUDINARY_CLOUD_NAME}" --env LISTINGS_QUERY="${LISTINGS_QUERY}" --env FILE_SERVICE="${FILE_SERVICE:-cloudinary}" From d8651e67e7f4803ecb5ec3777e95ba5b53cae853 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 18:22:53 -0700 Subject: [PATCH 153/217] fix: adding backend api base --- cdk/lib/build-pipeline/doorway-docker-build.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts index 002a0862091..aca63f9175e 100644 --- a/cdk/lib/build-pipeline/doorway-docker-build.ts +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -42,6 +42,7 @@ export class DoorwayDockerBuild { IMAGE_NAME: { value: props.imageName }, DOCKER_HUB_SECRET_ARN: { value: props.dockerHubSecret }, ENVIRONMENT: { value: props.environment }, + BACKEND_API_BASE: { value: `http://backend.${props.environment}.housingbayarea.mtc.ca.gov` } }, role: props.buildRole, cache: Cache.none(), From 768c5329a02141872129305f000520abd2189a62 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 3 Sep 2025 18:24:40 -0700 Subject: [PATCH 154/217] fix: removing ecs interdependencies --- cdk/lib/doorway_environment/doorway-app-environment-stack.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index b8e515112e0..e171f00ee2b 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -3,6 +3,7 @@ import { LogGroup } from "aws-cdk-lib/aws-logs" import { Construct } from "constructs" import dotenv from "dotenv" import path from "path" + import { DoorwayProps } from "./doorway-props" import { DoorwayPublicLoadBalancer } from "./doorway-public-load-balancer" import { DoorwayBackendService } from "./services/doorway-backend-service" @@ -42,11 +43,9 @@ export class DoorwayAppEnvironmentStack extends Stack { const api = new DoorwayBackendService(this, `doorway-api-service-${environment}`, props) const lb = new DoorwayPublicLoadBalancer(this, `doorway-public-lb-${environment}`, props); const publicSite = new DoorwayPublicSite(this, `doorway-public-${environment}`, props) - publicSite.service.node.addDependency(api.service) publicSite.service.node.addDependency(lb.loadBalancer) lb.publicTargetGroup.addTarget(publicSite.service) const partnersSite = new DoorwayPartnersSite(this, `doorway-partners-${environment}`, props) - partnersSite.service.node.addDependency(api.service) partnersSite.service.node.addDependency(lb.loadBalancer) lb.partnersTargetGroup.addTarget(partnersSite.service) From ee6ca59f19242858a70002a003c5df69cc051aa0 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 4 Sep 2025 09:41:29 -0700 Subject: [PATCH 155/217] fix: style change --- ci/buildspec/build_partners.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/ci/buildspec/build_partners.yml b/ci/buildspec/build_partners.yml index 835b84c961c..15eff1d3bfd 100644 --- a/ci/buildspec/build_partners.yml +++ b/ci/buildspec/build_partners.yml @@ -15,12 +15,10 @@ phases: pre_build: commands: - aws ecr get-login-password --region "${ECR_REGION}" | docker login --username AWS --password-stdin "${ECR_ACCOUNT_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com" - build: commands: - export ECR_HOST="${ECR_ACCOUNT_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com" - export ECR_REPO="${ECR_HOST}/${ECR_NAMESPACE}" - # Build test image - > docker build . -f build/docker/Dockerfile.sites-generic From 1b8305aafa5b7d662b0b75a81e4663ff47c38aca Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 4 Sep 2025 11:17:08 -0700 Subject: [PATCH 156/217] fix: adding more environment variables --- .../doorway-build-pipeline-stack.ts | 10 +-- .../build-pipeline/doorway-docker-build.ts | 70 ++++++++++++++++--- .../doorway-app-environment-stack.ts | 1 - 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index a07ecfd5037..975886cb3e1 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -6,7 +6,7 @@ import { Secret } from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" import { DoorwayDatabaseMigrate } from "./doorway-database-migrate" -import { DoorwayDockerBuild } from "./doorway-docker-build" +import { BackendDockerBuild, ImportDockerBuild, PartnersDockerBuild, PublicDockerBuild } from "./doorway-docker-build" import { DoorwayECSDeploy } from "./doorway-ecs-deploy" export class DoorwayBuildPipelineStack extends Stack { @@ -112,7 +112,7 @@ export class DoorwayBuildPipelineStack extends Stack { pipeline.addStage({ stageName: "Build-Dev", actions: [ - new DoorwayDockerBuild(this, `doorway-backend`, { + new BackendDockerBuild(this, `doorway-backend`, { buildspec: "../ci/buildspec/build_backend.yml", imageName: "backend", source: sourceArtifact, @@ -120,7 +120,7 @@ export class DoorwayBuildPipelineStack extends Stack { buildRole: buildRole, environment: "dev2" }).action, - new DoorwayDockerBuild(this, "doorway-import-listings", { + new ImportDockerBuild(this, "doorway-import-listings", { buildspec: "../ci/buildspec/build_import_listings.yml", imageName: "import-listings", source: sourceArtifact, @@ -128,7 +128,7 @@ export class DoorwayBuildPipelineStack extends Stack { buildRole: buildRole, environment: "dev2" }).action, - new DoorwayDockerBuild(this, `doorway-partners`, { + new PartnersDockerBuild(this, `doorway-partners`, { buildspec: "../ci/buildspec/build_partners.yml", imageName: "partners", source: sourceArtifact, @@ -136,7 +136,7 @@ export class DoorwayBuildPipelineStack extends Stack { buildRole: buildRole, environment: "dev2" }).action, - new DoorwayDockerBuild(this, "doorway-public", { + new PublicDockerBuild(this, "doorway-public", { buildspec: "../ci/buildspec/build_public.yml", imageName: "public", source: sourceArtifact, diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts index aca63f9175e..50e43a77579 100644 --- a/cdk/lib/build-pipeline/doorway-docker-build.ts +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -5,7 +5,9 @@ import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" import { Repository } from "aws-cdk-lib/aws-ecr" import { Role } from "aws-cdk-lib/aws-iam" import { Construct } from "constructs/lib/construct" +import dotenv from "dotenv" import fs from "fs" +import path from "path" import * as YAML from "yaml" export interface DoorwayDockerBuildProps { @@ -16,16 +18,33 @@ export interface DoorwayDockerBuildProps { buildRole: Role environment: string } - -export class DoorwayDockerBuild { +class DoorwayDockerBuild { public readonly action: CodeBuildAction + protected environmentVariables: { [name: string]: { value: string } } + + constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { // Create ECR repository const repo = new Repository(scope, `${id}-ECRRepository`, { repositoryName: `doorway/${props.imageName}`, }) + dotenv.config({ path: path.resolve(__dirname, `../../${props.environment}.env`) }) + this.environmentVariables = { + ECR_REGION: { value: Aws.REGION }, + ECR_ACCOUNT_ID: { value: Aws.ACCOUNT_ID }, + ECR_NAMESPACE: { value: "doorway" }, + IMAGE_NAME: { value: props.imageName }, + DOCKER_HUB_SECRET_ARN: { value: props.dockerHubSecret }, + ENVIRONMENT: { value: props.environment }, + BACKEND_API_BASE: { value: `http://backend.${props.environment}.housingbayarea.mtc.ca.gov` }, + CACHE_REVALIDATE: { value: process.env.CACHE_REVALIDATE || "30" }, + LANGUAGES: { + value: process.env.LANGUAGES || "en,es,zh,vi,tl" + }, + MAPBOX_TOKEN: { value: process.env.MAPBOX_TOKEN || "" } + } // Grant permissions to the provided role repo.grantPullPush(props.buildRole) @@ -35,15 +54,7 @@ export class DoorwayDockerBuild { environment: { computeType: ComputeType.LARGE, }, - environmentVariables: { - ECR_REGION: { value: Aws.REGION }, - ECR_ACCOUNT_ID: { value: Aws.ACCOUNT_ID }, - ECR_NAMESPACE: { value: "doorway" }, - IMAGE_NAME: { value: props.imageName }, - DOCKER_HUB_SECRET_ARN: { value: props.dockerHubSecret }, - ENVIRONMENT: { value: props.environment }, - BACKEND_API_BASE: { value: `http://backend.${props.environment}.housingbayarea.mtc.ca.gov` } - }, + environmentVariables: this.environmentVariables, role: props.buildRole, cache: Cache.none(), }) @@ -57,3 +68,40 @@ export class DoorwayDockerBuild { }) } } + +export class PartnersDockerBuild extends DoorwayDockerBuild { + constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { + super(scope, id, props) + this.environmentVariables.FEATURE_LISTINGS_APPROVAL = { value: process.env.FEATURE_LISTINGS_APPROVAL || "true" } + this.environmentVariables.USE_SECURE_DOWNLOAD_PATHWAY = { value: process.env.USE_SECURE_DOWNLOAD_PATHWAY || "true" } + this.environmentVariables.CLOUDINARY_CLOUD_NAME = { value: process.env.CLOUDINARY_CLOUD_NAME || "exygy" } + } +} +export class PublicDockerBuild extends DoorwayDockerBuild { + constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { + super(scope, id, props) + this.environmentVariables.SHOW_PROFESSIONAL_PARTNERS = { value: process.env.SHOW_PROFESSIONAL_PARTNERS || "true" } + this.environmentVariables.BLOOM_API_BASE = { value: process.env.BLOOM_API_BASE || "" } + this.environmentVariables.JURISDICTION_NAME = { + value: process.env.JURISDICTION_NAME || "Bay Area" + } + this.environmentVariables.MAINTENANCE_WINDOW = { value: process.env.MAINTENANCE_WINDOW || "2024-08-07 10:00 -07:00,2024-08-08 8:00 -07:00" } + this.environmentVariables.SITE_MESSAGE_WINDOW = { value: process.env.SITE_MESSAGE_WINDOW || "2025-05-05 10:00,2025-08-08 8:00" } + this.environmentVariables.NOTIFICATIONS_SIGN_UP_URL = { value: process.env.NOTIFICATIONS_SIGN_UP_URL || "https://public.govdelivery.com/accounts/CAMTC/signup/36832" } + this.environmentVariables.GTM_KEY = { value: process.env.GTM_KEY || "" } + } +} +export class BackendDockerBuild extends DoorwayDockerBuild { + constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { + super(scope, id, props) + // Backend build doesnt have any custom environment variables yet + + } +} +export class ImportDockerBuild extends DoorwayDockerBuild { + constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { + super(scope, id, props) + // Import listings build doesnt have any custom environment variables yet + + } +} \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index e171f00ee2b..3f6edf39ff6 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -51,6 +51,5 @@ export class DoorwayAppEnvironmentStack extends Stack { - } } From 9e48bceb1f91ca2a1be2dddaa971f7228b34c1b8 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 4 Sep 2025 12:28:20 -0700 Subject: [PATCH 157/217] fix: dbmigrate role fixes and backend api variable change --- .../build-pipeline/doorway-build-pipeline-stack.ts | 3 ++- cdk/lib/build-pipeline/doorway-database-migrate.ts | 12 ++++++++---- cdk/lib/build-pipeline/doorway-docker-build.ts | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 975886cb3e1..6cf3e7650ca 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -58,6 +58,7 @@ export class DoorwayBuildPipelineStack extends Stack { actions: ["secretsmanager:GetSecretValue"], resources: [ `arn:aws:secretsmanager:${this.region}:${this.account}:secret:mtc/dockerHub*`, + `arn:aws:secretsmanager:${this.region}:${this.account}:secret:doorwayDBSecret-*`, ], }), new PolicyStatement({ @@ -151,7 +152,7 @@ export class DoorwayBuildPipelineStack extends Stack { environment: "dev2", buildspec: "./ci/buildspec/migrate_stop_backend.yml", source: sourceArtifact, - buildRole: buildRole, + buildRole: buildRole, // This parameter is now ignored }).action const ecsDeploy = new DoorwayECSDeploy(this, "doorway-ecs-deploy-dev", { diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts index ac8695f6507..182861d0326 100644 --- a/cdk/lib/build-pipeline/doorway-database-migrate.ts +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -3,7 +3,7 @@ import { BuildSpec, Cache, ComputeType, LinuxBuildImage, PipelineProject } from import { Artifact } from "aws-cdk-lib/aws-codepipeline" import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" import { SecurityGroup, Subnet, Vpc } from "aws-cdk-lib/aws-ec2" -import { Role } from "aws-cdk-lib/aws-iam" +import { PolicyStatement, Role } from "aws-cdk-lib/aws-iam" import { Secret } from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" @@ -37,8 +37,6 @@ export class DoorwayDatabaseMigrate { subnetId: subnetId, }) const sg = SecurityGroup.fromSecurityGroupId(scope, "sg", securityGroupId) - Secret.fromSecretCompleteArn(scope, "dbSecret", dbSecretArn).grantRead(props.buildRole) - const project = new PipelineProject(scope, `doorway-dbMigrate-${props.environment}`, { vpc: vpc, subnetSelection: { subnets: [subnet] }, @@ -70,9 +68,15 @@ export class DoorwayDatabaseMigrate { value: "db:reseed:ci", }, }, - role: props.buildRole, cache: Cache.none(), }) + + // Grant permissions to the auto-created role + Secret.fromSecretCompleteArn(scope, "dbSecret", dbSecretArn).grantRead(project.role!) + project.addToRolePolicy(new PolicyStatement({ + actions: ["ecs:*", "ecr:*"], + resources: ["*"] + })) // Create the CodeBuild action this.action = new CodeBuildAction({ actionName: `${id}-DBMigrate`, diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts index 50e43a77579..6b684d444c6 100644 --- a/cdk/lib/build-pipeline/doorway-docker-build.ts +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -37,7 +37,7 @@ class DoorwayDockerBuild { IMAGE_NAME: { value: props.imageName }, DOCKER_HUB_SECRET_ARN: { value: props.dockerHubSecret }, ENVIRONMENT: { value: props.environment }, - BACKEND_API_BASE: { value: `http://backend.${props.environment}.housingbayarea.mtc.ca.gov` }, + BACKEND_API_BASE: { value: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int:3100` }, CACHE_REVALIDATE: { value: process.env.CACHE_REVALIDATE || "30" }, LANGUAGES: { value: process.env.LANGUAGES || "en,es,zh,vi,tl" @@ -94,7 +94,7 @@ export class PublicDockerBuild extends DoorwayDockerBuild { export class BackendDockerBuild extends DoorwayDockerBuild { constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { super(scope, id, props) - // Backend build doesnt have any custom environment variables yet + // Backend build doesnt have any custom environment variables yety } } From 1fb55abf97bb03ed7b58bf75c7b6d94444184ab4 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 4 Sep 2025 13:29:45 -0700 Subject: [PATCH 158/217] feat: adding cloudfront --- .../doorway-public-load-balancer.ts | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index ff616e6a6f4..fa236a3ec79 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -1,5 +1,7 @@ import { aws_ec2, Duration, Fn } from "aws-cdk-lib"; import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; +import { AllowedMethods, CachePolicy, Distribution, OriginRequestPolicy, ViewerProtocolPolicy } from "aws-cdk-lib/aws-cloudfront"; +import { LoadBalancerV2Origin } from "aws-cdk-lib/aws-cloudfront-origins"; import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2"; import { ApplicationListener, ApplicationListenerRule, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup, ListenerAction, ListenerCondition, TargetType } from "aws-cdk-lib/aws-elasticloadbalancingv2"; import { ARecord, HostedZone, RecordTarget } from "aws-cdk-lib/aws-route53"; @@ -47,8 +49,9 @@ export class DoorwayPublicLoadBalancer { const dnsZone = HostedZone.fromLookup(scope, `publicDnsZone-${props.environment}`, { domainName: "housingbayarea.mtc.ca.gov", }); + const publicDomainName = process.env.PUBLIC_PORTAL_DOMAIN || `${props.environment}.housingbayarea.mtc.ca.gov` const cert = new Certificate(scope, "PublicCertificate", { - domainName: process.env.PUBLIC_PORTAL_DOMAIN || `${props.environment}.housingbayarea.mtc.ca.gov`, + domainName: publicDomainName, validation: CertificateValidation.fromDns(dnsZone), subjectAlternativeNames: [process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov`] }) @@ -101,14 +104,14 @@ export class DoorwayPublicLoadBalancer { listener: httpsListener, priority: 100, action: ListenerAction.forward([this.publicTargetGroup]), - conditions: [ListenerCondition.hostHeaders([process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`]) + conditions: [ListenerCondition.hostHeaders([publicDomainName]) ] }); new ARecord(scope, "PublicARecord", { zone: dnsZone, - recordName: process.env.PUBLIC_PORTAL_DOMAIN || `${props.environment}.housingbayarea.mtc.ca.gov`, + recordName: publicDomainName, target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), }); this.partnersTargetGroup = new ApplicationTargetGroup(scope, "PartnersTargetGroup", { @@ -140,6 +143,26 @@ export class DoorwayPublicLoadBalancer { recordName: process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov`, target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), }); + const cloudfrontDist = new Distribution(scope, `CloudFront-${props.environment}`, { + domainNames: [publicDomainName, `*.${publicDomainName}`], + + defaultBehavior: { + origin: new LoadBalancerV2Origin(this.loadBalancer), + cachePolicy: CachePolicy.CACHING_DISABLED, // Disable caching + viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + allowedMethods: AllowedMethods.ALLOW_ALL, + originRequestPolicy: OriginRequestPolicy.ALL_VIEWER + }, + additionalBehaviors: { + "/images/*": { + origin: new LoadBalancerV2Origin(this.loadBalancer), + allowedMethods: AllowedMethods.ALLOW_ALL, + cachePolicy: CachePolicy.CACHING_OPTIMIZED, + viewerProtocolPolicy: ViewerProtocolPolicy.ALLOW_ALL, + originRequestPolicy: OriginRequestPolicy.ALL_VIEWER + } + } + }) } From e0bb907e4ebf8717bd59220c5ca57b0fedadb307 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 4 Sep 2025 15:30:19 -0700 Subject: [PATCH 159/217] feat: moving s3 stack --- .../doorway-app-environment-stack.ts | 3 + .../doorway-public-load-balancer.ts | 3 - .../services/doorway-public-site.ts | 8 ++- .../services/doorway-s3-stack.ts | 61 +++++++++++++++++++ 4 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 cdk/lib/doorway_environment/services/doorway-s3-stack.ts diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 3f6edf39ff6..75277fcfa76 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -9,6 +9,7 @@ import { DoorwayPublicLoadBalancer } from "./doorway-public-load-balancer" import { DoorwayBackendService } from "./services/doorway-backend-service" import { DoorwayPartnersSite } from "./services/doorway-partners-site" import { DoorwayPublicSite } from "./services/doorway-public-site" +import { DoorwayS3Stack } from "./services/doorway-s3-stack" export class DoorwayAppEnvironmentStack extends Stack { constructor(scope: Construct, id: string) { @@ -39,6 +40,8 @@ export class DoorwayAppEnvironmentStack extends Stack { partnersServiceName: `doorway-partners-${environment}`, clusterName: Fn.importValue(`doorway-ecs-cluster-${environment}`), } + new DoorwayS3Stack(this, `doorway-s3-${environment}`, props) + const api = new DoorwayBackendService(this, `doorway-api-service-${environment}`, props) const lb = new DoorwayPublicLoadBalancer(this, `doorway-public-lb-${environment}`, props); diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index fa236a3ec79..77bd38b21fd 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -99,15 +99,12 @@ export class DoorwayPublicLoadBalancer { unhealthyThresholdCount: 2, }, }) - new ApplicationListenerRule(scope, "PublicDomainRule", { listener: httpsListener, priority: 100, action: ListenerAction.forward([this.publicTargetGroup]), conditions: [ListenerCondition.hostHeaders([publicDomainName]) ] - - }); new ARecord(scope, "PublicARecord", { zone: dnsZone, diff --git a/cdk/lib/doorway_environment/services/doorway-public-site.ts b/cdk/lib/doorway_environment/services/doorway-public-site.ts index 4cbdb10d74c..cc07d5966ea 100644 --- a/cdk/lib/doorway_environment/services/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-public-site.ts @@ -17,11 +17,15 @@ export class DoorwayPublicSite { assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), }) - const publicUploads = Bucket.fromBucketArn( + const publicUploads = Bucket.fromBucketName( scope, `publicUploadsBucket-${id}`, - Fn.importValue(`doorway-public-uploads-${props.environment}`), + + `doorway-public-uploads-${props.environment}`, ) + + + const secureUploads = Bucket.fromBucketArn( scope, `secureUploadsBucket-${id}`, diff --git a/cdk/lib/doorway_environment/services/doorway-s3-stack.ts b/cdk/lib/doorway_environment/services/doorway-s3-stack.ts new file mode 100644 index 00000000000..f630c44604d --- /dev/null +++ b/cdk/lib/doorway_environment/services/doorway-s3-stack.ts @@ -0,0 +1,61 @@ +import { CfnOutput, Stack } from "aws-cdk-lib"; +import { Bucket, HttpMethods } from "aws-cdk-lib/aws-s3"; +import { Construct } from "constructs"; +import dotenv from "dotenv"; +import path from "path"; + +import { DoorwayProps } from "../doorway-props"; + +export class DoorwayS3Stack extends Stack { + constructor(scope: Construct, id: string, props: DoorwayProps) { + super(scope, id); + dotenv.config({ path: path.resolve(__dirname, `../../${props.environment}.env`) }) + + const secureUploadsBucket = new Bucket(this, "secureUploadsBucket", { + bucketName: `doorway-secure-uploads-${props.environment}`, + blockPublicAccess: { + blockPublicAcls: true, + blockPublicPolicy: true, + ignorePublicAcls: true, + restrictPublicBuckets: true, + }, + enforceSSL: true, + }); + + + // Output the bucket name + new CfnOutput(this, "SecureBucketName", { + exportName: `doorway-secure-uploads-${props.environment}`, + value: secureUploadsBucket.bucketArn, + description: `Doorway Secure Uploads Bucket ARN for the ${props.environment} environment`, + }); + const publicUploadsBucket = new Bucket(this, "publicUploadsBucket", { + bucketName: `doorway-public-uploads-${props.environment}`, + blockPublicAccess: { + blockPublicAcls: false, + blockPublicPolicy: false, + ignorePublicAcls: false, + restrictPublicBuckets: false, + }, + enforceSSL: true, + }); + publicUploadsBucket.grantPublicAccess(); + publicUploadsBucket.addCorsRule({ + allowedMethods: [ + HttpMethods.GET + ], + allowedOrigins: [ + `https://${process.env.PUBLIC_PORTAL_DOMAIN || `${props.environment}.housingbayarea.mtc.ca.gov`}`, + ], + allowedHeaders: ["*"], + exposedHeaders: [], + }) + + // Output the bucket name + new CfnOutput(this, "PublicBucketName", { + exportName: `doorway-public-uploads-${props.environment}`, + value: publicUploadsBucket.bucketArn, + description: `Doorway Public Uploads Bucket ARN for the ${props.environment} environment`, + }); + } +} From c0b9fb7dc48443b66750b7acbe856e71430ba1a9 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 4 Sep 2025 16:08:36 -0700 Subject: [PATCH 160/217] fix: adding access --- .../doorway-database-migrate.ts | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts index 182861d0326..745a5833493 100644 --- a/cdk/lib/build-pipeline/doorway-database-migrate.ts +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -70,13 +70,34 @@ export class DoorwayDatabaseMigrate { }, cache: Cache.none(), }) - + // Grant permissions to the auto-created role Secret.fromSecretCompleteArn(scope, "dbSecret", dbSecretArn).grantRead(project.role!) project.addToRolePolicy(new PolicyStatement({ actions: ["ecs:*", "ecr:*"], resources: ["*"] })) + project.addToRolePolicy(new PolicyStatement({ + + actions: ["secretsmanager:GetSecretValue"], + resources: [ + `arn:aws:secretsmanager:${Aws.REGION}:${Aws.ACCOUNT_ID}:secret:mtc/dockerHub*`, + `arn:aws:secretsmanager::${Aws.REGION}:${Aws.ACCOUNT_ID}:secret:doorwayDBSecret-*`, + ], + })) + project.addToRolePolicy(new PolicyStatement({ + actions: [ + "cloudformation:*", + "ec2:*", + "ssm:*", + "codebuild:*", + "logs:*", + "iam:AssumeRole", + "iam:PassRole", + ], + resources: ["*"], + })) + // Create the CodeBuild action this.action = new CodeBuildAction({ actionName: `${id}-DBMigrate`, From b310885d5af106fc5482b5f6b79064f0cee024ef Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 4 Sep 2025 16:18:23 -0700 Subject: [PATCH 161/217] fix: adding cert and georestriction --- .../doorway-public-load-balancer.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index 77bd38b21fd..0da02230198 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -1,6 +1,6 @@ import { aws_ec2, Duration, Fn } from "aws-cdk-lib"; import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; -import { AllowedMethods, CachePolicy, Distribution, OriginRequestPolicy, ViewerProtocolPolicy } from "aws-cdk-lib/aws-cloudfront"; +import { AllowedMethods, CachePolicy, Distribution, OriginRequestPolicy, PriceClass, SecurityPolicyProtocol, ViewerProtocolPolicy } from "aws-cdk-lib/aws-cloudfront"; import { LoadBalancerV2Origin } from "aws-cdk-lib/aws-cloudfront-origins"; import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2"; import { ApplicationListener, ApplicationListenerRule, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup, ListenerAction, ListenerCondition, TargetType } from "aws-cdk-lib/aws-elasticloadbalancingv2"; @@ -142,6 +142,23 @@ export class DoorwayPublicLoadBalancer { }); const cloudfrontDist = new Distribution(scope, `CloudFront-${props.environment}`, { domainNames: [publicDomainName, `*.${publicDomainName}`], + certificate: cert, + minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021, + enableIpv6: true, + + + defaultRootObject: "", + logIncludesCookies: true, + logFilePrefix: "cloudfront", + + comment: `Doorway for ${props.environment}`, + + priceClass: PriceClass.PRICE_CLASS_100, + geoRestriction: { + locations: ["US"], + restrictionType: "whitelist" + }, + defaultBehavior: { origin: new LoadBalancerV2Origin(this.loadBalancer), From 5df953b170c8c81816a5db89f840a3e7da728588 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 5 Sep 2025 09:48:21 -0700 Subject: [PATCH 162/217] fix: pipeline fixes --- cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 2 +- cdk/lib/build-pipeline/doorway-database-migrate.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 6cf3e7650ca..5fe152dd182 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -164,7 +164,7 @@ export class DoorwayBuildPipelineStack extends Stack { pipeline.addStage({ stageName: "Dev", - actions: [dbmigrate, ecsDeploy], + actions: [ecsDeploy], }) } } diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts index 745a5833493..8447c448bad 100644 --- a/cdk/lib/build-pipeline/doorway-database-migrate.ts +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -85,6 +85,10 @@ export class DoorwayDatabaseMigrate { `arn:aws:secretsmanager::${Aws.REGION}:${Aws.ACCOUNT_ID}:secret:doorwayDBSecret-*`, ], })) + project.addToRolePolicy(new PolicyStatement({ + actions: ["iam:PassRole"], + resources: [`arn:aws:iam::${Aws.ACCOUNT_ID}:role/*doorway*internal-api-sc-role*`] + })) project.addToRolePolicy(new PolicyStatement({ actions: [ "cloudformation:*", From 8544173c8e7f08df46af6d043ed1fa12cdc40aa2 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 5 Sep 2025 09:50:29 -0700 Subject: [PATCH 163/217] fix: make api dependent on s3 stack --- cdk/lib/doorway_environment/doorway-app-environment-stack.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 75277fcfa76..b9d215e3650 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -40,10 +40,11 @@ export class DoorwayAppEnvironmentStack extends Stack { partnersServiceName: `doorway-partners-${environment}`, clusterName: Fn.importValue(`doorway-ecs-cluster-${environment}`), } - new DoorwayS3Stack(this, `doorway-s3-${environment}`, props) + const s3stack = new DoorwayS3Stack(this, `doorway-s3-${environment}`, props) const api = new DoorwayBackendService(this, `doorway-api-service-${environment}`, props) + api.service.node.addDependency(s3stack) const lb = new DoorwayPublicLoadBalancer(this, `doorway-public-lb-${environment}`, props); const publicSite = new DoorwayPublicSite(this, `doorway-public-${environment}`, props) publicSite.service.node.addDependency(lb.loadBalancer) From 6d6628b3794a7c108d52e4f398067c2d33712918 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 5 Sep 2025 12:56:23 -0700 Subject: [PATCH 164/217] fix: Yay! --- cdk/bin/doorway-cdk.ts | 16 ++++++- cdk/cdk.context.json | 7 +-- .../doorway-app-environment-stack.ts | 44 +++++++++++++++---- .../doorway-cloudfront-cert-stack.ts | 41 +++++++++++++++++ cdk/lib/doorway_environment/doorway-props.ts | 7 ++- .../doorway-public-load-balancer.ts | 17 ++++--- .../services/doorway-backend-service.ts | 15 ++++++- .../services/doorway-partners-site.ts | 4 +- .../services/doorway-public-site.ts | 3 +- .../services/doorway_service.ts | 39 ++++++---------- 10 files changed, 143 insertions(+), 50 deletions(-) create mode 100644 cdk/lib/doorway_environment/doorway-cloudfront-cert-stack.ts diff --git a/cdk/bin/doorway-cdk.ts b/cdk/bin/doorway-cdk.ts index 857ba7d7c85..9d902b6319e 100644 --- a/cdk/bin/doorway-cdk.ts +++ b/cdk/bin/doorway-cdk.ts @@ -3,14 +3,28 @@ import * as cdk from "aws-cdk-lib" import { DoorwayBuildPipelineStack } from "../lib/build-pipeline/doorway-build-pipeline-stack" import { DoorwayAppEnvironmentStack } from "../lib/doorway_environment/doorway-app-environment-stack" +import { DoorwayCloudFrontCertStack } from "../lib/doorway_environment/doorway-cloudfront-cert-stack" + /** * This script is really just a wrapper to execute the various * stacks that make up the Doorway Application infrastructure and build pipeline. **/ const app = new cdk.App() +const environment = process.env.ENVIRONMENT || "dev2" + new DoorwayBuildPipelineStack(app, "DoorwayBuildPipelineStack", { githubSecret: "mtc/githubSecret", dockerHubSecret: "mtc/dockerHub", }) -new DoorwayAppEnvironmentStack(app, "DoorwayAppEnvironmentStack") + +const cfCertStack = new DoorwayCloudFrontCertStack(app, `DoorwayCloudFrontCertStack-${environment}`, { + environment, + publicDomainName: process.env.PUBLIC_PORTAL_DOMAIN || `${environment}.housingbayarea.mtc.ca.gov`, + partnersDomainName: process.env.PARTNERS_PORTAL_DOMAIN || `partners.${environment}.housingbayarea.mtc.ca.gov`, +}) + +new DoorwayAppEnvironmentStack(app, "DoorwayAppEnvironmentStack", { + cfCertArn: cfCertStack.cloudFrontCertArn, +}) + diff --git a/cdk/cdk.context.json b/cdk/cdk.context.json index 72b0a6d8e1b..6e3bed29a77 100644 --- a/cdk/cdk.context.json +++ b/cdk/cdk.context.json @@ -1,12 +1,9 @@ { - "acknowledged-issue-numbers": [ - 34892 - ], - "hosted-zone:account=364076391763:domainName=housingbayarea.mtc.ca.gov:region=us-west-2": { + "hosted-zone:account=364076391763:domainName=housingbayarea.mtc.ca.gov:region=us-east-1": { "Id": "/hostedzone/Z01682742VM0KIXZ4Y3W5", "Name": "housingbayarea.mtc.ca.gov." }, - "hosted-zone:account=364076391763:domainName=housingbayarea.mtc.ca.gov:region=us-west-1": { + "hosted-zone:account=364076391763:domainName=housingbayarea.mtc.ca.gov:region=us-west-2": { "Id": "/hostedzone/Z01682742VM0KIXZ4Y3W5", "Name": "housingbayarea.mtc.ca.gov." } diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index b9d215e3650..f6138f6ac42 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -1,4 +1,5 @@ -import { aws_logs, Fn, RemovalPolicy, Stack } from "aws-cdk-lib" +import { aws_logs, Fn, RemovalPolicy, Stack, StackProps } from "aws-cdk-lib" +import { Vpc } from "aws-cdk-lib/aws-ec2" import { LogGroup } from "aws-cdk-lib/aws-logs" import { Construct } from "constructs" import dotenv from "dotenv" @@ -11,13 +12,19 @@ import { DoorwayPartnersSite } from "./services/doorway-partners-site" import { DoorwayPublicSite } from "./services/doorway-public-site" import { DoorwayS3Stack } from "./services/doorway-s3-stack" +export interface DWAppEnvProps extends StackProps { + cfCertArn: string +} + export class DoorwayAppEnvironmentStack extends Stack { - constructor(scope: Construct, id: string) { + constructor(scope: Construct, id: string, props: DWAppEnvProps) { super(scope, id, { + ...props, env: { account: process.env.CDK_DEFAULT_ACCOUNT || "364076391763", region: process.env.CDK_DEFAULT_REGION || "us-west-2", }, + crossRegionReferences: true }) const environment = process.env.ENVIRONMENT || "dev2" dotenv.config({ path: path.resolve(__dirname, `../../${environment}.env`) }) @@ -29,9 +36,18 @@ export class DoorwayAppEnvironmentStack extends Stack { retention: aws_logs.RetentionDays.ONE_WEEK, }) const scLogGroup = new LogGroup(this, `${id}-service-connect-log-group`, { - logGroupName: `/doorway/${environment}/service-connect` + logGroupName: `/doorway/${environment}/service-connect`, + removalPolicy: RemovalPolicy.DESTROY, + retention: aws_logs.RetentionDays.ONE_WEEK, + }) + const vpcId = Fn.importValue(`doorway-vpc-id-${environment}`) + const azs = Fn.importValue(`doorway-azs-${environment}`).split(",") + const vpc = Vpc.fromVpcAttributes(this, `vpc-${id}`, { + vpcId: vpcId, + availabilityZones: azs, }) - const props: DoorwayProps = { + + const dwProps: DoorwayProps = { environment: environment, serviceConnectLogGroup: scLogGroup, logGroup: logGroup, @@ -39,18 +55,28 @@ export class DoorwayAppEnvironmentStack extends Stack { publicServiceName: `doorway-public-${environment}`, partnersServiceName: `doorway-partners-${environment}`, clusterName: Fn.importValue(`doorway-ecs-cluster-${environment}`), + cfCertArn: props.cfCertArn, + vpc: vpc } - const s3stack = new DoorwayS3Stack(this, `doorway-s3-${environment}`, props) + const s3stack = new DoorwayS3Stack(this, `doorway-s3-${environment}`, dwProps) - const api = new DoorwayBackendService(this, `doorway-api-service-${environment}`, props) + + + const api = new DoorwayBackendService(this, `doorway-api-service-${environment}`, dwProps) api.service.node.addDependency(s3stack) - const lb = new DoorwayPublicLoadBalancer(this, `doorway-public-lb-${environment}`, props); - const publicSite = new DoorwayPublicSite(this, `doorway-public-${environment}`, props) + + const lb = new DoorwayPublicLoadBalancer(this, `doorway-public-lb-${environment}`, dwProps); + lb.loadBalancer.node.addDependency(s3stack) + + const publicSite = new DoorwayPublicSite(this, `doorway-public-${environment}`, dwProps) publicSite.service.node.addDependency(lb.loadBalancer) + publicSite.service.node.addDependency(api.service) + lb.publicTargetGroup.addTarget(publicSite.service) - const partnersSite = new DoorwayPartnersSite(this, `doorway-partners-${environment}`, props) + const partnersSite = new DoorwayPartnersSite(this, `doorway-partners-${environment}`, dwProps) partnersSite.service.node.addDependency(lb.loadBalancer) + partnersSite.service.node.addDependency(api.service) lb.partnersTargetGroup.addTarget(partnersSite.service) diff --git a/cdk/lib/doorway_environment/doorway-cloudfront-cert-stack.ts b/cdk/lib/doorway_environment/doorway-cloudfront-cert-stack.ts new file mode 100644 index 00000000000..f5272c0607f --- /dev/null +++ b/cdk/lib/doorway_environment/doorway-cloudfront-cert-stack.ts @@ -0,0 +1,41 @@ +import { CfnOutput, Stack, StackProps } from "aws-cdk-lib"; +import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; +import { HostedZone } from "aws-cdk-lib/aws-route53"; +import { Construct } from "constructs"; + +export interface CloudFrontCertStackProps extends StackProps { + environment: string; + publicDomainName: string; + partnersDomainName: string; +} + +export class DoorwayCloudFrontCertStack extends Stack { + public cloudFrontCertArn: string; + constructor(scope: Construct, id: string, props: CloudFrontCertStackProps) { + super(scope, id, { + ...props, env: { + region: 'us-east-1', + account: process.env.CDK_DEFAULT_ACCOUNT || "364076391763", + }, + crossRegionReferences: true + }); + + const dnsZone = HostedZone.fromLookup(this, `publicDnsZone-${props.environment}`, { + domainName: "housingbayarea.mtc.ca.gov", + }); + + const cert = new Certificate(this, "CloudFrontCertificate", { + domainName: props.publicDomainName, + validation: CertificateValidation.fromDns(dnsZone), + subjectAlternativeNames: [props.partnersDomainName] + }); + + new CfnOutput(this, "CloudFrontCertArn", { + value: cert.certificateArn, + exportName: `doorway-cf-cert-arn-${props.environment}`, + description: `CloudFront certificate ARN for ${props.environment}`, + + }); + this.cloudFrontCertArn = cert.certificateArn; + } +} \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-props.ts b/cdk/lib/doorway_environment/doorway-props.ts index 443350286f1..9c77e33ff5f 100644 --- a/cdk/lib/doorway_environment/doorway-props.ts +++ b/cdk/lib/doorway_environment/doorway-props.ts @@ -1,4 +1,4 @@ -import { ISecurityGroup } from "aws-cdk-lib/aws-ec2" +import { ISecurityGroup, IVpc } from "aws-cdk-lib/aws-ec2" import { FargateService, Secret } from "aws-cdk-lib/aws-ecs" import { Role } from "aws-cdk-lib/aws-iam" import { LogGroup } from "aws-cdk-lib/aws-logs" @@ -12,6 +12,8 @@ export interface DoorwayProps { backendServiceName: string publicServiceName: string partnersServiceName: string + cfCertArn: string + vpc: IVpc } export interface DoorwayServiceProps extends DoorwayProps { @@ -32,6 +34,9 @@ export interface DoorwayServiceProps extends DoorwayProps { securityGroup: ISecurityGroup serviceName: string clusterName: string + apiNamespace: string + + } export interface DoorwayLoadBalancerProps extends DoorwayProps { diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index 0da02230198..1791cf8657d 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -50,12 +50,19 @@ export class DoorwayPublicLoadBalancer { domainName: "housingbayarea.mtc.ca.gov", }); const publicDomainName = process.env.PUBLIC_PORTAL_DOMAIN || `${props.environment}.housingbayarea.mtc.ca.gov` + const partnersDomainName = process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov` const cert = new Certificate(scope, "PublicCertificate", { domainName: publicDomainName, validation: CertificateValidation.fromDns(dnsZone), - subjectAlternativeNames: [process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov`] + subjectAlternativeNames: [partnersDomainName] }) + + + + const cfCert = Certificate.fromCertificateArn(scope, "CloudFrontCertificate", props.cfCertArn); + + const httpListener = this.loadBalancer.addListener("HttpListener", { port: 80, open: true, @@ -130,19 +137,19 @@ export class DoorwayPublicLoadBalancer { listener: httpsListener, priority: 200, action: ListenerAction.forward([this.partnersTargetGroup]), - conditions: [ListenerCondition.hostHeaders([process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov`]) + conditions: [ListenerCondition.hostHeaders([partnersDomainName]) ] }); new ARecord(scope, "PartnersARecord", { zone: dnsZone, - recordName: process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov`, + recordName: partnersDomainName, target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), }); const cloudfrontDist = new Distribution(scope, `CloudFront-${props.environment}`, { - domainNames: [publicDomainName, `*.${publicDomainName}`], - certificate: cert, + domainNames: [publicDomainName, partnersDomainName], + certificate: cfCert, minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021, enableIpv6: true, diff --git a/cdk/lib/doorway_environment/services/doorway-backend-service.ts b/cdk/lib/doorway_environment/services/doorway-backend-service.ts index 3b80a34fcd7..61b5f73b30e 100644 --- a/cdk/lib/doorway_environment/services/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/services/doorway-backend-service.ts @@ -4,6 +4,7 @@ import { FargateService, Secret } from "aws-cdk-lib/aws-ecs" import { PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" +import { PrivateDnsNamespace } from "aws-cdk-lib/aws-servicediscovery" import { EmailIdentity } from "aws-cdk-lib/aws-ses" import { Construct } from "constructs" @@ -144,6 +145,16 @@ export class DoorwayBackendService { }), "Allow internal traffic from web app servers") executionRole.addToPolicy(policy) + + const namespace = new PrivateDnsNamespace( + scope, + `doorway-${props.environment}-internal-api-namespace`, + { + vpc: props.vpc, + name: `doorway-${props.environment}-internal-api`, + description: `Private DNS namespace for the Doorway ${props.environment} internal API`, + }, + ) this.service = new DoorwayService(scope, `doorway-api-service-${props.environment}`, { ...props, memory: Number(process.env.BACKEND_MEMORY || 4096), @@ -161,8 +172,10 @@ export class DoorwayBackendService { container: `doorway/backend:run-${gitHash}`, securityGroup: privateSG, serviceName: props.backendServiceName, - clusterName: props.clusterName + clusterName: props.clusterName, + apiNamespace: namespace.namespaceName }).service + this.service.node.addDependency(namespace) } } diff --git a/cdk/lib/doorway_environment/services/doorway-partners-site.ts b/cdk/lib/doorway_environment/services/doorway-partners-site.ts index 70f4d2c31df..72da2e48f8a 100644 --- a/cdk/lib/doorway_environment/services/doorway-partners-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-partners-site.ts @@ -5,6 +5,7 @@ import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" + import { DoorwayProps } from "../doorway-props" import { DoorwayService } from "./doorway_service" @@ -86,7 +87,8 @@ export class DoorwayPartnersSite { container: `doorway/partners:run-${process.env.ENVIRONMENT || "dev2"}-${gitHash}`, securityGroup: privateSG, serviceName: props.partnersServiceName, - clusterName: props.clusterName + clusterName: props.clusterName, + apiNamespace: `doorway-${props.environment}-internal-api-namespace`, }).service } diff --git a/cdk/lib/doorway_environment/services/doorway-public-site.ts b/cdk/lib/doorway_environment/services/doorway-public-site.ts index cc07d5966ea..4a868043bbe 100644 --- a/cdk/lib/doorway_environment/services/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-public-site.ts @@ -95,7 +95,8 @@ export class DoorwayPublicSite { container: `doorway/public:run-${process.env.ENVIRONMENT || "dev2"}-${gitHash}`, securityGroup: privateSG, serviceName: props.publicServiceName, - clusterName: props.clusterName + clusterName: props.clusterName, + apiNamespace: `doorway-${props.environment}-internal-api-namespace`, }).service } diff --git a/cdk/lib/doorway_environment/services/doorway_service.ts b/cdk/lib/doorway_environment/services/doorway_service.ts index 1474785f622..8ad3fbd9001 100644 --- a/cdk/lib/doorway_environment/services/doorway_service.ts +++ b/cdk/lib/doorway_environment/services/doorway_service.ts @@ -4,7 +4,6 @@ import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2" import { AppProtocol, Cluster, Compatibility, ContainerImage, FargateService, LogDrivers, NetworkMode, Protocol, ServiceConnectProps, TaskDefinition } from "aws-cdk-lib/aws-ecs" //import * as ecs from "aws-cdk-lib/aws-ecs"; import { CompositePrincipal, ManagedPolicy, PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" -import { PrivateDnsNamespace } from "aws-cdk-lib/aws-servicediscovery" import { StringParameter } from "aws-cdk-lib/aws-ssm" import { Construct } from "constructs" @@ -31,13 +30,7 @@ export class DoorwayService { props.executionRole.addManagedPolicy( ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly"), ) - // Get Network setup - const vpcId = cdk.Fn.importValue(`doorway-vpc-id-${props.environment}`) - const azs = cdk.Fn.importValue(`doorway-azs-${props.environment}`).split(",") - const vpc = cdk.aws_ec2.Vpc.fromVpcAttributes(scope, `vpc-${id}`, { - vpcId: vpcId, - availabilityZones: azs, - }) + // Get the subnets const appSubnetIds: string[] = [] appSubnetIds.push(cdk.Fn.importValue(`doorway-app-subnet-1-${props.environment}`)) @@ -83,24 +76,10 @@ export class DoorwayService { //Setting up ServiceConnect which will allow the partenr and public services // to connect to the internal api service directly through ECS // without a load balancer. - let serviceConnectProps: ServiceConnectProps = { - namespace: `doorway-${props.environment}-internal-api`, - logDriver: LogDrivers.awsLogs({ - logGroup: props.serviceConnectLogGroup, - streamPrefix: `${id}-service-connect`, - }), - } + let serviceConnectProps: ServiceConnectProps = {} if (props.serviceConnectServer) { - const namespace = new PrivateDnsNamespace( - scope, - `doorway-${props.environment}-internal-api-namespace`, - { - vpc: vpc, - name: `doorway-${props.environment}-internal-api`, - description: `Private DNS namespace for the Doorway ${props.environment} internal API`, - }, - ) + // The private CA is used for TLS encryption of ServiceConnect traffic. const privateCAArn = StringParameter.fromStringParameterAttributes(scope, "privateCAArn", { parameterName: `/doorway/privateCertAuthority`, @@ -134,7 +113,7 @@ export class DoorwayService { logGroup: props.logGroup, streamPrefix: `${id}-service-connect`, }), - namespace: namespace.namespaceName, + namespace: props.apiNamespace, services: [{ tls: { @@ -148,6 +127,14 @@ export class DoorwayService { }], } + } else { + serviceConnectProps = { + namespace: `doorway-${props.environment}-internal-api`, + logDriver: LogDrivers.awsLogs({ + logGroup: props.serviceConnectLogGroup, + streamPrefix: `${id}-service-connect`, + }), + } } this.service = new FargateService(scope, `${id}-fargate-service`, { @@ -159,7 +146,7 @@ export class DoorwayService { }, cluster: Cluster.fromClusterAttributes(scope, `ecsCluster-${id}`, { clusterName: Fn.importValue(`doorway-ecs-cluster-${props.environment}`), - vpc: vpc, + vpc: props.vpc, }), vpcSubnets: { subnets: appsubnets, From 3671bf9cf6f8a33f0ac8095449ac87798f12dd96 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 5 Sep 2025 13:35:13 -0700 Subject: [PATCH 165/217] fix: pointing dns at cloudfront --- .../doorway-public-load-balancer.ts | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index 1791cf8657d..3135b7ebb8a 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -5,7 +5,7 @@ import { LoadBalancerV2Origin } from "aws-cdk-lib/aws-cloudfront-origins"; import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2"; import { ApplicationListener, ApplicationListenerRule, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup, ListenerAction, ListenerCondition, TargetType } from "aws-cdk-lib/aws-elasticloadbalancingv2"; import { ARecord, HostedZone, RecordTarget } from "aws-cdk-lib/aws-route53"; -import { LoadBalancerTarget } from "aws-cdk-lib/aws-route53-targets"; +import { CloudFrontTarget } from "aws-cdk-lib/aws-route53-targets"; import { StringParameter } from "aws-cdk-lib/aws-ssm"; import { Construct } from "constructs"; @@ -113,11 +113,7 @@ export class DoorwayPublicLoadBalancer { conditions: [ListenerCondition.hostHeaders([publicDomainName]) ] }); - new ARecord(scope, "PublicARecord", { - zone: dnsZone, - recordName: publicDomainName, - target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), - }); + this.partnersTargetGroup = new ApplicationTargetGroup(scope, "PartnersTargetGroup", { port: Number(process.env.PUBLIC_PORTAL_PORT) || 3000, protocol: ApplicationProtocol.HTTP, @@ -142,11 +138,7 @@ export class DoorwayPublicLoadBalancer { }); - new ARecord(scope, "PartnersARecord", { - zone: dnsZone, - recordName: partnersDomainName, - target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), - }); + const cloudfrontDist = new Distribution(scope, `CloudFront-${props.environment}`, { domainNames: [publicDomainName, partnersDomainName], certificate: cfCert, @@ -184,7 +176,16 @@ export class DoorwayPublicLoadBalancer { } } }) - + new ARecord(scope, "PartnersARecord", { + zone: dnsZone, + recordName: partnersDomainName, + target: RecordTarget.fromAlias(new CloudFrontTarget(cloudfrontDist)), + }); + new ARecord(scope, "PublicARecord", { + zone: dnsZone, + recordName: publicDomainName, + target: RecordTarget.fromAlias(new CloudFrontTarget(cloudfrontDist)), + }); } } \ No newline at end of file From 2c036744d975a625f7843aeb15bdc40db9d2a775 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 5 Sep 2025 15:40:25 -0700 Subject: [PATCH 166/217] fix: re-adding migrate --- cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 5fe152dd182..6cf3e7650ca 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -164,7 +164,7 @@ export class DoorwayBuildPipelineStack extends Stack { pipeline.addStage({ stageName: "Dev", - actions: [ecsDeploy], + actions: [dbmigrate, ecsDeploy], }) } } From b1624b3fb3f9f5c3b31fa5539cf8c9a96394ff10 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 5 Sep 2025 16:17:36 -0700 Subject: [PATCH 167/217] fix: removing secret field names --- cdk/lib/doit.ts | 3 +++ .../utility_lambdas/restart-services-lambda.ts | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 cdk/lib/doit.ts diff --git a/cdk/lib/doit.ts b/cdk/lib/doit.ts new file mode 100644 index 00000000000..8c499333aa5 --- /dev/null +++ b/cdk/lib/doit.ts @@ -0,0 +1,3 @@ +const secretarn = "arn:aws:secretsmanager:us-west-2:364076391763:secret:/doorway/dev2/CLOUDINARY_KEY" +const cleanArn = secretarn.replace(/(-[A-Za-z0-9]{6}):.*$/, '$1'); +console.log(cleanArn) \ No newline at end of file diff --git a/cdk/lib/doorway_environment/utility_lambdas/restart-services-lambda.ts b/cdk/lib/doorway_environment/utility_lambdas/restart-services-lambda.ts index 607b4337a32..c2ba4dbb932 100644 --- a/cdk/lib/doorway_environment/utility_lambdas/restart-services-lambda.ts +++ b/cdk/lib/doorway_environment/utility_lambdas/restart-services-lambda.ts @@ -23,10 +23,12 @@ export class RestartServicesLambda { let secretArns: string[] = []; for (const [key, secret] of Object.entries(props.secrets)) { - console.log("Processing secret key:", key); - console.log("Secret ARN:", secret.arn); - console.log("Secret object:", JSON.stringify(secret, null, 2)); - secretArns.push(secret.arn); + // Remove field names from secret ARN (everything after the 6-character suffix) + const baseArn = secret.arn.replace(/(-[A-Za-z0-9]{6}):.*$/, '$1'); + if (!secretArns.includes(baseArn)) { + secretArns.push(baseArn); + } + } console.log("All secret ARNs:", secretArns); From ed2b92902e048e8c8651462cd3f5c5c49295b74e Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 8 Sep 2025 09:13:39 -0700 Subject: [PATCH 168/217] fix: change namespace name to something more generic --- .../doorway_environment/services/doorway-backend-service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cdk/lib/doorway_environment/services/doorway-backend-service.ts b/cdk/lib/doorway_environment/services/doorway-backend-service.ts index 61b5f73b30e..c8406f540d5 100644 --- a/cdk/lib/doorway_environment/services/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/services/doorway-backend-service.ts @@ -148,11 +148,11 @@ export class DoorwayBackendService { const namespace = new PrivateDnsNamespace( scope, - `doorway-${props.environment}-internal-api-namespace`, + `doorway-${props.environment}-namespace`, { vpc: props.vpc, - name: `doorway-${props.environment}-internal-api`, - description: `Private DNS namespace for the Doorway ${props.environment} internal API`, + name: `doorway-${props.environment}`, + description: `Private DNS namespace for Doorway ${props.environment}`, }, ) this.service = new DoorwayService(scope, `doorway-api-service-${props.environment}`, { From 818e25348effb0fa08571c375954a9b7cd2da91b Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 8 Sep 2025 09:53:44 -0700 Subject: [PATCH 169/217] fix: fixing log group and cloudfront origin --- .../doorway_environment/doorway-public-load-balancer.ts | 9 +++++---- cdk/lib/doorway_environment/services/doorway_service.ts | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index 3135b7ebb8a..56693f264f4 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -138,6 +138,7 @@ export class DoorwayPublicLoadBalancer { }); + const cfOrigin = new LoadBalancerV2Origin(this.loadBalancer) const cloudfrontDist = new Distribution(scope, `CloudFront-${props.environment}`, { domainNames: [publicDomainName, partnersDomainName], @@ -150,7 +151,7 @@ export class DoorwayPublicLoadBalancer { logIncludesCookies: true, logFilePrefix: "cloudfront", - comment: `Doorway for ${props.environment}`, + comment: `Doorway Cloudfront for ${props.environment}`, priceClass: PriceClass.PRICE_CLASS_100, geoRestriction: { @@ -160,15 +161,15 @@ export class DoorwayPublicLoadBalancer { defaultBehavior: { - origin: new LoadBalancerV2Origin(this.loadBalancer), + origin: cfOrigin, cachePolicy: CachePolicy.CACHING_DISABLED, // Disable caching viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS, allowedMethods: AllowedMethods.ALLOW_ALL, - originRequestPolicy: OriginRequestPolicy.ALL_VIEWER + }, additionalBehaviors: { "/images/*": { - origin: new LoadBalancerV2Origin(this.loadBalancer), + origin: cfOrigin, allowedMethods: AllowedMethods.ALLOW_ALL, cachePolicy: CachePolicy.CACHING_OPTIMIZED, viewerProtocolPolicy: ViewerProtocolPolicy.ALLOW_ALL, diff --git a/cdk/lib/doorway_environment/services/doorway_service.ts b/cdk/lib/doorway_environment/services/doorway_service.ts index 8ad3fbd9001..272162779f3 100644 --- a/cdk/lib/doorway_environment/services/doorway_service.ts +++ b/cdk/lib/doorway_environment/services/doorway_service.ts @@ -110,7 +110,7 @@ export class DoorwayService { props.logGroup.grantWrite(scRole) serviceConnectProps = { logDriver: LogDrivers.awsLogs({ - logGroup: props.logGroup, + logGroup: props.serviceConnectLogGroup, streamPrefix: `${id}-service-connect`, }), namespace: props.apiNamespace, From 0f5aa018a1ba14df333f54779cda68439057ef99 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 8 Sep 2025 10:30:24 -0700 Subject: [PATCH 170/217] fix: changing origin request policy --- cdk/lib/doorway_environment/doorway-public-load-balancer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index 56693f264f4..4d342137542 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -165,6 +165,7 @@ export class DoorwayPublicLoadBalancer { cachePolicy: CachePolicy.CACHING_DISABLED, // Disable caching viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS, allowedMethods: AllowedMethods.ALLOW_ALL, + originRequestPolicy: OriginRequestPolicy.ALL_VIEWER }, additionalBehaviors: { @@ -173,7 +174,7 @@ export class DoorwayPublicLoadBalancer { allowedMethods: AllowedMethods.ALLOW_ALL, cachePolicy: CachePolicy.CACHING_OPTIMIZED, viewerProtocolPolicy: ViewerProtocolPolicy.ALLOW_ALL, - originRequestPolicy: OriginRequestPolicy.ALL_VIEWER + } } }) From ab935219dd6646ef379720c730041d32d36e15b8 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 8 Sep 2025 10:58:30 -0700 Subject: [PATCH 171/217] fix: fixing namespace --- cdk/lib/doorway_environment/services/doorway_service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/services/doorway_service.ts b/cdk/lib/doorway_environment/services/doorway_service.ts index 272162779f3..1a215c3fd41 100644 --- a/cdk/lib/doorway_environment/services/doorway_service.ts +++ b/cdk/lib/doorway_environment/services/doorway_service.ts @@ -129,7 +129,7 @@ export class DoorwayService { } } else { serviceConnectProps = { - namespace: `doorway-${props.environment}-internal-api`, + namespace: `doorway-${props.environment}`, logDriver: LogDrivers.awsLogs({ logGroup: props.serviceConnectLogGroup, streamPrefix: `${id}-service-connect`, From 687a0faa75a195ecd50534081ab75acfd36ae3cf Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 8 Sep 2025 14:35:37 -0700 Subject: [PATCH 172/217] fix: cache policy --- .../doorway-public-load-balancer.ts | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts index 4d342137542..7df96e6d1e7 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-public-load-balancer.ts @@ -139,6 +139,24 @@ export class DoorwayPublicLoadBalancer { }); const cfOrigin = new LoadBalancerV2Origin(this.loadBalancer) + const cachePolicy = new CachePolicy(scope, `CachePolicy-images-${props.environment}`, { + cachePolicyName: `CachePolicy-images-${props.environment}`, + enableAcceptEncodingGzip: false, + enableAcceptEncodingBrotli: false, + minTtl: Duration.seconds(60), + maxTtl: Duration.seconds(31536000), + defaultTtl: Duration.seconds(86400), + headerBehavior: { + behavior: "whitelist", + headers: ["Host"], + }, + queryStringBehavior: { + behavior: "none", + }, + cookieBehavior: { + behavior: "none", + }, + }); const cloudfrontDist = new Distribution(scope, `CloudFront-${props.environment}`, { domainNames: [publicDomainName, partnersDomainName], @@ -172,7 +190,7 @@ export class DoorwayPublicLoadBalancer { "/images/*": { origin: cfOrigin, allowedMethods: AllowedMethods.ALLOW_ALL, - cachePolicy: CachePolicy.CACHING_OPTIMIZED, + cachePolicy: cachePolicy, viewerProtocolPolicy: ViewerProtocolPolicy.ALLOW_ALL, } From 76b7a35735d626547946931c781d71392b2ab237 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 8 Sep 2025 14:56:31 -0700 Subject: [PATCH 173/217] fix: autoscaling --- .../services/doorway-backend-service.ts | 1 + cdk/lib/doorway_environment/services/doorway_service.ts | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/cdk/lib/doorway_environment/services/doorway-backend-service.ts b/cdk/lib/doorway_environment/services/doorway-backend-service.ts index c8406f540d5..2e8fe5b9153 100644 --- a/cdk/lib/doorway_environment/services/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/services/doorway-backend-service.ts @@ -177,5 +177,6 @@ export class DoorwayBackendService { }).service this.service.node.addDependency(namespace) + } } diff --git a/cdk/lib/doorway_environment/services/doorway_service.ts b/cdk/lib/doorway_environment/services/doorway_service.ts index 1a215c3fd41..f45301098bc 100644 --- a/cdk/lib/doorway_environment/services/doorway_service.ts +++ b/cdk/lib/doorway_environment/services/doorway_service.ts @@ -138,6 +138,7 @@ export class DoorwayService { } this.service = new FargateService(scope, `${id}-fargate-service`, { + taskDefinition: task, serviceName: props.serviceName, circuitBreaker: { @@ -164,6 +165,14 @@ export class DoorwayService { }) } props.logGroup.grantWrite(props.executionRole) + this.service.autoScaleTaskCount({ + minCapacity: 3, + maxCapacity: 10, + + }).scaleOnCpuUtilization("CpuScaling", { + targetUtilizationPercent: 50, + }) + } From 54231f937ab1d2495c41edd1c4842ab66bfcb409 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 9 Sep 2025 12:33:20 -0700 Subject: [PATCH 174/217] feat: private lb --- cdk/cdk.context.json | 4 ++ cdk/dev2.env | 1 + .../doorway-app-environment-stack.ts | 7 +- ...-balancer.ts => doorway-load-balancers.ts} | 68 ++++++++++++++++++- 4 files changed, 74 insertions(+), 6 deletions(-) rename cdk/lib/doorway_environment/{doorway-public-load-balancer.ts => doorway-load-balancers.ts} (74%) diff --git a/cdk/cdk.context.json b/cdk/cdk.context.json index 6e3bed29a77..2a2ba61df13 100644 --- a/cdk/cdk.context.json +++ b/cdk/cdk.context.json @@ -6,5 +6,9 @@ "hosted-zone:account=364076391763:domainName=housingbayarea.mtc.ca.gov:region=us-west-2": { "Id": "/hostedzone/Z01682742VM0KIXZ4Y3W5", "Name": "housingbayarea.mtc.ca.gov." + }, + "hosted-zone:account=364076391763:domainName=housingbayarea.mtc.ca.gov:region=us-west-1": { + "Id": "/hostedzone/Z01682742VM0KIXZ4Y3W5", + "Name": "housingbayarea.mtc.ca.gov." } } diff --git a/cdk/dev2.env b/cdk/dev2.env index e075838ba57..93f5841001b 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -26,6 +26,7 @@ HTTPS_OFF=true PARTNERS_PORTAL_PORT=3200 PARTNERS_PORTAL_DOMAIN=partners.dev2.housingbayarea.mtc.ca.gov +BACKEND_API_DOMAIN=backend.dev2.housingbayarea.int BACKEND_API_BASE=http://backend.dev2.housingbayarea.int:3100 BACKEND_API_PORT=3100 BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVDELIVERY_API_URL diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index f6138f6ac42..7acc94a8ca3 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -5,8 +5,8 @@ import { Construct } from "constructs" import dotenv from "dotenv" import path from "path" +import { DoorwayLoadBalancers } from "./doorway-load-balancers" import { DoorwayProps } from "./doorway-props" -import { DoorwayPublicLoadBalancer } from "./doorway-public-load-balancer" import { DoorwayBackendService } from "./services/doorway-backend-service" import { DoorwayPartnersSite } from "./services/doorway-partners-site" import { DoorwayPublicSite } from "./services/doorway-public-site" @@ -66,7 +66,7 @@ export class DoorwayAppEnvironmentStack extends Stack { const api = new DoorwayBackendService(this, `doorway-api-service-${environment}`, dwProps) api.service.node.addDependency(s3stack) - const lb = new DoorwayPublicLoadBalancer(this, `doorway-public-lb-${environment}`, dwProps); + const lb = new DoorwayLoadBalancers(this, `doorway-lbs-${environment}`, dwProps); lb.loadBalancer.node.addDependency(s3stack) const publicSite = new DoorwayPublicSite(this, `doorway-public-${environment}`, dwProps) @@ -78,8 +78,7 @@ export class DoorwayAppEnvironmentStack extends Stack { partnersSite.service.node.addDependency(lb.loadBalancer) partnersSite.service.node.addDependency(api.service) lb.partnersTargetGroup.addTarget(partnersSite.service) - - + lb.privateTargetGroup.addTarget(api.service) } } diff --git a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts b/cdk/lib/doorway_environment/doorway-load-balancers.ts similarity index 74% rename from cdk/lib/doorway_environment/doorway-public-load-balancer.ts rename to cdk/lib/doorway_environment/doorway-load-balancers.ts index 7df96e6d1e7..0604f9bc179 100644 --- a/cdk/lib/doorway_environment/doorway-public-load-balancer.ts +++ b/cdk/lib/doorway_environment/doorway-load-balancers.ts @@ -5,16 +5,18 @@ import { LoadBalancerV2Origin } from "aws-cdk-lib/aws-cloudfront-origins"; import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2"; import { ApplicationListener, ApplicationListenerRule, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup, ListenerAction, ListenerCondition, TargetType } from "aws-cdk-lib/aws-elasticloadbalancingv2"; import { ARecord, HostedZone, RecordTarget } from "aws-cdk-lib/aws-route53"; -import { CloudFrontTarget } from "aws-cdk-lib/aws-route53-targets"; +import { CloudFrontTarget, LoadBalancerTarget } from "aws-cdk-lib/aws-route53-targets"; import { StringParameter } from "aws-cdk-lib/aws-ssm"; import { Construct } from "constructs"; import { DoorwayProps } from "./doorway-props"; -export class DoorwayPublicLoadBalancer { +export class DoorwayLoadBalancers { public loadBalancer: ApplicationLoadBalancer + public privateLoadBalancer: ApplicationLoadBalancer public publicTargetGroup: ApplicationTargetGroup; public partnersTargetGroup: ApplicationTargetGroup; + public privateTargetGroup: ApplicationTargetGroup; constructor(scope: Construct, id: string, props: DoorwayProps) { // Get Network setup @@ -51,6 +53,7 @@ export class DoorwayPublicLoadBalancer { }); const publicDomainName = process.env.PUBLIC_PORTAL_DOMAIN || `${props.environment}.housingbayarea.mtc.ca.gov` const partnersDomainName = process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov` + const backendDomainName = process.env.BACKEND_API_DOMAIN || `backend.${props.environment}.housingbayarea.int` const cert = new Certificate(scope, "PublicCertificate", { domainName: publicDomainName, validation: CertificateValidation.fromDns(dnsZone), @@ -207,5 +210,66 @@ export class DoorwayPublicLoadBalancer { target: RecordTarget.fromAlias(new CloudFrontTarget(cloudfrontDist)), }); + const appSGId = Fn.importValue(`doorway-app-sg-${props.environment}`) + + const appSg = aws_ec2.SecurityGroup.fromSecurityGroupId(scope, `doorway-app-sg-${props.environment}`, appSGId) + const appSubnetIds: string[] = [] + appSubnetIds.push(Fn.importValue(`doorway-public-subnet-1-${props.environment}`)) + appSubnetIds.push(Fn.importValue(`doorway-public-subnet-2-${props.environment}`)) + const appsubnets: ISubnet[] = [] + appSubnetIds.forEach((id) => { + appsubnets.push(Subnet.fromSubnetId(scope, id, id)) + }) + + this.privateLoadBalancer = new ApplicationLoadBalancer(scope, `private-${id}`, { + vpc: vpc, + internetFacing: false, + loadBalancerName: `private-${id}`, + securityGroup: appSg, + vpcSubnets: { + subnets: appsubnets, + }, + + }); + const httpListenerPrivate = this.privateLoadBalancer.addListener("PrivateListener", { + + port: 80, + open: true, + protocol: ApplicationProtocol.HTTP, + + }) + this.privateTargetGroup = new ApplicationTargetGroup(scope, "PrivateTargetGroup", { + port: Number(process.env.BACKEND_API_PORT) || 3100, + protocol: ApplicationProtocol.HTTP, + vpc: vpc, + targetGroupName: `private-tg-${props.environment}`, + targetType: TargetType.IP, + healthCheck: { + enabled: true, + healthyThresholdCount: 2, + interval: Duration.seconds(10), + path: "/", + timeout: Duration.seconds(5), + unhealthyThresholdCount: 2, + }, + }) + httpListenerPrivate.addAction("DefaultAction", { + action: ListenerAction.forward([this.privateTargetGroup]), + }) + const privateDnsZoneName = StringParameter.fromStringParameterName(scope, `privateDnsZoneId-${props.environment}`, `/doorway/private-hosted-zone`).stringValue; + + + const privateDnsZone = HostedZone.fromHostedZoneAttributes(scope, `privateDnsZone-${props.environment}`, { + hostedZoneId: privateDnsZoneName, + zoneName: "housingbayarea.int", + }); + new ARecord(scope, "PrivateARecord", { + zone: privateDnsZone, + recordName: backendDomainName, + target: RecordTarget.fromAlias(new LoadBalancerTarget(this.privateLoadBalancer)), + }); + + + } } \ No newline at end of file From 290a2cdffb10a2b20cb0f55821735ad634062858 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 9 Sep 2025 13:22:53 -0700 Subject: [PATCH 175/217] removing serviceconnect --- .../doorway-build-pipeline-stack.ts | 2 +- .../doorway-load-balancers.ts | 8 +- .../services/doorway_service.ts | 131 +++++++++--------- 3 files changed, 66 insertions(+), 75 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 6cf3e7650ca..5fe152dd182 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -164,7 +164,7 @@ export class DoorwayBuildPipelineStack extends Stack { pipeline.addStage({ stageName: "Dev", - actions: [dbmigrate, ecsDeploy], + actions: [ecsDeploy], }) } } diff --git a/cdk/lib/doorway_environment/doorway-load-balancers.ts b/cdk/lib/doorway_environment/doorway-load-balancers.ts index 0604f9bc179..3b6416a957a 100644 --- a/cdk/lib/doorway_environment/doorway-load-balancers.ts +++ b/cdk/lib/doorway_environment/doorway-load-balancers.ts @@ -42,7 +42,6 @@ export class DoorwayLoadBalancers { loadBalancerName: id, securityGroup: sg, vpcSubnets: { - subnets: publicsubnets, }, @@ -60,12 +59,8 @@ export class DoorwayLoadBalancers { subjectAlternativeNames: [partnersDomainName] }) - - - const cfCert = Certificate.fromCertificateArn(scope, "CloudFrontCertificate", props.cfCertArn); - const httpListener = this.loadBalancer.addListener("HttpListener", { port: 80, open: true, @@ -138,8 +133,6 @@ export class DoorwayLoadBalancers { action: ListenerAction.forward([this.partnersTargetGroup]), conditions: [ListenerCondition.hostHeaders([partnersDomainName]) ] - - }); const cfOrigin = new LoadBalancerV2Origin(this.loadBalancer) const cachePolicy = new CachePolicy(scope, `CachePolicy-images-${props.environment}`, { @@ -242,6 +235,7 @@ export class DoorwayLoadBalancers { port: Number(process.env.BACKEND_API_PORT) || 3100, protocol: ApplicationProtocol.HTTP, vpc: vpc, + targetGroupName: `private-tg-${props.environment}`, targetType: TargetType.IP, healthCheck: { diff --git a/cdk/lib/doorway_environment/services/doorway_service.ts b/cdk/lib/doorway_environment/services/doorway_service.ts index f45301098bc..00830eec88f 100644 --- a/cdk/lib/doorway_environment/services/doorway_service.ts +++ b/cdk/lib/doorway_environment/services/doorway_service.ts @@ -1,10 +1,9 @@ import * as cdk from "aws-cdk-lib" import { Fn } from "aws-cdk-lib" import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2" -import { AppProtocol, Cluster, Compatibility, ContainerImage, FargateService, LogDrivers, NetworkMode, Protocol, ServiceConnectProps, TaskDefinition } from "aws-cdk-lib/aws-ecs" +import { AppProtocol, Cluster, Compatibility, ContainerImage, FargateService, LogDrivers, NetworkMode, Protocol, TaskDefinition } from "aws-cdk-lib/aws-ecs" //import * as ecs from "aws-cdk-lib/aws-ecs"; -import { CompositePrincipal, ManagedPolicy, PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" -import { StringParameter } from "aws-cdk-lib/aws-ssm" +import { ManagedPolicy } from "aws-cdk-lib/aws-iam" import { Construct } from "constructs" import { DoorwayServiceProps } from "../doorway-props" @@ -73,69 +72,67 @@ export class DoorwayService { }, ], }) - //Setting up ServiceConnect which will allow the partenr and public services - // to connect to the internal api service directly through ECS - // without a load balancer. - let serviceConnectProps: ServiceConnectProps = {} - if (props.serviceConnectServer) { - - - // The private CA is used for TLS encryption of ServiceConnect traffic. - const privateCAArn = StringParameter.fromStringParameterAttributes(scope, "privateCAArn", { - parameterName: `/doorway/privateCertAuthority`, - }).stringValue - const scRole = new Role(scope, `doorway-${props.environment}-internal-api-sc-role`, { - assumedBy: new CompositePrincipal( - new ServicePrincipal("ecs.amazonaws.com"), - new ServicePrincipal("ecs-tasks.amazonaws.com"), - new ServicePrincipal("ecs.application-autoscaling.amazonaws.com") - ), - managedPolicies: [ - ManagedPolicy.fromAwsManagedPolicyName( - "service-role/AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity", - ), - ], - inlinePolicies: { - pcaAuth: new PolicyDocument({ - statements: [ - new PolicyStatement({ - actions: ["acm-pca:*"], - resources: [privateCAArn], - }), - ], - }), - }, - description: "Service Connect TLS role for ECS service communication", - }) - props.logGroup.grantWrite(scRole) - serviceConnectProps = { - logDriver: LogDrivers.awsLogs({ - logGroup: props.serviceConnectLogGroup, - streamPrefix: `${id}-service-connect`, - }), - namespace: props.apiNamespace, - - services: [{ - tls: { - role: scRole, - awsPcaAuthorityArn: privateCAArn, - }, - portMappingName: `${id}-port-mapping`, - dnsName: props.domainName, - discoveryName: props.serviceName, - port: props.port, - - }], - } - } else { - serviceConnectProps = { - namespace: `doorway-${props.environment}`, - logDriver: LogDrivers.awsLogs({ - logGroup: props.serviceConnectLogGroup, - streamPrefix: `${id}-service-connect`, - }), - } - } + // //Setting up ServiceConnect which will allow the partenr and public services + // // to connect to the internal api service directly through ECS + // // without a load balancer. + // let serviceConnectProps: ServiceConnectProps = {} + // if (props.serviceConnectServer) { + + + + + // const scRole = new Role(scope, `doorway-${props.environment}-internal-api-sc-role`, { + // assumedBy: new CompositePrincipal( + // new ServicePrincipal("ecs.amazonaws.com"), + // new ServicePrincipal("ecs-tasks.amazonaws.com"), + // new ServicePrincipal("ecs.application-autoscaling.amazonaws.com") + // ), + // managedPolicies: [ + // ManagedPolicy.fromAwsManagedPolicyName( + // "service-role/AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity", + // ), + // ], + // inlinePolicies: { + // pcaAuth: new PolicyDocument({ + // statements: [ + // new PolicyStatement({ + // actions: ["acm-pca:*"], + // resources: [privateCAArn], + // }), + // ], + // }), + // }, + // description: "Service Connect TLS role for ECS service communication", + // }) + // props.logGroup.grantWrite(scRole) + // serviceConnectProps = { + // logDriver: LogDrivers.awsLogs({ + // logGroup: props.serviceConnectLogGroup, + // streamPrefix: `${id}-service-connect`, + // }), + // namespace: props.apiNamespace, + + // services: [{ + // tls: { + // role: scRole, + // awsPcaAuthorityArn: privateCAArn, + // }, + // portMappingName: `${id}-port-mapping`, + // dnsName: props.domainName, + // discoveryName: props.serviceName, + // port: props.port, + + // }], + // } + // } else { + // serviceConnectProps = { + // namespace: `doorway-${props.environment}`, + // logDriver: LogDrivers.awsLogs({ + // logGroup: props.serviceConnectLogGroup, + // streamPrefix: `${id}-service-connect`, + // }), + // } + // } this.service = new FargateService(scope, `${id}-fargate-service`, { @@ -154,7 +151,7 @@ export class DoorwayService { }, securityGroups: [props.securityGroup], desiredCount: props.instances, - serviceConnectConfiguration: serviceConnectProps, + }) if (Object.keys(props.secrets).length > 0) { From 55547f746c0ccbf0e316819890aeb2a5cc8d7501 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 9 Sep 2025 14:42:15 -0700 Subject: [PATCH 176/217] moving lb to app subnets --- cdk/dev2.env | 2 +- .../doorway-load-balancers.ts | 3 - cdk/lib/doorway_environment/doorway-props.ts | 2 +- .../services/doorway-backend-service.ts | 15 +---- .../services/doorway_service.ts | 62 +------------------ 5 files changed, 6 insertions(+), 78 deletions(-) diff --git a/cdk/dev2.env b/cdk/dev2.env index 93f5841001b..5d2a6e78b27 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -27,7 +27,7 @@ HTTPS_OFF=true PARTNERS_PORTAL_PORT=3200 PARTNERS_PORTAL_DOMAIN=partners.dev2.housingbayarea.mtc.ca.gov BACKEND_API_DOMAIN=backend.dev2.housingbayarea.int -BACKEND_API_BASE=http://backend.dev2.housingbayarea.int:3100 +BACKEND_API_BASE=http://backend.dev2.housingbayarea.int BACKEND_API_PORT=3100 BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVDELIVERY_API_URL BACKEND_API_MEMORY=4096 diff --git a/cdk/lib/doorway_environment/doorway-load-balancers.ts b/cdk/lib/doorway_environment/doorway-load-balancers.ts index 3b6416a957a..9119bd7d1b2 100644 --- a/cdk/lib/doorway_environment/doorway-load-balancers.ts +++ b/cdk/lib/doorway_environment/doorway-load-balancers.ts @@ -92,9 +92,6 @@ export class DoorwayLoadBalancers { vpc: vpc, targetGroupName: `public-tg-${props.environment}`, targetType: TargetType.IP, - - - healthCheck: { enabled: true, healthyThresholdCount: 2, diff --git a/cdk/lib/doorway_environment/doorway-props.ts b/cdk/lib/doorway_environment/doorway-props.ts index 9c77e33ff5f..78674583cf0 100644 --- a/cdk/lib/doorway_environment/doorway-props.ts +++ b/cdk/lib/doorway_environment/doorway-props.ts @@ -34,7 +34,7 @@ export interface DoorwayServiceProps extends DoorwayProps { securityGroup: ISecurityGroup serviceName: string clusterName: string - apiNamespace: string + diff --git a/cdk/lib/doorway_environment/services/doorway-backend-service.ts b/cdk/lib/doorway_environment/services/doorway-backend-service.ts index 2e8fe5b9153..55a665d5c4c 100644 --- a/cdk/lib/doorway_environment/services/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/services/doorway-backend-service.ts @@ -4,7 +4,6 @@ import { FargateService, Secret } from "aws-cdk-lib/aws-ecs" import { PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Bucket } from "aws-cdk-lib/aws-s3" import * as secret from "aws-cdk-lib/aws-secretsmanager" -import { PrivateDnsNamespace } from "aws-cdk-lib/aws-servicediscovery" import { EmailIdentity } from "aws-cdk-lib/aws-ses" import { Construct } from "constructs" @@ -146,15 +145,7 @@ export class DoorwayBackendService { }), "Allow internal traffic from web app servers") executionRole.addToPolicy(policy) - const namespace = new PrivateDnsNamespace( - scope, - `doorway-${props.environment}-namespace`, - { - vpc: props.vpc, - name: `doorway-${props.environment}`, - description: `Private DNS namespace for Doorway ${props.environment}`, - }, - ) + this.service = new DoorwayService(scope, `doorway-api-service-${props.environment}`, { ...props, memory: Number(process.env.BACKEND_MEMORY || 4096), @@ -173,10 +164,10 @@ export class DoorwayBackendService { securityGroup: privateSG, serviceName: props.backendServiceName, clusterName: props.clusterName, - apiNamespace: namespace.namespaceName + }).service - this.service.node.addDependency(namespace) + } } diff --git a/cdk/lib/doorway_environment/services/doorway_service.ts b/cdk/lib/doorway_environment/services/doorway_service.ts index 00830eec88f..06ba1bb5282 100644 --- a/cdk/lib/doorway_environment/services/doorway_service.ts +++ b/cdk/lib/doorway_environment/services/doorway_service.ts @@ -72,67 +72,7 @@ export class DoorwayService { }, ], }) - // //Setting up ServiceConnect which will allow the partenr and public services - // // to connect to the internal api service directly through ECS - // // without a load balancer. - // let serviceConnectProps: ServiceConnectProps = {} - // if (props.serviceConnectServer) { - - - - - // const scRole = new Role(scope, `doorway-${props.environment}-internal-api-sc-role`, { - // assumedBy: new CompositePrincipal( - // new ServicePrincipal("ecs.amazonaws.com"), - // new ServicePrincipal("ecs-tasks.amazonaws.com"), - // new ServicePrincipal("ecs.application-autoscaling.amazonaws.com") - // ), - // managedPolicies: [ - // ManagedPolicy.fromAwsManagedPolicyName( - // "service-role/AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity", - // ), - // ], - // inlinePolicies: { - // pcaAuth: new PolicyDocument({ - // statements: [ - // new PolicyStatement({ - // actions: ["acm-pca:*"], - // resources: [privateCAArn], - // }), - // ], - // }), - // }, - // description: "Service Connect TLS role for ECS service communication", - // }) - // props.logGroup.grantWrite(scRole) - // serviceConnectProps = { - // logDriver: LogDrivers.awsLogs({ - // logGroup: props.serviceConnectLogGroup, - // streamPrefix: `${id}-service-connect`, - // }), - // namespace: props.apiNamespace, - - // services: [{ - // tls: { - // role: scRole, - // awsPcaAuthorityArn: privateCAArn, - // }, - // portMappingName: `${id}-port-mapping`, - // dnsName: props.domainName, - // discoveryName: props.serviceName, - // port: props.port, - - // }], - // } - // } else { - // serviceConnectProps = { - // namespace: `doorway-${props.environment}`, - // logDriver: LogDrivers.awsLogs({ - // logGroup: props.serviceConnectLogGroup, - // streamPrefix: `${id}-service-connect`, - // }), - // } - // } + this.service = new FargateService(scope, `${id}-fargate-service`, { From c8a4e25ad762ae8fbc4157e60774a9aa13e340af Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 9 Sep 2025 15:26:26 -0700 Subject: [PATCH 177/217] fix: cdk issues --- cdk/lib/doorway_environment/doorway-load-balancers.ts | 4 ++-- cdk/lib/doorway_environment/services/doorway-partners-site.ts | 1 - cdk/lib/doorway_environment/services/doorway-public-site.ts | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cdk/lib/doorway_environment/doorway-load-balancers.ts b/cdk/lib/doorway_environment/doorway-load-balancers.ts index 9119bd7d1b2..c57d3efef3b 100644 --- a/cdk/lib/doorway_environment/doorway-load-balancers.ts +++ b/cdk/lib/doorway_environment/doorway-load-balancers.ts @@ -204,8 +204,8 @@ export class DoorwayLoadBalancers { const appSg = aws_ec2.SecurityGroup.fromSecurityGroupId(scope, `doorway-app-sg-${props.environment}`, appSGId) const appSubnetIds: string[] = [] - appSubnetIds.push(Fn.importValue(`doorway-public-subnet-1-${props.environment}`)) - appSubnetIds.push(Fn.importValue(`doorway-public-subnet-2-${props.environment}`)) + appSubnetIds.push(Fn.importValue(`doorway-app-subnet-1-${props.environment}`)) + appSubnetIds.push(Fn.importValue(`doorway-app-subnet-2-${props.environment}`)) const appsubnets: ISubnet[] = [] appSubnetIds.forEach((id) => { appsubnets.push(Subnet.fromSubnetId(scope, id, id)) diff --git a/cdk/lib/doorway_environment/services/doorway-partners-site.ts b/cdk/lib/doorway_environment/services/doorway-partners-site.ts index 72da2e48f8a..0b782052e48 100644 --- a/cdk/lib/doorway_environment/services/doorway-partners-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-partners-site.ts @@ -88,7 +88,6 @@ export class DoorwayPartnersSite { securityGroup: privateSG, serviceName: props.partnersServiceName, clusterName: props.clusterName, - apiNamespace: `doorway-${props.environment}-internal-api-namespace`, }).service } diff --git a/cdk/lib/doorway_environment/services/doorway-public-site.ts b/cdk/lib/doorway_environment/services/doorway-public-site.ts index 4a868043bbe..654acc7550f 100644 --- a/cdk/lib/doorway_environment/services/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-public-site.ts @@ -96,7 +96,6 @@ export class DoorwayPublicSite { securityGroup: privateSG, serviceName: props.publicServiceName, clusterName: props.clusterName, - apiNamespace: `doorway-${props.environment}-internal-api-namespace`, }).service } From 0e14a64a44ca7b581d936d653d95dbda630d963e Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 9 Sep 2025 15:41:06 -0700 Subject: [PATCH 178/217] feat: going to https --- cdk/dev2.env | 2 +- .../doorway-load-balancers.ts | 21 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/cdk/dev2.env b/cdk/dev2.env index 5d2a6e78b27..662cd81dc96 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -27,7 +27,7 @@ HTTPS_OFF=true PARTNERS_PORTAL_PORT=3200 PARTNERS_PORTAL_DOMAIN=partners.dev2.housingbayarea.mtc.ca.gov BACKEND_API_DOMAIN=backend.dev2.housingbayarea.int -BACKEND_API_BASE=http://backend.dev2.housingbayarea.int +BACKEND_API_BASE=https://backend.dev2.housingbayarea.int BACKEND_API_PORT=3100 BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVDELIVERY_API_URL BACKEND_API_MEMORY=4096 diff --git a/cdk/lib/doorway_environment/doorway-load-balancers.ts b/cdk/lib/doorway_environment/doorway-load-balancers.ts index c57d3efef3b..53cef00aa72 100644 --- a/cdk/lib/doorway_environment/doorway-load-balancers.ts +++ b/cdk/lib/doorway_environment/doorway-load-balancers.ts @@ -1,5 +1,6 @@ import { aws_ec2, Duration, Fn } from "aws-cdk-lib"; -import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; +import { CertificateAuthority } from "aws-cdk-lib/aws-acmpca"; +import { Certificate, CertificateValidation, PrivateCertificate } from "aws-cdk-lib/aws-certificatemanager"; import { AllowedMethods, CachePolicy, Distribution, OriginRequestPolicy, PriceClass, SecurityPolicyProtocol, ViewerProtocolPolicy } from "aws-cdk-lib/aws-cloudfront"; import { LoadBalancerV2Origin } from "aws-cdk-lib/aws-cloudfront-origins"; import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2"; @@ -210,7 +211,14 @@ export class DoorwayLoadBalancers { appSubnetIds.forEach((id) => { appsubnets.push(Subnet.fromSubnetId(scope, id, id)) }) - + const privateCAArn = StringParameter.fromStringParameterAttributes(scope, "privateCAArn", { + parameterName: `/doorway/privateCertAuthority`, + }).stringValue + const privateCA = CertificateAuthority.fromCertificateAuthorityArn(scope, "privateCA", privateCAArn); + const privateCert = new PrivateCertificate(scope, "PrivateCertificate", { + domainName: backendDomainName, + certificateAuthority: privateCA, + }) this.privateLoadBalancer = new ApplicationLoadBalancer(scope, `private-${id}`, { vpc: vpc, internetFacing: false, @@ -221,11 +229,12 @@ export class DoorwayLoadBalancers { }, }); - const httpListenerPrivate = this.privateLoadBalancer.addListener("PrivateListener", { + const httpsListenerPrivate = this.privateLoadBalancer.addListener("PrivateListener", { - port: 80, + port: 443, open: true, - protocol: ApplicationProtocol.HTTP, + protocol: ApplicationProtocol.HTTPS, + certificates: [privateCert] }) this.privateTargetGroup = new ApplicationTargetGroup(scope, "PrivateTargetGroup", { @@ -244,7 +253,7 @@ export class DoorwayLoadBalancers { unhealthyThresholdCount: 2, }, }) - httpListenerPrivate.addAction("DefaultAction", { + httpsListenerPrivate.addAction("DefaultAction", { action: ListenerAction.forward([this.privateTargetGroup]), }) const privateDnsZoneName = StringParameter.fromStringParameterName(scope, `privateDnsZoneId-${props.environment}`, `/doorway/private-hosted-zone`).stringValue; From ef47682400423185d6e57cf633e4e0664a49d669 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 9 Sep 2025 15:58:05 -0700 Subject: [PATCH 179/217] fix: sigh....can't use https --- cdk/dev2.env | 2 +- .../doorway-load-balancers.ts | 23 +++++-------------- cdk/lib/doorway_environment/doorway-props.ts | 4 ---- .../services/doorway_service.ts | 5 ++-- 4 files changed, 9 insertions(+), 25 deletions(-) diff --git a/cdk/dev2.env b/cdk/dev2.env index 662cd81dc96..5d2a6e78b27 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -27,7 +27,7 @@ HTTPS_OFF=true PARTNERS_PORTAL_PORT=3200 PARTNERS_PORTAL_DOMAIN=partners.dev2.housingbayarea.mtc.ca.gov BACKEND_API_DOMAIN=backend.dev2.housingbayarea.int -BACKEND_API_BASE=https://backend.dev2.housingbayarea.int +BACKEND_API_BASE=http://backend.dev2.housingbayarea.int BACKEND_API_PORT=3100 BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVDELIVERY_API_URL BACKEND_API_MEMORY=4096 diff --git a/cdk/lib/doorway_environment/doorway-load-balancers.ts b/cdk/lib/doorway_environment/doorway-load-balancers.ts index 53cef00aa72..bca89d345ce 100644 --- a/cdk/lib/doorway_environment/doorway-load-balancers.ts +++ b/cdk/lib/doorway_environment/doorway-load-balancers.ts @@ -1,6 +1,5 @@ import { aws_ec2, Duration, Fn } from "aws-cdk-lib"; -import { CertificateAuthority } from "aws-cdk-lib/aws-acmpca"; -import { Certificate, CertificateValidation, PrivateCertificate } from "aws-cdk-lib/aws-certificatemanager"; +import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; import { AllowedMethods, CachePolicy, Distribution, OriginRequestPolicy, PriceClass, SecurityPolicyProtocol, ViewerProtocolPolicy } from "aws-cdk-lib/aws-cloudfront"; import { LoadBalancerV2Origin } from "aws-cdk-lib/aws-cloudfront-origins"; import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2"; @@ -211,14 +210,7 @@ export class DoorwayLoadBalancers { appSubnetIds.forEach((id) => { appsubnets.push(Subnet.fromSubnetId(scope, id, id)) }) - const privateCAArn = StringParameter.fromStringParameterAttributes(scope, "privateCAArn", { - parameterName: `/doorway/privateCertAuthority`, - }).stringValue - const privateCA = CertificateAuthority.fromCertificateAuthorityArn(scope, "privateCA", privateCAArn); - const privateCert = new PrivateCertificate(scope, "PrivateCertificate", { - domainName: backendDomainName, - certificateAuthority: privateCA, - }) + this.privateLoadBalancer = new ApplicationLoadBalancer(scope, `private-${id}`, { vpc: vpc, internetFacing: false, @@ -229,12 +221,11 @@ export class DoorwayLoadBalancers { }, }); - const httpsListenerPrivate = this.privateLoadBalancer.addListener("PrivateListener", { + const httpListenerPrivate = this.privateLoadBalancer.addListener("PrivateListener", { - port: 443, + port: 80, open: true, - protocol: ApplicationProtocol.HTTPS, - certificates: [privateCert] + protocol: ApplicationProtocol.HTTP, }) this.privateTargetGroup = new ApplicationTargetGroup(scope, "PrivateTargetGroup", { @@ -253,9 +244,7 @@ export class DoorwayLoadBalancers { unhealthyThresholdCount: 2, }, }) - httpsListenerPrivate.addAction("DefaultAction", { - action: ListenerAction.forward([this.privateTargetGroup]), - }) + const privateDnsZoneName = StringParameter.fromStringParameterName(scope, `privateDnsZoneId-${props.environment}`, `/doorway/private-hosted-zone`).stringValue; diff --git a/cdk/lib/doorway_environment/doorway-props.ts b/cdk/lib/doorway_environment/doorway-props.ts index 78674583cf0..086ba007c9f 100644 --- a/cdk/lib/doorway_environment/doorway-props.ts +++ b/cdk/lib/doorway_environment/doorway-props.ts @@ -34,10 +34,6 @@ export interface DoorwayServiceProps extends DoorwayProps { securityGroup: ISecurityGroup serviceName: string clusterName: string - - - - } export interface DoorwayLoadBalancerProps extends DoorwayProps { publicService: FargateService, diff --git a/cdk/lib/doorway_environment/services/doorway_service.ts b/cdk/lib/doorway_environment/services/doorway_service.ts index 06ba1bb5282..afac465aac6 100644 --- a/cdk/lib/doorway_environment/services/doorway_service.ts +++ b/cdk/lib/doorway_environment/services/doorway_service.ts @@ -2,13 +2,12 @@ import * as cdk from "aws-cdk-lib" import { Fn } from "aws-cdk-lib" import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2" import { AppProtocol, Cluster, Compatibility, ContainerImage, FargateService, LogDrivers, NetworkMode, Protocol, TaskDefinition } from "aws-cdk-lib/aws-ecs" +import { DoorwayServiceProps } from "../doorway-props" +import { RestartServicesLambda } from "../utility_lambdas/restart-services-lambda" //import * as ecs from "aws-cdk-lib/aws-ecs"; import { ManagedPolicy } from "aws-cdk-lib/aws-iam" import { Construct } from "constructs" -import { DoorwayServiceProps } from "../doorway-props" -import { RestartServicesLambda } from "../utility_lambdas/restart-services-lambda" - export class DoorwayService { public service: FargateService From 1dc286fe524db445a53c28e16f2c4f0ca3a376fb Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 9 Sep 2025 16:12:40 -0700 Subject: [PATCH 180/217] fix: adding dbmigrate back --- cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 2 +- cdk/lib/doorway_environment/doorway-load-balancers.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 5fe152dd182..6cf3e7650ca 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -164,7 +164,7 @@ export class DoorwayBuildPipelineStack extends Stack { pipeline.addStage({ stageName: "Dev", - actions: [ecsDeploy], + actions: [dbmigrate, ecsDeploy], }) } } diff --git a/cdk/lib/doorway_environment/doorway-load-balancers.ts b/cdk/lib/doorway_environment/doorway-load-balancers.ts index bca89d345ce..5a0ae581aaf 100644 --- a/cdk/lib/doorway_environment/doorway-load-balancers.ts +++ b/cdk/lib/doorway_environment/doorway-load-balancers.ts @@ -244,6 +244,9 @@ export class DoorwayLoadBalancers { unhealthyThresholdCount: 2, }, }) + httpListenerPrivate.addTargetGroups("PrivateTargetGroup", { + targetGroups: [this.privateTargetGroup], + }); const privateDnsZoneName = StringParameter.fromStringParameterName(scope, `privateDnsZoneId-${props.environment}`, `/doorway/private-hosted-zone`).stringValue; From 504e68c58fd82c708e1d083f353d9aedd7e6cda9 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 10 Sep 2025 11:45:03 -0700 Subject: [PATCH 181/217] feat: adding secrets to env deploy --- .../doorway-app-environment-stack.ts | 3 + .../services/doorway-secrets-stack.ts | 62 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 cdk/lib/doorway_environment/services/doorway-secrets-stack.ts diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 7acc94a8ca3..80585391f07 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -11,6 +11,7 @@ import { DoorwayBackendService } from "./services/doorway-backend-service" import { DoorwayPartnersSite } from "./services/doorway-partners-site" import { DoorwayPublicSite } from "./services/doorway-public-site" import { DoorwayS3Stack } from "./services/doorway-s3-stack" +import { DoorwaySecrets } from "./services/doorway-secrets-stack" export interface DWAppEnvProps extends StackProps { cfCertArn: string @@ -59,12 +60,14 @@ export class DoorwayAppEnvironmentStack extends Stack { vpc: vpc } const s3stack = new DoorwayS3Stack(this, `doorway-s3-${environment}`, dwProps) + const secrets = new DoorwaySecrets(this, `doorway-secrets-${environment}`, dwProps) const api = new DoorwayBackendService(this, `doorway-api-service-${environment}`, dwProps) api.service.node.addDependency(s3stack) + api.service.node.addDependency(secrets) const lb = new DoorwayLoadBalancers(this, `doorway-lbs-${environment}`, dwProps); lb.loadBalancer.node.addDependency(s3stack) diff --git a/cdk/lib/doorway_environment/services/doorway-secrets-stack.ts b/cdk/lib/doorway_environment/services/doorway-secrets-stack.ts new file mode 100644 index 00000000000..4a22d4ee399 --- /dev/null +++ b/cdk/lib/doorway_environment/services/doorway-secrets-stack.ts @@ -0,0 +1,62 @@ +import { SecretValue } from "aws-cdk-lib"; +import { Secret } from "aws-cdk-lib/aws-secretsmanager"; +import { Construct } from "constructs"; + +import { DoorwayProps } from "../doorway-props"; + +export class DoorwaySecrets { + constructor(scope: Construct, id: string, props: DoorwayProps) { + + new Secret(scope, "googleId", { + secretName: `/doorway/${props.environment}/GOOGLE_API_ID`, + secretStringValue: SecretValue.unsafePlainText("changeme"), + }); + + new Secret(scope, "googleEmail", { + secretName: `/doorway/${props.environment}/GOOGLE_API_EMAIL`, + secretStringValue: SecretValue.unsafePlainText("changeme"), + }); + + new Secret(scope, "googleKey", { + secretName: `/doorway/${props.environment}/GOOGLE_API_KEY`, + secretStringValue: SecretValue.unsafePlainText("changeme"), + }); + new Secret(scope, "googleMapsKey", { + secretName: `/doorway/${props.environment}/GOOGLE_MAPS_API_KEY`, + secretStringValue: SecretValue.unsafePlainText("changeme"), + }); + new Secret(scope, "googleMapsMapId", { + secretName: `/doorway/${props.environment}/GOOGLE_MAPS_MAP_ID`, + secretStringValue: SecretValue.unsafePlainText("changeme"), + }); + + new Secret(scope, "govDeliveryURL", { + secretName: `/doorway/${props.environment}/GOVDELIVERY_API_URL`, + secretStringValue: SecretValue.unsafePlainText("changeme"), + }); + + new Secret(scope, "govDeliveryPassword", { + secretName: `/doorway/${props.environment}/GOVDELIVERY_PASSWORD`, + secretStringValue: SecretValue.unsafePlainText("changeme"), + }); + + new Secret(scope, "govDeliveryUsername", { + secretName: `/doorway/${props.environment}/GOVDELIVERY_USERNAME`, + secretStringValue: SecretValue.unsafePlainText("changeme"), + }); + new Secret(scope, "emailApiKey", { + secretName: `/doorway/${props.environment}/EMAIL_API_KEY`, + secretStringValue: SecretValue.unsafePlainText("changeme"), + }); + + new Secret(scope, "cloudinaryKey", { + secretName: `/doorway/${props.environment}/CLOUDINARY_KEY`, + secretStringValue: SecretValue.unsafePlainText("changeme"), + }); + + new Secret(scope, "cloudinarySecret", { + secretName: `/doorway/${props.environment}/CLOUDINARY_SECRET`, + secretStringValue: SecretValue.unsafePlainText("changeme"), + }); + } +} \ No newline at end of file From ad43ccf2d1530fc6e9c86ccbb92ccd6dd03f8ec9 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 10 Sep 2025 14:48:35 -0700 Subject: [PATCH 182/217] fix: dependency fix --- cdk/lib/doorway_environment/doorway-app-environment-stack.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 80585391f07..b4e8ab3ad26 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -67,7 +67,6 @@ export class DoorwayAppEnvironmentStack extends Stack { const api = new DoorwayBackendService(this, `doorway-api-service-${environment}`, dwProps) api.service.node.addDependency(s3stack) - api.service.node.addDependency(secrets) const lb = new DoorwayLoadBalancers(this, `doorway-lbs-${environment}`, dwProps); lb.loadBalancer.node.addDependency(s3stack) From d0431cc17e847c51c690216a6cf2e9ed21056ec0 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 11 Sep 2025 14:08:38 -0700 Subject: [PATCH 183/217] fix. renaming application stack --- cdk/bin/doorway-cdk.ts | 7 ++++--- cdk/lib/doit.ts | 3 --- 2 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 cdk/lib/doit.ts diff --git a/cdk/bin/doorway-cdk.ts b/cdk/bin/doorway-cdk.ts index 9d902b6319e..f0bda43bbe9 100644 --- a/cdk/bin/doorway-cdk.ts +++ b/cdk/bin/doorway-cdk.ts @@ -12,19 +12,20 @@ import { DoorwayCloudFrontCertStack } from "../lib/doorway_environment/doorway-c const app = new cdk.App() const environment = process.env.ENVIRONMENT || "dev2" - +/** This stack creates the doorway application build pipeline **/ new DoorwayBuildPipelineStack(app, "DoorwayBuildPipelineStack", { githubSecret: "mtc/githubSecret", dockerHubSecret: "mtc/dockerHub", }) - +/** This stack creates the cert used by the cloudfront distribution. It sits in it's own stack because cloudfront certs have to be created in us-east-1 **/ const cfCertStack = new DoorwayCloudFrontCertStack(app, `DoorwayCloudFrontCertStack-${environment}`, { environment, publicDomainName: process.env.PUBLIC_PORTAL_DOMAIN || `${environment}.housingbayarea.mtc.ca.gov`, partnersDomainName: process.env.PARTNERS_PORTAL_DOMAIN || `partners.${environment}.housingbayarea.mtc.ca.gov`, }) -new DoorwayAppEnvironmentStack(app, "DoorwayAppEnvironmentStack", { +/** This stack actually creates a doorway environment. */ +new DoorwayAppEnvironmentStack(app, `DoorwayAppEnvironmentStack-${environment}`, { cfCertArn: cfCertStack.cloudFrontCertArn, }) diff --git a/cdk/lib/doit.ts b/cdk/lib/doit.ts deleted file mode 100644 index 8c499333aa5..00000000000 --- a/cdk/lib/doit.ts +++ /dev/null @@ -1,3 +0,0 @@ -const secretarn = "arn:aws:secretsmanager:us-west-2:364076391763:secret:/doorway/dev2/CLOUDINARY_KEY" -const cleanArn = secretarn.replace(/(-[A-Za-z0-9]{6}):.*$/, '$1'); -console.log(cleanArn) \ No newline at end of file From edfb3c67401878e91b00d7774aee187ecffb4389 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 12 Sep 2025 16:39:33 -0700 Subject: [PATCH 184/217] fix: changing cloudfront definition --- cdk/bin/doorway-cdk.ts | 9 +- cdk/lib/build-pipeline/doorway-ecs-deploy.ts | 5 + .../doorway-app-environment-stack.ts | 10 +- .../doorway-cloudfront-cert-stack.ts | 41 ------- .../doorway-cloudfront-stack.ts | 111 ++++++++++++++++++ .../doorway-load-balancers.ts | 99 ++++------------ cdk/lib/doorway_environment/doorway-props.ts | 1 - cdk/staging3.env | 37 ++++++ ci/buildspec/update_ecs.yml | 2 +- 9 files changed, 185 insertions(+), 130 deletions(-) delete mode 100644 cdk/lib/doorway_environment/doorway-cloudfront-cert-stack.ts create mode 100644 cdk/lib/doorway_environment/doorway-cloudfront-stack.ts create mode 100644 cdk/staging3.env diff --git a/cdk/bin/doorway-cdk.ts b/cdk/bin/doorway-cdk.ts index f0bda43bbe9..0a039f3c345 100644 --- a/cdk/bin/doorway-cdk.ts +++ b/cdk/bin/doorway-cdk.ts @@ -3,7 +3,7 @@ import * as cdk from "aws-cdk-lib" import { DoorwayBuildPipelineStack } from "../lib/build-pipeline/doorway-build-pipeline-stack" import { DoorwayAppEnvironmentStack } from "../lib/doorway_environment/doorway-app-environment-stack" -import { DoorwayCloudFrontCertStack } from "../lib/doorway_environment/doorway-cloudfront-cert-stack" +import { DoorwayCloudFrontStack } from "../lib/doorway_environment/doorway-cloudfront-stack" /** * This script is really just a wrapper to execute the various @@ -18,14 +18,13 @@ new DoorwayBuildPipelineStack(app, "DoorwayBuildPipelineStack", { dockerHubSecret: "mtc/dockerHub", }) /** This stack creates the cert used by the cloudfront distribution. It sits in it's own stack because cloudfront certs have to be created in us-east-1 **/ -const cfCertStack = new DoorwayCloudFrontCertStack(app, `DoorwayCloudFrontCertStack-${environment}`, { +const cfCertStack = new DoorwayCloudFrontStack(app, `DoorwayCloudFrontStack-${environment}`, { environment, publicDomainName: process.env.PUBLIC_PORTAL_DOMAIN || `${environment}.housingbayarea.mtc.ca.gov`, partnersDomainName: process.env.PARTNERS_PORTAL_DOMAIN || `partners.${environment}.housingbayarea.mtc.ca.gov`, + loadBalancerDnsName: process.env.LOAD_BALANCER_DNS_NAME }) /** This stack actually creates a doorway environment. */ -new DoorwayAppEnvironmentStack(app, `DoorwayAppEnvironmentStack-${environment}`, { - cfCertArn: cfCertStack.cloudFrontCertArn, -}) +new DoorwayAppEnvironmentStack(app, `DoorwayAppEnvironmentStack-${environment}`) diff --git a/cdk/lib/build-pipeline/doorway-ecs-deploy.ts b/cdk/lib/build-pipeline/doorway-ecs-deploy.ts index 1ceeaff407e..3fc75507520 100644 --- a/cdk/lib/build-pipeline/doorway-ecs-deploy.ts +++ b/cdk/lib/build-pipeline/doorway-ecs-deploy.ts @@ -18,6 +18,11 @@ export class DoorwayECSDeploy { constructor(scope: Construct, id: string, props: DoorwayECSDeployProps) { const project = new PipelineProject(scope, `doorway-ecsDeploy-${props.environment}`, { buildSpec: BuildSpec.fromSourceFilename(props.buildspec), + environmentVariables: { + ENVIRONMENT: { + value: props.environment, + }, + }, environment: { buildImage: LinuxBuildImage.STANDARD_7_0, computeType: ComputeType.LARGE, diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index b4e8ab3ad26..45abfd6ef5b 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -1,4 +1,4 @@ -import { aws_logs, Fn, RemovalPolicy, Stack, StackProps } from "aws-cdk-lib" +import { aws_logs, Fn, RemovalPolicy, Stack } from "aws-cdk-lib" import { Vpc } from "aws-cdk-lib/aws-ec2" import { LogGroup } from "aws-cdk-lib/aws-logs" import { Construct } from "constructs" @@ -13,14 +13,9 @@ import { DoorwayPublicSite } from "./services/doorway-public-site" import { DoorwayS3Stack } from "./services/doorway-s3-stack" import { DoorwaySecrets } from "./services/doorway-secrets-stack" -export interface DWAppEnvProps extends StackProps { - cfCertArn: string -} - export class DoorwayAppEnvironmentStack extends Stack { - constructor(scope: Construct, id: string, props: DWAppEnvProps) { + constructor(scope: Construct, id: string) { super(scope, id, { - ...props, env: { account: process.env.CDK_DEFAULT_ACCOUNT || "364076391763", region: process.env.CDK_DEFAULT_REGION || "us-west-2", @@ -56,7 +51,6 @@ export class DoorwayAppEnvironmentStack extends Stack { publicServiceName: `doorway-public-${environment}`, partnersServiceName: `doorway-partners-${environment}`, clusterName: Fn.importValue(`doorway-ecs-cluster-${environment}`), - cfCertArn: props.cfCertArn, vpc: vpc } const s3stack = new DoorwayS3Stack(this, `doorway-s3-${environment}`, dwProps) diff --git a/cdk/lib/doorway_environment/doorway-cloudfront-cert-stack.ts b/cdk/lib/doorway_environment/doorway-cloudfront-cert-stack.ts deleted file mode 100644 index f5272c0607f..00000000000 --- a/cdk/lib/doorway_environment/doorway-cloudfront-cert-stack.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { CfnOutput, Stack, StackProps } from "aws-cdk-lib"; -import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; -import { HostedZone } from "aws-cdk-lib/aws-route53"; -import { Construct } from "constructs"; - -export interface CloudFrontCertStackProps extends StackProps { - environment: string; - publicDomainName: string; - partnersDomainName: string; -} - -export class DoorwayCloudFrontCertStack extends Stack { - public cloudFrontCertArn: string; - constructor(scope: Construct, id: string, props: CloudFrontCertStackProps) { - super(scope, id, { - ...props, env: { - region: 'us-east-1', - account: process.env.CDK_DEFAULT_ACCOUNT || "364076391763", - }, - crossRegionReferences: true - }); - - const dnsZone = HostedZone.fromLookup(this, `publicDnsZone-${props.environment}`, { - domainName: "housingbayarea.mtc.ca.gov", - }); - - const cert = new Certificate(this, "CloudFrontCertificate", { - domainName: props.publicDomainName, - validation: CertificateValidation.fromDns(dnsZone), - subjectAlternativeNames: [props.partnersDomainName] - }); - - new CfnOutput(this, "CloudFrontCertArn", { - value: cert.certificateArn, - exportName: `doorway-cf-cert-arn-${props.environment}`, - description: `CloudFront certificate ARN for ${props.environment}`, - - }); - this.cloudFrontCertArn = cert.certificateArn; - } -} \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-cloudfront-stack.ts b/cdk/lib/doorway_environment/doorway-cloudfront-stack.ts new file mode 100644 index 00000000000..f2ec2bcb088 --- /dev/null +++ b/cdk/lib/doorway_environment/doorway-cloudfront-stack.ts @@ -0,0 +1,111 @@ +import { Duration, Fn, Stack, StackProps, CfnParameter } from "aws-cdk-lib"; +import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; +import { AllowedMethods, CachePolicy, Distribution, OriginRequestPolicy, PriceClass, SecurityPolicyProtocol, ViewerProtocolPolicy } from "aws-cdk-lib/aws-cloudfront"; +import { LoadBalancerV2Origin } from "aws-cdk-lib/aws-cloudfront-origins"; +import { ApplicationLoadBalancer } from "aws-cdk-lib/aws-elasticloadbalancingv2"; +import { HostedZone } from "aws-cdk-lib/aws-route53"; +import { Construct } from "constructs"; + +export interface CloudFrontCertStackProps extends StackProps { + environment: string; + publicDomainName: string; + partnersDomainName: string; + loadBalancerDnsName?: string; +} + +export class DoorwayCloudFrontStack extends Stack { + public cloudFrontCertArn: string; + constructor(scope: Construct, id: string, props: CloudFrontCertStackProps) { + super(scope, id, { + ...props, env: { + region: 'us-east-1', + account: process.env.CDK_DEFAULT_ACCOUNT || "364076391763", + }, + crossRegionReferences: true + }); + + + + + const dnsZone = HostedZone.fromLookup(this, `publicDnsZone-${props.environment}`, { + domainName: "housingbayarea.mtc.ca.gov", + }); + const publicDomainName = process.env.PUBLIC_PORTAL_DOMAIN || `${props.environment}.housingbayarea.mtc.ca.gov` + const partnersDomainName = process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov` + const lbDnsName = props.loadBalancerDnsName || 'doorway-lbs-dev2-734646298.us-west-2.elb.amazonaws.com' + + const cert = new Certificate(this, "CloudFrontCertificate", { + domainName: props.publicDomainName, + validation: CertificateValidation.fromDns(dnsZone), + subjectAlternativeNames: [props.partnersDomainName] + }); + + const cfOrigin = new LoadBalancerV2Origin(ApplicationLoadBalancer.fromApplicationLoadBalancerAttributes(this, `PublicLB-${props.environment}`, { + loadBalancerArn: `arn:aws:elasticloadbalancing:us-west-2:364076391763:loadbalancer/app/doorway-lbs-${props.environment}/dummy`, + securityGroupId: 'sg-dummy', + loadBalancerDnsName: lbDnsName + })) + const cachePolicy = new CachePolicy(this, `CachePolicy-images-${props.environment}`, { + cachePolicyName: `CachePolicy-images-${props.environment}`, + enableAcceptEncodingGzip: false, + enableAcceptEncodingBrotli: false, + minTtl: Duration.seconds(60), + maxTtl: Duration.seconds(31536000), + defaultTtl: Duration.seconds(86400), + headerBehavior: { + behavior: "whitelist", + headers: ["Host"], + }, + queryStringBehavior: { + behavior: "none", + }, + cookieBehavior: { + behavior: "none", + }, + }); + + new Distribution(this, `CloudFront-${props.environment}`, { + domainNames: [publicDomainName, partnersDomainName], + certificate: cert, + minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021, + enableIpv6: true, + + + defaultRootObject: "", + logIncludesCookies: true, + logFilePrefix: "cloudfront", + + comment: `Doorway Cloudfront for ${props.environment}`, + + priceClass: PriceClass.PRICE_CLASS_100, + geoRestriction: { + locations: ["US"], + restrictionType: "whitelist" + }, + + + defaultBehavior: { + origin: cfOrigin, + cachePolicy: CachePolicy.CACHING_DISABLED, // Disable caching + viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + allowedMethods: AllowedMethods.ALLOW_ALL, + originRequestPolicy: OriginRequestPolicy.ALL_VIEWER + + }, + additionalBehaviors: { + "/images/*": { + origin: cfOrigin, + allowedMethods: AllowedMethods.ALLOW_ALL, + cachePolicy: cachePolicy, + viewerProtocolPolicy: ViewerProtocolPolicy.ALLOW_ALL, + + } + } + }) + this.cloudFrontCertArn = cert.certificateArn; + // Only create CloudFront A records if environment variable is set + + + + } +} diff --git a/cdk/lib/doorway_environment/doorway-load-balancers.ts b/cdk/lib/doorway_environment/doorway-load-balancers.ts index 5a0ae581aaf..a0bf77b4bfb 100644 --- a/cdk/lib/doorway_environment/doorway-load-balancers.ts +++ b/cdk/lib/doorway_environment/doorway-load-balancers.ts @@ -1,11 +1,9 @@ -import { aws_ec2, Duration, Fn } from "aws-cdk-lib"; +import { aws_ec2, CfnOutput, Duration, Fn } from "aws-cdk-lib"; import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; -import { AllowedMethods, CachePolicy, Distribution, OriginRequestPolicy, PriceClass, SecurityPolicyProtocol, ViewerProtocolPolicy } from "aws-cdk-lib/aws-cloudfront"; -import { LoadBalancerV2Origin } from "aws-cdk-lib/aws-cloudfront-origins"; import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2"; import { ApplicationListener, ApplicationListenerRule, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup, ListenerAction, ListenerCondition, TargetType } from "aws-cdk-lib/aws-elasticloadbalancingv2"; import { ARecord, HostedZone, RecordTarget } from "aws-cdk-lib/aws-route53"; -import { CloudFrontTarget, LoadBalancerTarget } from "aws-cdk-lib/aws-route53-targets"; +import { LoadBalancerTarget } from "aws-cdk-lib/aws-route53-targets"; import { StringParameter } from "aws-cdk-lib/aws-ssm"; import { Construct } from "constructs"; @@ -46,7 +44,6 @@ export class DoorwayLoadBalancers { }, }); - const dnsZoneName = StringParameter.fromStringParameterName(scope, `publicDnsZoneId-${props.environment}`, `/doorway/public-hosted-zone`).stringValue; const dnsZone = HostedZone.fromLookup(scope, `publicDnsZone-${props.environment}`, { domainName: "housingbayarea.mtc.ca.gov", }); @@ -59,7 +56,6 @@ export class DoorwayLoadBalancers { subjectAlternativeNames: [partnersDomainName] }) - const cfCert = Certificate.fromCertificateArn(scope, "CloudFrontCertificate", props.cfCertArn); const httpListener = this.loadBalancer.addListener("HttpListener", { port: 80, @@ -131,74 +127,9 @@ export class DoorwayLoadBalancers { conditions: [ListenerCondition.hostHeaders([partnersDomainName]) ] }); - const cfOrigin = new LoadBalancerV2Origin(this.loadBalancer) - const cachePolicy = new CachePolicy(scope, `CachePolicy-images-${props.environment}`, { - cachePolicyName: `CachePolicy-images-${props.environment}`, - enableAcceptEncodingGzip: false, - enableAcceptEncodingBrotli: false, - minTtl: Duration.seconds(60), - maxTtl: Duration.seconds(31536000), - defaultTtl: Duration.seconds(86400), - headerBehavior: { - behavior: "whitelist", - headers: ["Host"], - }, - queryStringBehavior: { - behavior: "none", - }, - cookieBehavior: { - behavior: "none", - }, - }); - - const cloudfrontDist = new Distribution(scope, `CloudFront-${props.environment}`, { - domainNames: [publicDomainName, partnersDomainName], - certificate: cfCert, - minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021, - enableIpv6: true, - defaultRootObject: "", - logIncludesCookies: true, - logFilePrefix: "cloudfront", - - comment: `Doorway Cloudfront for ${props.environment}`, - - priceClass: PriceClass.PRICE_CLASS_100, - geoRestriction: { - locations: ["US"], - restrictionType: "whitelist" - }, - - defaultBehavior: { - origin: cfOrigin, - cachePolicy: CachePolicy.CACHING_DISABLED, // Disable caching - viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS, - allowedMethods: AllowedMethods.ALLOW_ALL, - originRequestPolicy: OriginRequestPolicy.ALL_VIEWER - - }, - additionalBehaviors: { - "/images/*": { - origin: cfOrigin, - allowedMethods: AllowedMethods.ALLOW_ALL, - cachePolicy: cachePolicy, - viewerProtocolPolicy: ViewerProtocolPolicy.ALLOW_ALL, - - } - } - }) - new ARecord(scope, "PartnersARecord", { - zone: dnsZone, - recordName: partnersDomainName, - target: RecordTarget.fromAlias(new CloudFrontTarget(cloudfrontDist)), - }); - new ARecord(scope, "PublicARecord", { - zone: dnsZone, - recordName: publicDomainName, - target: RecordTarget.fromAlias(new CloudFrontTarget(cloudfrontDist)), - }); const appSGId = Fn.importValue(`doorway-app-sg-${props.environment}`) @@ -255,13 +186,33 @@ export class DoorwayLoadBalancers { hostedZoneId: privateDnsZoneName, zoneName: "housingbayarea.int", }); - new ARecord(scope, "PrivateARecord", { + new ARecord(scope, `PrivateARecord-${props.environment}`, { zone: privateDnsZone, recordName: backendDomainName, target: RecordTarget.fromAlias(new LoadBalancerTarget(this.privateLoadBalancer)), }); + new ARecord(scope, "PartnersARecord", { + zone: dnsZone, + recordName: partnersDomainName, + target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), + }); - - + new ARecord(scope, "PublicARecord", { + zone: dnsZone, + recordName: publicDomainName, + target: RecordTarget.fromAlias(new LoadBalancerTarget(this.loadBalancer)), + }); + new CfnOutput(scope, `public-lb-dns-${props.environment}`, { + value: this.loadBalancer.loadBalancerDnsName, + exportName: `public-lb-dns-${props.environment}` + }) + new CfnOutput(scope, `public-lb-arn-${props.environment}`, { + value: this.loadBalancer.loadBalancerArn, + exportName: `doorway-public-lb-${props.environment}-arn` + }) + new CfnOutput(scope, `public-lb-sg-${props.environment}`, { + value: sg.securityGroupId, + exportName: `doorway-public-lb-${props.environment}-sg` + }) } } \ No newline at end of file diff --git a/cdk/lib/doorway_environment/doorway-props.ts b/cdk/lib/doorway_environment/doorway-props.ts index 086ba007c9f..375d13cfab4 100644 --- a/cdk/lib/doorway_environment/doorway-props.ts +++ b/cdk/lib/doorway_environment/doorway-props.ts @@ -12,7 +12,6 @@ export interface DoorwayProps { backendServiceName: string publicServiceName: string partnersServiceName: string - cfCertArn: string vpc: IVpc } diff --git a/cdk/staging3.env b/cdk/staging3.env new file mode 100644 index 00000000000..8e4f8009d86 --- /dev/null +++ b/cdk/staging3.env @@ -0,0 +1,37 @@ +MAINTENANCE_WINDOW=2024-03-26 18:00 -08:00,2024-04-25 18:00 -08:00 +DEST_REPO_NAMESPACE=doorway +CACHE_REVALIDATE=60 +LANGUAGES=en,es,zh,vi,tl +FEATURE_LISTINGS_APPROVAL=TRUE + +ECR_ACCOUNT_ID=364076391763 +ECR_NAMESPACE=doorway +ECR_REGION=us-west-2 +CORS_ORIGINS=https://staging3.housingbayarea.mtc.ca.gov, https://partners.staging3.housingbayarea.mtc.ca.gov + +USE_SECURE_DOWNLOAD_PATHWAY=TRUE +GTM_KEY=GTM-KF22FJP +SOURCE_REPO_NAMESPACE=doorway +IDLE_TIMEOUT=5 +CLOUDINARY_CLOUD_NAME=exygy +APP_SECRET="" + +PUBLIC_PORTAL_PORT=3000 +PUBLIC_PORTAL_DOMAIN=staging3.housingbayarea.mtc.ca.gov +PUBLIC_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY +DISABLE_CORS=FALSE +SAME_SITE=true +HTTPS_OFF=true + + +PARTNERS_PORTAL_PORT=3200 +PARTNERS_PORTAL_DOMAIN=partners.staging3.housingbayarea.mtc.ca.gov +BACKEND_API_DOMAIN=backend.staging3.housingbayarea.int +BACKEND_API_BASE=http://backend.staging3.housingbayarea.int +BACKEND_API_PORT=3100 +BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVDELIVERY_API_URL +BACKEND_API_MEMORY=4096 +BACKEND_API_CPUS=2 +BACKEND_API_INSTANCES=3 +LOG_LEVEL=info + diff --git a/ci/buildspec/update_ecs.yml b/ci/buildspec/update_ecs.yml index 9258510dd54..d99663fef27 100644 --- a/ci/buildspec/update_ecs.yml +++ b/ci/buildspec/update_ecs.yml @@ -12,4 +12,4 @@ phases: - npm install -g aws-cdk - cd ${CODEBUILD_SRC_DIR}/cdk - yarn install - - cdk deploy DoorwayAppEnvironmentStack --require-approval never + - cdk deploy DoorwayAppEnvironmentStack-${ENVIRONMENT} --require-approval never From 09769b2a7a93f58fcadcbe0a6c4f536881e30907 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 15 Sep 2025 13:19:50 -0700 Subject: [PATCH 185/217] feat: import listings task --- .../doorway-app-environment-stack.ts | 10 +-- cdk/lib/doorway_environment/doorway-props.ts | 4 +- .../services/doorway-ecs-task.ts | 46 ++++++++++ .../services/doorway-import-listings.ts | 89 +++++++++++++++++++ .../services/doorway_service.ts | 45 ++-------- ci/buildspec/build_import_listings.yml | 4 +- 6 files changed, 148 insertions(+), 50 deletions(-) create mode 100644 cdk/lib/doorway_environment/services/doorway-ecs-task.ts create mode 100644 cdk/lib/doorway_environment/services/doorway-import-listings.ts diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index 45abfd6ef5b..a8ac1c7bb4a 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -8,6 +8,7 @@ import path from "path" import { DoorwayLoadBalancers } from "./doorway-load-balancers" import { DoorwayProps } from "./doorway-props" import { DoorwayBackendService } from "./services/doorway-backend-service" +import { DoorwayImportListings } from "./services/doorway-import-listings" import { DoorwayPartnersSite } from "./services/doorway-partners-site" import { DoorwayPublicSite } from "./services/doorway-public-site" import { DoorwayS3Stack } from "./services/doorway-s3-stack" @@ -55,26 +56,21 @@ export class DoorwayAppEnvironmentStack extends Stack { } const s3stack = new DoorwayS3Stack(this, `doorway-s3-${environment}`, dwProps) const secrets = new DoorwaySecrets(this, `doorway-secrets-${environment}`, dwProps) - - - - const api = new DoorwayBackendService(this, `doorway-api-service-${environment}`, dwProps) api.service.node.addDependency(s3stack) - const lb = new DoorwayLoadBalancers(this, `doorway-lbs-${environment}`, dwProps); lb.loadBalancer.node.addDependency(s3stack) - const publicSite = new DoorwayPublicSite(this, `doorway-public-${environment}`, dwProps) publicSite.service.node.addDependency(lb.loadBalancer) publicSite.service.node.addDependency(api.service) - lb.publicTargetGroup.addTarget(publicSite.service) const partnersSite = new DoorwayPartnersSite(this, `doorway-partners-${environment}`, dwProps) partnersSite.service.node.addDependency(lb.loadBalancer) partnersSite.service.node.addDependency(api.service) lb.partnersTargetGroup.addTarget(partnersSite.service) lb.privateTargetGroup.addTarget(api.service) + const scheduler = new DoorwayImportListings(this, `doorway-import-listings-scheduler-${environment}`, dwProps) + scheduler.schedule.node.addDependency(api.service) } } diff --git a/cdk/lib/doorway_environment/doorway-props.ts b/cdk/lib/doorway_environment/doorway-props.ts index 375d13cfab4..6bbf0ad8f9c 100644 --- a/cdk/lib/doorway_environment/doorway-props.ts +++ b/cdk/lib/doorway_environment/doorway-props.ts @@ -27,8 +27,8 @@ export interface DoorwayServiceProps extends DoorwayProps { apiTargetDomainName?: string apiTargetPort?: number executionRole: Role - publicUploads: IBucket - secureUploads: IBucket + publicUploads?: IBucket + secureUploads?: IBucket container: string securityGroup: ISecurityGroup serviceName: string diff --git a/cdk/lib/doorway_environment/services/doorway-ecs-task.ts b/cdk/lib/doorway_environment/services/doorway-ecs-task.ts new file mode 100644 index 00000000000..6af13b8ac39 --- /dev/null +++ b/cdk/lib/doorway_environment/services/doorway-ecs-task.ts @@ -0,0 +1,46 @@ +import { Aws } from "aws-cdk-lib"; +import { AppProtocol, Compatibility, ContainerImage, LogDrivers, NetworkMode, Protocol, TaskDefinition } from "aws-cdk-lib/aws-ecs"; +import { Construct } from "constructs"; + +import { DoorwayServiceProps } from "../doorway-props"; + +export class DoorwayEcsTask { + public task: TaskDefinition + constructor(scope: Construct, id: string, props: DoorwayServiceProps) { + this.task = new TaskDefinition(scope, `${id}-task`, { + + compatibility: Compatibility.FARGATE, + executionRole: props.executionRole, + taskRole: props.executionRole, + networkMode: NetworkMode.AWS_VPC, + cpu: `${props.cpu * 1024}`, + memoryMiB: `${props.memory}` + + }) + this.task.addContainer(`${id}-container`, { + image: ContainerImage.fromRegistry( + `${Aws.ACCOUNT_ID}.dkr.ecr.${Aws.REGION}.amazonaws.com/${props.container}`, + ), + cpu: props.cpu, + memoryLimitMiB: props.memory, + essential: true, + logging: LogDrivers.awsLogs({ + streamPrefix: id, + logGroup: props.logGroup, + }), + secrets: props.secrets || {}, + environment: props.environmentVariables, + entryPoint: [], + portMappings: [ + { + name: `${id}-port-mapping`, + containerPort: props.port, + protocol: Protocol.TCP, + appProtocol: AppProtocol.http, + hostPort: props.port, + }, + ], + + }) + } +} \ No newline at end of file diff --git a/cdk/lib/doorway_environment/services/doorway-import-listings.ts b/cdk/lib/doorway_environment/services/doorway-import-listings.ts new file mode 100644 index 00000000000..c2abd04a8e3 --- /dev/null +++ b/cdk/lib/doorway_environment/services/doorway-import-listings.ts @@ -0,0 +1,89 @@ +import { Duration, Fn } from "aws-cdk-lib"; +import { ISecurityGroup, ISubnet, SecurityGroup, Subnet } from "aws-cdk-lib/aws-ec2"; +import { Cluster, Secret } from "aws-cdk-lib/aws-ecs"; +import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"; +import { Schedule, ScheduleExpression } from "aws-cdk-lib/aws-scheduler"; +import { EcsRunFargateTask } from "aws-cdk-lib/aws-scheduler-targets"; +import * as secret from "aws-cdk-lib/aws-secretsmanager"; +import { Construct } from "constructs"; + +import { DoorwayProps } from "../doorway-props"; +import { DoorwayEcsTask } from "./doorway-ecs-task"; + +export class DoorwayImportListings { + public schedule: Schedule + constructor(scope: Construct, id: string, props: DoorwayProps) { + const executionRole = new Role(scope, `executionRole-${id}`, { + assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), + }) + const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) + const dbSecret = secret.Secret.fromSecretCompleteArn(scope, "dbSecret-import-listings", dbSecretArn) + dbSecret.grantRead(executionRole) + const secrets: { [key: string]: Secret } = { + PGUSER: Secret.fromSecretsManager(dbSecret, "username"), + PGPASSWORD: Secret.fromSecretsManager(dbSecret, "password"), + PGPORT: Secret.fromSecretsManager(dbSecret, "port"), + PGHOST: Secret.fromSecretsManager(dbSecret, "host"), + PGDATABASE: Secret.fromSecretsManager(dbSecret, "dbname"), + } + const appTierPrivateSGId = Fn.importValue(`doorway-app-sg-${props.environment}`) + const privateSG: ISecurityGroup = SecurityGroup.fromSecurityGroupId( + scope, + `appTierPrivateSG-${id}`, + appTierPrivateSGId, + ) + + + const importListings = new DoorwayEcsTask(scope, `doorway-import-listings-${props.environment}`, { + ...props, + memory: 1024, + cpu: 1, + instances: 1, + port: 10246, + secrets: secrets, + environmentVariables: { + JURISDICTION_INCLUDE_LIST: "San Jose,San Mateo,Alameda", + EXTERNAL_API_BASE: "https://proxy.housingbayarea.org", + LISTING_VIEW: "base" + }, + serviceConnectServer: false, + domainName: "", + executionRole: executionRole, + container: `doorway/import-listings:run-${process.env.GIT_HASH}`, + securityGroup: privateSG, + serviceName: `doorway-import-listings-${props.environment}`, + clusterName: props.clusterName, + + + }) + const cluster = Cluster.fromClusterAttributes(scope, `import-listings-cluster-${props.environment}`, { + clusterName: props.clusterName, + vpc: props.vpc + + + }) + // Get the subnets + const appSubnetIds: string[] = [] + appSubnetIds.push(Fn.importValue(`doorway-app-subnet-1-${props.environment}`)) + appSubnetIds.push(Fn.importValue(`doorway-app-subnet-2-${props.environment}`)) + const appsubnets: ISubnet[] = [] + appSubnetIds.forEach((id) => { + appsubnets.push(Subnet.fromSubnetId(scope, id, id)) + }) + const target = new EcsRunFargateTask(cluster, { + taskDefinition: importListings.task, + assignPublicIp: false, + securityGroups: [privateSG], + vpcSubnets: { + subnets: appsubnets, + }, + + }) + this.schedule = new Schedule(scope, `import-listings - schedule - ${props.environment} `, { + target: target, + schedule: ScheduleExpression.rate(Duration.minutes(30)) + + + }) + } +} \ No newline at end of file diff --git a/cdk/lib/doorway_environment/services/doorway_service.ts b/cdk/lib/doorway_environment/services/doorway_service.ts index afac465aac6..aa4f6498739 100644 --- a/cdk/lib/doorway_environment/services/doorway_service.ts +++ b/cdk/lib/doorway_environment/services/doorway_service.ts @@ -1,13 +1,15 @@ import * as cdk from "aws-cdk-lib" import { Fn } from "aws-cdk-lib" import { ISubnet, Subnet } from "aws-cdk-lib/aws-ec2" -import { AppProtocol, Cluster, Compatibility, ContainerImage, FargateService, LogDrivers, NetworkMode, Protocol, TaskDefinition } from "aws-cdk-lib/aws-ecs" -import { DoorwayServiceProps } from "../doorway-props" -import { RestartServicesLambda } from "../utility_lambdas/restart-services-lambda" +import { Cluster, FargateService } from "aws-cdk-lib/aws-ecs" //import * as ecs from "aws-cdk-lib/aws-ecs"; import { ManagedPolicy } from "aws-cdk-lib/aws-iam" import { Construct } from "constructs" +import { DoorwayServiceProps } from "../doorway-props" +import { RestartServicesLambda } from "../utility_lambdas/restart-services-lambda" +import { DoorwayEcsTask } from "./doorway-ecs-task" + export class DoorwayService { public service: FargateService @@ -37,42 +39,7 @@ export class DoorwayService { appSubnetIds.forEach((id) => { appsubnets.push(Subnet.fromSubnetId(scope, id, id)) }) - - const task = new TaskDefinition(scope, `${id}-task`, { - - compatibility: Compatibility.FARGATE, - executionRole: props.executionRole, - taskRole: props.executionRole, - networkMode: NetworkMode.AWS_VPC, - cpu: `${props.cpu * 1024}`, - memoryMiB: `${props.memory}` - }) - task.addContainer(`${id}-container`, { - image: ContainerImage.fromRegistry( - `${cdk.Aws.ACCOUNT_ID}.dkr.ecr.${cdk.Aws.REGION}.amazonaws.com/${props.container}`, - ), - cpu: props.cpu, - memoryLimitMiB: props.memory, - essential: true, - logging: LogDrivers.awsLogs({ - streamPrefix: id, - logGroup: props.logGroup, - }), - secrets: props.secrets || {}, - environment: props.environmentVariables, - entryPoint: [], - portMappings: [ - { - name: `${id}-port-mapping`, - containerPort: props.port, - protocol: Protocol.TCP, - appProtocol: AppProtocol.http, - hostPort: props.port, - }, - ], - }) - - + const task = new DoorwayEcsTask(scope, id, props).task this.service = new FargateService(scope, `${id}-fargate-service`, { taskDefinition: task, diff --git a/ci/buildspec/build_import_listings.yml b/ci/buildspec/build_import_listings.yml index b5003854ee7..59d1cd3572c 100644 --- a/ci/buildspec/build_import_listings.yml +++ b/ci/buildspec/build_import_listings.yml @@ -11,8 +11,8 @@ phases: - export QUALIFIED_IMAGE_NAME="${ECR_NAMESPACE}/${IMAGE_NAME}" - export TEST_IMAGE="${QUALIFIED_IMAGE_NAME}:test" - export RUN_IMAGE="${QUALIFIED_IMAGE_NAME}:run" - - export ECR_GIT_TAG="${ECR_URL}/${QUALIFIED_IMAGE_NAME}:run-${ENVIRONMENT}-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" - - export ECR_RUN_TAG="${ECR_URL}/${QUALIFIED_IMAGE_NAME}:run-${ENVIRONMENT}-candidate" + - export ECR_GIT_TAG="${ECR_URL}/${QUALIFIED_IMAGE_NAME}:run-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" + - export ECR_RUN_TAG="${ECR_URL}/${QUALIFIED_IMAGE_NAME}:run-candidate" # Log in to ECR - aws ecr get-login-password --region "${ECR_REGION}" | docker login --username AWS --password-stdin "${ECR_URL}" From 5e771345001fa8e827a4d14f84a3fe1c4486d690 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 15 Sep 2025 13:39:22 -0700 Subject: [PATCH 186/217] fix: reduce cpu size for listings import --- cdk/lib/doorway_environment/services/doorway-import-listings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/services/doorway-import-listings.ts b/cdk/lib/doorway_environment/services/doorway-import-listings.ts index c2abd04a8e3..d35dfab2e9b 100644 --- a/cdk/lib/doorway_environment/services/doorway-import-listings.ts +++ b/cdk/lib/doorway_environment/services/doorway-import-listings.ts @@ -37,7 +37,7 @@ export class DoorwayImportListings { const importListings = new DoorwayEcsTask(scope, `doorway-import-listings-${props.environment}`, { ...props, memory: 1024, - cpu: 1, + cpu: .5, instances: 1, port: 10246, secrets: secrets, From e37a0a69255d0e4bb0617bea4dc6373a216d2545 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 15 Sep 2025 14:27:01 -0700 Subject: [PATCH 187/217] fix: cpu definition and name --- .../doorway_environment/services/doorway-ecs-task.ts | 10 +++++----- .../services/doorway-import-listings.ts | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cdk/lib/doorway_environment/services/doorway-ecs-task.ts b/cdk/lib/doorway_environment/services/doorway-ecs-task.ts index 6af13b8ac39..78ac62d16fe 100644 --- a/cdk/lib/doorway_environment/services/doorway-ecs-task.ts +++ b/cdk/lib/doorway_environment/services/doorway-ecs-task.ts @@ -21,9 +21,9 @@ export class DoorwayEcsTask { image: ContainerImage.fromRegistry( `${Aws.ACCOUNT_ID}.dkr.ecr.${Aws.REGION}.amazonaws.com/${props.container}`, ), - cpu: props.cpu, - memoryLimitMiB: props.memory, - essential: true, + cpu: Number(props.cpu * 1024), + memoryLimitMiB: Number(props.memory), + essential: Boolean(true), logging: LogDrivers.awsLogs({ streamPrefix: id, logGroup: props.logGroup, @@ -34,10 +34,10 @@ export class DoorwayEcsTask { portMappings: [ { name: `${id}-port-mapping`, - containerPort: props.port, + containerPort: Number(props.port), protocol: Protocol.TCP, appProtocol: AppProtocol.http, - hostPort: props.port, + hostPort: Number(props.port), }, ], diff --git a/cdk/lib/doorway_environment/services/doorway-import-listings.ts b/cdk/lib/doorway_environment/services/doorway-import-listings.ts index d35dfab2e9b..d7220ac511c 100644 --- a/cdk/lib/doorway_environment/services/doorway-import-listings.ts +++ b/cdk/lib/doorway_environment/services/doorway-import-listings.ts @@ -80,6 +80,7 @@ export class DoorwayImportListings { }) this.schedule = new Schedule(scope, `import-listings - schedule - ${props.environment} `, { + scheduleName: `import-listings-schedule-${props.environment}`, target: target, schedule: ScheduleExpression.rate(Duration.minutes(30)) From 02395eba87308b0aebaf1c1803adc702dbb1fb39 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 15 Sep 2025 16:17:02 -0700 Subject: [PATCH 188/217] feat: adding jsdoc comments --- .gitignore | 1 + cdk/jsdoc.json | 8 +- .../services/doorway-backend-service.ts | 5 + .../services/doorway-import-listings.ts | 33 +- .../services/doorway-partners-site.ts | 5 + .../services/doorway-public-site.ts | 9 +- .../services/doorway_service.ts | 20 +- cdk/package.json | 1 + cdk/yarn.lock | 805 +++++++++++++++++- 9 files changed, 851 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 6fb03404e16..6759baa1e7d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ logs npm-debug.log* yarn-debug.log* yarn-error.log* +docs # Next.js cache and compiled artifacts .next/ diff --git a/cdk/jsdoc.json b/cdk/jsdoc.json index d433fafa3bd..16bf6bba43f 100644 --- a/cdk/jsdoc.json +++ b/cdk/jsdoc.json @@ -1,17 +1,21 @@ { "plugins": [ "plugins/markdown", - "node_modules/jsdoc-plugin-typescript" + "better-docs/typescript" ], "source": { "include": [ "./lib", - "./bin", "./README.md" ], "includePattern": ".+\\.(js|jsx|ts|tsx)$", "excludePattern": "(^|\\/|\\\\)_" }, + "opts": { + "destination": "./docs/", + "recurse": true + }, + "templates": { "cleverLinks": false, "monospaceLinks": false, diff --git a/cdk/lib/doorway_environment/services/doorway-backend-service.ts b/cdk/lib/doorway_environment/services/doorway-backend-service.ts index 55a665d5c4c..e5ed07e1a57 100644 --- a/cdk/lib/doorway_environment/services/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/services/doorway-backend-service.ts @@ -10,6 +10,11 @@ import { Construct } from "constructs" import { DoorwayProps } from "../doorway-props" import { DoorwayService } from "./doorway_service" +/** + * The Backend API Service for Doorway + * @class + * @see DoorwayService + */ export class DoorwayBackendService { public service: FargateService public constructor(scope: Construct, id: string, props: DoorwayProps) { diff --git a/cdk/lib/doorway_environment/services/doorway-import-listings.ts b/cdk/lib/doorway_environment/services/doorway-import-listings.ts index d7220ac511c..b733a0d3173 100644 --- a/cdk/lib/doorway_environment/services/doorway-import-listings.ts +++ b/cdk/lib/doorway_environment/services/doorway-import-listings.ts @@ -1,3 +1,4 @@ +/** @fileoverview Doorway Import Listings scheduled task configuration */ import { Duration, Fn } from "aws-cdk-lib"; import { ISecurityGroup, ISubnet, SecurityGroup, Subnet } from "aws-cdk-lib/aws-ec2"; import { Cluster, Secret } from "aws-cdk-lib/aws-ecs"; @@ -10,7 +11,28 @@ import { Construct } from "constructs"; import { DoorwayProps } from "../doorway-props"; import { DoorwayEcsTask } from "./doorway-ecs-task"; +/** + * Creates a new DoorwayImportListings construct. + * + * @param scope - The parent construct (typically a Stack or Stage) + * @param id - Unique identifier for this construct within the scope + * @param props - Configuration properties including VPC, environment, and cluster settings + * @param props.environment - Deployment environment (e.g., 'staging', 'production') + * @param props.vpc - VPC where the ECS task will run + * @param props.clusterName - Name of the existing ECS cluster to use + */ export class DoorwayImportListings { + /** + * + * It sets up an EventBridge schedule that triggers the import listings ECS task every 30 minutes. + * Configured to run the task in private subnets with database access for importing + * housing listings from San Jose, San Mateo, and Alameda jurisdictions. + * + * @public + * @property {Schedule} + * @see {@link https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_scheduler.Schedule.html | AWS CDK Schedule} + * @see {@link https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_scheduler_targets.EcsRunFargateTask.html | EcsRunFargateTask} + */ public schedule: Schedule constructor(scope: Construct, id: string, props: DoorwayProps) { const executionRole = new Role(scope, `executionRole-${id}`, { @@ -32,8 +54,6 @@ export class DoorwayImportListings { `appTierPrivateSG-${id}`, appTierPrivateSGId, ) - - const importListings = new DoorwayEcsTask(scope, `doorway-import-listings-${props.environment}`, { ...props, memory: 1024, @@ -53,14 +73,10 @@ export class DoorwayImportListings { securityGroup: privateSG, serviceName: `doorway-import-listings-${props.environment}`, clusterName: props.clusterName, - - }) const cluster = Cluster.fromClusterAttributes(scope, `import-listings-cluster-${props.environment}`, { clusterName: props.clusterName, vpc: props.vpc - - }) // Get the subnets const appSubnetIds: string[] = [] @@ -77,14 +93,11 @@ export class DoorwayImportListings { vpcSubnets: { subnets: appsubnets, }, - }) - this.schedule = new Schedule(scope, `import-listings - schedule - ${props.environment} `, { + this.schedule = new Schedule(scope, `import-listings-schedule-${props.environment} `, { scheduleName: `import-listings-schedule-${props.environment}`, target: target, schedule: ScheduleExpression.rate(Duration.minutes(30)) - - }) } } \ No newline at end of file diff --git a/cdk/lib/doorway_environment/services/doorway-partners-site.ts b/cdk/lib/doorway_environment/services/doorway-partners-site.ts index 0b782052e48..54853892bc8 100644 --- a/cdk/lib/doorway_environment/services/doorway-partners-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-partners-site.ts @@ -9,6 +9,11 @@ import { Construct } from "constructs" import { DoorwayProps } from "../doorway-props" import { DoorwayService } from "./doorway_service" +/** + * Builds the Doorway Partners Site + * @class + * @see DoorwayService + */ export class DoorwayPartnersSite { public service: FargateService constructor(scope: Construct, id: string, props: DoorwayProps) { diff --git a/cdk/lib/doorway_environment/services/doorway-public-site.ts b/cdk/lib/doorway_environment/services/doorway-public-site.ts index 654acc7550f..d2ae2b211f2 100644 --- a/cdk/lib/doorway_environment/services/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-public-site.ts @@ -9,6 +9,11 @@ import { Construct } from "constructs" import { DoorwayProps } from "../doorway-props" import { DoorwayService } from "./doorway_service" +/** + * Builds the Doorway Public Site + * @class + * @see DoorwayService + */ export class DoorwayPublicSite { public service: FargateService constructor(scope: Construct, id: string, props: DoorwayProps) { @@ -16,16 +21,12 @@ export class DoorwayPublicSite { const executionRole = new Role(scope, `executionRole-${id}`, { assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), }) - const publicUploads = Bucket.fromBucketName( scope, `publicUploadsBucket-${id}`, `doorway-public-uploads-${props.environment}`, ) - - - const secureUploads = Bucket.fromBucketArn( scope, `secureUploadsBucket-${id}`, diff --git a/cdk/lib/doorway_environment/services/doorway_service.ts b/cdk/lib/doorway_environment/services/doorway_service.ts index aa4f6498739..a4d2d37748a 100644 --- a/cdk/lib/doorway_environment/services/doorway_service.ts +++ b/cdk/lib/doorway_environment/services/doorway_service.ts @@ -10,14 +10,21 @@ import { DoorwayServiceProps } from "../doorway-props" import { RestartServicesLambda } from "../utility_lambdas/restart-services-lambda" import { DoorwayEcsTask } from "./doorway-ecs-task" +/** + * @class + * Base Class for Doorway Services (API, Public, Partners, etc) + * This really shouldn't change all that much. Most configuration settings are in the classes that use this. + * @see DoorwayBackendService + * @see DoorwayPublicSite + * @see DoorwayPartnersSite + * @see DoorwayImportListings + */ export class DoorwayService { public service: FargateService constructor(scope: Construct, id: string, props: DoorwayServiceProps) { // Initial Execution Role Setup // Create the execution role for the doorway API service - - props.executionRole.addManagedPolicy( ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy"), ) @@ -39,9 +46,11 @@ export class DoorwayService { appSubnetIds.forEach((id) => { appsubnets.push(Subnet.fromSubnetId(scope, id, id)) }) + + // Set up the ECS Task const task = new DoorwayEcsTask(scope, id, props).task + // Set up the fargate service this.service = new FargateService(scope, `${id}-fargate-service`, { - taskDefinition: task, serviceName: props.serviceName, circuitBreaker: { @@ -57,9 +66,10 @@ export class DoorwayService { }, securityGroups: [props.securityGroup], desiredCount: props.instances, - - }) + // If the service has secrets associated with it, + // create a lambda to restart the service when the secrets are updated + const secrets = Object.keys(props.secrets) if (Object.keys(props.secrets).length > 0) { new RestartServicesLambda(scope, `restart-${id}-${props.environment}`, { service: this.service, diff --git a/cdk/package.json b/cdk/package.json index f2b5c62c378..5219ab92965 100644 --- a/cdk/package.json +++ b/cdk/package.json @@ -15,6 +15,7 @@ "@types/jest": "^29.5.14", "@types/node": "22.7.9", "aws-cdk": "2.1024.0", + "better-docs": "^2.7.3", "jest": "^29.7.0", "jsdoc": "^4.0.4", "jsdoc-plugin-typescript": "^3.2.0", diff --git a/cdk/yarn.lock b/cdk/yarn.lock index 6796bcc3c41..c7c55e318f5 100644 --- a/cdk/yarn.lock +++ b/cdk/yarn.lock @@ -798,7 +798,28 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.28.3", "@babel/generator@^7.7.2": +"@babel/core@^7.7.5": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.4.tgz#12a550b8794452df4c8b084f95003bce1742d496" + integrity sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.28.3" + "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-module-transforms" "^7.28.3" + "@babel/helpers" "^7.28.4" + "@babel/parser" "^7.28.4" + "@babel/template" "^7.27.2" + "@babel/traverse" "^7.28.4" + "@babel/types" "^7.28.4" + "@jridgewell/remapping" "^2.3.5" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.12.11", "@babel/generator@^7.28.3", "@babel/generator@^7.7.2": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.3.tgz#9626c1741c650cbac39121694a0f2d7451b8ef3e" integrity sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw== @@ -870,6 +891,14 @@ "@babel/template" "^7.27.2" "@babel/types" "^7.28.2" +"@babel/helpers@^7.28.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827" + integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w== + dependencies: + "@babel/template" "^7.27.2" + "@babel/types" "^7.28.4" + "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.15", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.27.2", "@babel/parser@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.3.tgz#d2d25b814621bca5fe9d172bc93792547e7a2a71" @@ -877,6 +906,13 @@ dependencies: "@babel/types" "^7.28.2" +"@babel/parser@^7.2.3", "@babel/parser@^7.28.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.4.tgz#da25d4643532890932cc03f7705fe19637e03fa8" + integrity sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg== + dependencies: + "@babel/types" "^7.28.4" + "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -996,6 +1032,11 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" +"@babel/runtime@^7.7.6": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" + integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== + "@babel/template@^7.27.2", "@babel/template@^7.3.3": version "7.27.2" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" @@ -1005,6 +1046,19 @@ "@babel/parser" "^7.27.2" "@babel/types" "^7.27.1" +"@babel/traverse@^7.1.6", "@babel/traverse@^7.28.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.4.tgz#8d456101b96ab175d487249f60680221692b958b" + integrity sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.28.3" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.28.4" + "@babel/template" "^7.27.2" + "@babel/types" "^7.28.4" + debug "^4.3.1" + "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.3.tgz#6911a10795d2cce43ec6a28cffc440cca2593434" @@ -1026,6 +1080,14 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" +"@babel/types@^7.2.0", "@babel/types@^7.28.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.4.tgz#0a4e618f4c60a7cd6c11cb2d48060e4dbe38ac3a" + integrity sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + "@balena/dockerignore@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@balena/dockerignore/-/dockerignore-1.0.2.tgz#9ffe4726915251e8eb69f44ef3547e0da2c03e0d" @@ -1259,6 +1321,14 @@ "@jridgewell/sourcemap-codec" "^1.5.0" "@jridgewell/trace-mapping" "^0.3.24" +"@jridgewell/remapping@^2.3.5": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" + integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" @@ -1788,6 +1858,11 @@ resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.152.tgz#f68424a8175f0a54a2a941e65b76c3f51f3bd89d" integrity sha512-soT/c2gYBnT5ygwiHPmd9a1bftj462NWVk2tKCc1PYHSIacB2UwbTS2zYG4jzag1mRDuzg/OjtxQjQ2NKRB6Rw== +"@types/babel-types@*", "@types/babel-types@^7.0.0": + version "7.0.16" + resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.16.tgz#74916c1b7a6bd53dc3d3f4053b65126bcc5e8e6f" + integrity sha512-5QXs9GBFTNTmilLlWBhnsprqpjfrotyrnzUdwDrywEL/DA4LuCWQT300BTOXA3Y9ngT9F2uvmCoIxI6z8DlJEA== + "@types/babel__core@^7.1.14": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" @@ -1821,6 +1896,13 @@ dependencies: "@babel/types" "^7.28.2" +"@types/babylon@^6.16.2": + version "6.16.9" + resolved "https://registry.yarnpkg.com/@types/babylon/-/babylon-6.16.9.tgz#7abf03f6591a921fe3171af91433077cd2666e36" + integrity sha512-sEKyxMVEowhcr8WLfN0jJYe4gS4Z9KC2DGz0vqfC7+MXFbmvOF7jSjALC77thvAO2TLgFUPa9vDeOak+AcUrZA== + dependencies: + "@types/babel-types" "*" + "@types/graceful-fs@^4.1.3": version "4.1.9" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" @@ -1909,6 +1991,18 @@ dependencies: "@types/yargs-parser" "*" +ace-builds@^1.4.13: + version "1.43.3" + resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.43.3.tgz#ca4120fa763827dfd53c2b65a2d6b8c86f531928" + integrity sha512-MCl9rALmXwIty/4Qboijo/yNysx1r6hBTzG+6n/TiOm5LFhZpEvEIcIITPFiEOEFDfgBOEmxu+a4f54LEFM6Sg== + +acorn-globals@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf" + integrity sha512-uWttZCk96+7itPxK8xCzY86PnxKTMrReKDqrHzv42VQY0K30PUO8WY13WMOuI+cOdX4EIdzdvQ8k6jkuGRFMYw== + dependencies: + acorn "^4.0.4" + acorn-walk@^8.1.1: version "8.3.4" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" @@ -1916,6 +2010,16 @@ acorn-walk@^8.1.1: dependencies: acorn "^8.11.0" +acorn@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + integrity sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw== + +acorn@^4.0.4, acorn@~4.0.2: + version "4.0.13" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" + integrity sha512-fu2ygVGuMmlzG8ZeRJ0bvR41nsAkxxhbyk8bZ1SS521Z7vmgJFTQQlfz/Mp/nJexGBz+v8sC9bM6+lNgskt4Ug== + acorn@^8.11.0, acorn@^8.4.1: version "8.15.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" @@ -1931,6 +2035,15 @@ ajv@^8.0.1: json-schema-traverse "^1.0.0" require-from-string "^2.0.2" +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + integrity sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg== + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + ansi-escapes@^4.2.1: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -2001,6 +2114,23 @@ arraybuffer.prototype.slice@^1.0.4: get-intrinsic "^1.2.6" is-array-buffer "^3.0.4" +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +ast-types@0.12.4, ast-types@^0.12.2: + version "0.12.4" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.12.4.tgz#71ce6383800f24efc9a1a3308f3a6e420a0974d1" + integrity sha512-ky/YVYCbtVAS8TdMIaTiPFHwEpRB5z1hctepJplTr3UW5q8TDrpIMCILyk8pmLxGtn2KCtC/lSn7zOsaI7nzDw== + +ast-types@^0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" + integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== + dependencies: + tslib "^2.0.1" + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -2108,11 +2238,48 @@ babel-preset-jest@^29.6.3: babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" +babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g== + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g== + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +better-docs@^2.7.3: + version "2.7.3" + resolved "https://registry.yarnpkg.com/better-docs/-/better-docs-2.7.3.tgz#bdeec1b24514bc22562af2a277c2cf527abbe726" + integrity sha512-OEk9e7RQUQbo1DmVo0mdsALZ+mT0SwIen7/DGnN4xKNktXKgz1j7+KQ2pObNHHVSFGu8YMQW/Ig1v6eiTkdnbw== + dependencies: + brace "^0.11.1" + react-ace "^9.5.0" + react-docgen "^5.4.0" + react-frame-component "^5.2.1" + typescript "^4.5.4" + underscore "^1.13.2" + vue-docgen-api "^3.26.0" + vue2-ace-editor "^0.0.15" + bluebird@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -2131,6 +2298,11 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace@^0.11.0, brace@^0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/brace/-/brace-0.11.1.tgz#4896fcc9d544eef45f4bb7660db320d3b379fe58" + integrity sha512-Fc8Ne62jJlKHiG/ajlonC4Sd66Pq68fFwK4ihJGNZpGqboc324SQk+lRvMzpPRuJOmfrJefdG8/7JdWX4bzJ2Q== + braces@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -2167,6 +2339,24 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +c8@^7.6.0: + version "7.14.0" + resolved "https://registry.yarnpkg.com/c8/-/c8-7.14.0.tgz#f368184c73b125a80565e9ab2396ff0be4d732f3" + integrity sha512-i04rtkkcNcCf7zsQcSv/T9EbUn4RXQ6mropeMcjFOsQXQ0iGLAr/xT6TImQg4+U9hmNpN9XdvPkjUL1IzbgxJw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@istanbuljs/schema" "^0.1.3" + find-up "^5.0.0" + foreground-child "^2.0.0" + istanbul-lib-coverage "^3.2.0" + istanbul-lib-report "^3.0.0" + istanbul-reports "^3.1.4" + rimraf "^3.0.2" + test-exclude "^6.0.0" + v8-to-istanbul "^9.0.0" + yargs "^16.2.0" + yargs-parser "^20.2.9" + call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" @@ -2198,6 +2388,11 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + integrity sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g== + camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -2225,6 +2420,14 @@ catharsis@^0.9.0: dependencies: lodash "^4.17.15" +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + integrity sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ== + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + chalk@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -2238,6 +2441,13 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +character-parser@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/character-parser/-/character-parser-2.2.0.tgz#c7ce28f36d4bcd9744e5ffc2c5fcde1c73261fc0" + integrity sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw== + dependencies: + is-regex "^1.0.3" + ci-info@^3.2.0: version "3.9.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" @@ -2248,6 +2458,31 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz#0f79731eb8cfe1ec72acd4066efac9d61991b00d" integrity sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q== +clean-css@^4.1.11: + version "4.2.4" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" + integrity sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A== + dependencies: + source-map "~0.6.0" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + integrity sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA== + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + cliui@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" @@ -2279,11 +2514,26 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +commander@^2.19.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +constantinople@^3.0.1, constantinople@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/constantinople/-/constantinople-3.1.2.tgz#d45ed724f57d3d10500017a7d3a889c1381ae647" + integrity sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw== + dependencies: + "@types/babel-types" "^7.0.0" + "@types/babylon" "^6.16.2" + babel-types "^6.26.0" + babylon "^6.18.0" + constructs@^10.0.0: version "10.4.2" resolved "https://registry.yarnpkg.com/constructs/-/constructs-10.4.2.tgz#e875a78bef932cca12ea63965969873a25c1c132" @@ -2294,6 +2544,11 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +core-js@^2.4.0: + version "2.6.12" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" + integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== + create-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" @@ -2312,7 +2567,7 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.3: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -2348,6 +2603,11 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" +de-indent@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" + integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg== + debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.4.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" @@ -2355,6 +2615,11 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: dependencies: ms "^2.1.3" +decamelize@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + dedent@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.6.0.tgz#79d52d6389b1ffa67d2bcef59ba51847a9d503b2" @@ -2388,6 +2653,11 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +diff-match-patch@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37" + integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw== + diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -2398,6 +2668,18 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +doctypes@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" + integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== + dotenv@^17.2.1: version "17.2.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.2.1.tgz#6f32e10faf014883515538dc922a0fb8765d9b32" @@ -2545,11 +2827,25 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -esprima@^4.0.0: +esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +estree-to-babel@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/estree-to-babel/-/estree-to-babel-3.2.1.tgz#82e78315275c3ca74475fdc8ac1a5103c8a75bf5" + integrity sha512-YNF+mZ/Wu2FU/gvmzuWtYc8rloubL7wfXCTgouFrnjGVXPA/EeYYA7pupXWrb3Iv1cTBeSSxxJIbK23l4MRNqg== + dependencies: + "@babel/traverse" "^7.1.6" + "@babel/types" "^7.2.0" + c8 "^7.6.0" + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -2625,6 +2921,14 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + for-each@^0.3.3, for-each@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" @@ -2632,6 +2936,14 @@ for-each@^0.3.3, for-each@^0.3.5: dependencies: is-callable "^1.2.7" +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^3.0.2" + fs-extra@^11.3.0: version "11.3.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.1.tgz#ba7a1f97a85f94c6db2e52ff69570db3671d5a74" @@ -2809,6 +3121,11 @@ has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" +hash-sum@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04" + integrity sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA== + hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" @@ -2816,6 +3133,11 @@ hasown@^2.0.2: dependencies: function-bind "^1.1.2" +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -2906,6 +3228,11 @@ is-boolean-object@^1.2.1: call-bound "^1.0.3" has-tostringtag "^1.0.2" +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" @@ -2935,6 +3262,14 @@ is-date-object@^1.0.5, is-date-object@^1.1.0: call-bound "^1.0.2" has-tostringtag "^1.0.2" +is-expression@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-expression/-/is-expression-3.0.0.tgz#39acaa6be7fd1f3471dc42c7416e61c24317ac9f" + integrity sha512-vyMeQMq+AiH5uUnoBfMTwf18tO3bM6k1QXBE9D6ueAAquEfCZe3AJPtud9g6qS0+4X8xA7ndpZiDyeb2l2qOBw== + dependencies: + acorn "~4.0.2" + object-assign "^4.0.1" + is-finalizationregistry@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" @@ -2985,7 +3320,12 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-regex@^1.2.1: +is-promise@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-regex@^1.0.3, is-regex@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== @@ -3119,6 +3459,14 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +istanbul-reports@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93" + integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + jest-changed-files@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" @@ -3477,7 +3825,12 @@ jest@^29.7.0: import-local "^3.0.2" jest-cli "^29.7.0" -js-tokens@^4.0.0: +js-stringify@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" + integrity sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -3564,6 +3917,21 @@ jsonschema@~1.4.1: resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== +jstransformer@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3" + integrity sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A== + dependencies: + is-promise "^2.0.0" + promise "^7.0.1" + +kind-of@^3.0.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== + dependencies: + is-buffer "^1.1.5" + klaw@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/klaw/-/klaw-3.0.0.tgz#b11bec9cf2492f06756d6e809ab73a2910259146" @@ -3576,6 +3944,11 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + integrity sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ== + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -3600,6 +3973,23 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -3610,11 +4000,31 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== -lodash@^4.17.15, lodash@^4.17.21: +lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + integrity sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg== + +loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -3703,7 +4113,12 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -3730,11 +4145,18 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -neo-async@^2.6.2: +neo-async@^2.6.1, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +node-dir@^0.1.10: + version "0.1.17" + resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + integrity sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg== + dependencies: + minimatch "^3.0.2" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -3757,6 +4179,11 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + object-inspect@^1.13.3, object-inspect@^1.13.4: version "1.13.4" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" @@ -3809,7 +4236,7 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.1.0: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -3823,6 +4250,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -3894,6 +4328,18 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" +private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + +promise@^7.0.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + prompts@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -3902,6 +4348,125 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" +prop-types@^15.7.2: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== + +pug-attrs@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-2.0.4.tgz#b2f44c439e4eb4ad5d4ef25cac20d18ad28cc336" + integrity sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ== + dependencies: + constantinople "^3.0.1" + js-stringify "^1.0.1" + pug-runtime "^2.0.5" + +pug-code-gen@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-2.0.3.tgz#122eb9ada9b5bf601705fe15aaa0a7d26bc134ab" + integrity sha512-r9sezXdDuZJfW9J91TN/2LFbiqDhmltTFmGpHTsGdrNGp3p4SxAjjXEfnuK2e4ywYsRIVP0NeLbSAMHUcaX1EA== + dependencies: + constantinople "^3.1.2" + doctypes "^1.1.0" + js-stringify "^1.0.1" + pug-attrs "^2.0.4" + pug-error "^1.3.3" + pug-runtime "^2.0.5" + void-elements "^2.0.1" + with "^5.0.0" + +pug-error@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-1.3.3.tgz#f342fb008752d58034c185de03602dd9ffe15fa6" + integrity sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ== + +pug-filters@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pug-filters/-/pug-filters-3.1.1.tgz#ab2cc82db9eeccf578bda89130e252a0db026aa7" + integrity sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg== + dependencies: + clean-css "^4.1.11" + constantinople "^3.0.1" + jstransformer "1.0.0" + pug-error "^1.3.3" + pug-walk "^1.1.8" + resolve "^1.1.6" + uglify-js "^2.6.1" + +pug-lexer@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-4.1.0.tgz#531cde48c7c0b1fcbbc2b85485c8665e31489cfd" + integrity sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA== + dependencies: + character-parser "^2.1.1" + is-expression "^3.0.0" + pug-error "^1.3.3" + +pug-linker@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-3.0.6.tgz#f5bf218b0efd65ce6670f7afc51658d0f82989fb" + integrity sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg== + dependencies: + pug-error "^1.3.3" + pug-walk "^1.1.8" + +pug-load@^2.0.12: + version "2.0.12" + resolved "https://registry.yarnpkg.com/pug-load/-/pug-load-2.0.12.tgz#d38c85eb85f6e2f704dea14dcca94144d35d3e7b" + integrity sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg== + dependencies: + object-assign "^4.1.0" + pug-walk "^1.1.8" + +pug-parser@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pug-parser/-/pug-parser-5.0.1.tgz#03e7ada48b6840bd3822f867d7d90f842d0ffdc9" + integrity sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA== + dependencies: + pug-error "^1.3.3" + token-stream "0.0.1" + +pug-runtime@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-2.0.5.tgz#6da7976c36bf22f68e733c359240d8ae7a32953a" + integrity sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw== + +pug-strip-comments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz#cc1b6de1f6e8f5931cf02ec66cdffd3f50eaf8a8" + integrity sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw== + dependencies: + pug-error "^1.3.3" + +pug-walk@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-1.1.8.tgz#b408f67f27912f8c21da2f45b7230c4bd2a5ea7a" + integrity sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA== + +pug@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pug/-/pug-2.0.4.tgz#ee7682ec0a60494b38d48a88f05f3b0ac931377d" + integrity sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw== + dependencies: + pug-code-gen "^2.0.2" + pug-filters "^3.1.1" + pug-lexer "^4.1.0" + pug-linker "^3.0.6" + pug-load "^2.0.12" + pug-parser "^5.0.1" + pug-runtime "^2.0.5" + pug-strip-comments "^1.0.4" + punycode.js@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" @@ -3917,11 +4482,58 @@ pure-rand@^6.0.0: resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== +react-ace@^9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-9.5.0.tgz#b6c32b70d404dd821a7e01accc2d76da667ff1f7" + integrity sha512-4l5FgwGh6K7A0yWVMQlPIXDItM4Q9zzXRqOae8KkCl6MkOob7sC1CzHxZdOGvV+QioKWbX2p5HcdOVUv6cAdSg== + dependencies: + ace-builds "^1.4.13" + diff-match-patch "^1.0.5" + lodash.get "^4.4.2" + lodash.isequal "^4.5.0" + prop-types "^15.7.2" + +react-docgen@^5.4.0: + version "5.4.3" + resolved "https://registry.yarnpkg.com/react-docgen/-/react-docgen-5.4.3.tgz#7d297f73b977d0c7611402e5fc2a168acf332b26" + integrity sha512-xlLJyOlnfr8lLEEeaDZ+X2J/KJoe6Nr9AzxnkdQWush5hz2ZSu66w6iLMOScMmxoSHWpWMn+k3v5ZiyCfcWsOA== + dependencies: + "@babel/core" "^7.7.5" + "@babel/generator" "^7.12.11" + "@babel/runtime" "^7.7.6" + ast-types "^0.14.2" + commander "^2.19.0" + doctrine "^3.0.0" + estree-to-babel "^3.1.0" + neo-async "^2.6.1" + node-dir "^0.1.10" + strip-indent "^3.0.0" + +react-frame-component@^5.2.1: + version "5.2.7" + resolved "https://registry.yarnpkg.com/react-frame-component/-/react-frame-component-5.2.7.tgz#e31c0943be95fdf667c59d6d7fcf18c1dda4d4b2" + integrity sha512-ROjHtSLoSVYUBfTieazj/nL8jIX9rZFmHC0yXEU+dx6Y82OcBEGgU9o7VyHMrBFUN9FuQ849MtIPNNLsb4krbg== + +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + react-is@^18.0.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== +recast@^0.17.3: + version "0.17.6" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.17.6.tgz#64ae98d0d2dfb10ff92ff5fb9ffb7371823b69fa" + integrity sha512-yoQRMRrK1lszNtbkGyM4kN45AwylV5hMiuEveUBlxytUViWevjvX6w+tzJt1LH4cfUhWt4NZvy3ThIhu6+m5wQ== + dependencies: + ast-types "0.12.4" + esprima "~4.0.0" + private "^0.1.8" + source-map "~0.6.1" + reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" @@ -3936,6 +4548,11 @@ reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: get-proto "^1.0.1" which-builtin-type "^1.2.1" +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + regexp.prototype.flags@^1.5.3, regexp.prototype.flags@^1.5.4: version "1.5.4" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" @@ -3948,6 +4565,11 @@ regexp.prototype.flags@^1.5.3, regexp.prototype.flags@^1.5.4: gopd "^1.2.0" set-function-name "^2.0.2" +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -3982,7 +4604,7 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== -resolve@^1.20.0: +resolve@^1.1.6, resolve@^1.20.0: version "1.22.10" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== @@ -3991,6 +4613,20 @@ resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + integrity sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg== + dependencies: + align-text "^0.1.1" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + safe-array-concat@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" @@ -4112,7 +4748,7 @@ side-channel@^1.1.0: side-channel-map "^1.0.1" side-channel-weakmap "^1.0.2" -signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -4144,11 +4780,16 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0, source-map@^0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +source-map@~0.5.1: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -4254,6 +4895,13 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -4308,6 +4956,11 @@ tmpl@1.0.5: resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -4315,6 +4968,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +token-stream@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-0.0.1.tgz#ceeefc717a76c4316f126d0b9dbaa55d7e7df01a" + integrity sha512-nfjOAu/zAWmX9tgwi5NRp7O7zTDUD1miHiB40klUnAh9qnL1iXdgzcz/i5dMaL5jahcBAaSfmNOBBJBLJW8TEg== + ts-jest@^29.2.5: version "29.4.1" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.1.tgz#42d33beb74657751d315efb9a871fe99e3b9b519" @@ -4330,6 +4988,11 @@ ts-jest@^29.2.5: type-fest "^4.41.0" yargs-parser "^21.1.1" +ts-map@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ts-map/-/ts-map-1.0.3.tgz#1c4d218dec813d2103b7e04e4bcf348e1471c1ff" + integrity sha512-vDWbsl26LIcPGmDpoVzjEP6+hvHZkBkLW7JpvwbCv/5IYPJlsbzCVXY3wsCeAxAUeTclNOUZxnLdGh3VBD/J6w== + ts-node@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" @@ -4349,7 +5012,7 @@ ts-node@^10.9.2: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tslib@^2.6.2: +tslib@^2.0.1, tslib@^2.6.2: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -4414,6 +5077,16 @@ typed-array-length@^1.0.7: possible-typed-array-names "^1.0.0" reflect.getprototypeof "^1.0.6" +typescript@^3.2.2: + version "3.9.10" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" + integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== + +typescript@^4.5.4: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + typescript@~5.6.3: version "5.6.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" @@ -4424,11 +5097,26 @@ uc.micro@^2.0.0, uc.micro@^2.1.0: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== +uglify-js@^2.6.1: + version "2.8.29" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" + integrity sha512-qLq/4y2pjcU3vhlhseXGGJ7VbFO4pBANu0kwl8VCa9KEI0V8VfZIx2Fy3w01iSTA/pGwKZSmu/+I4etLNDdt5w== + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + uglify-js@^3.1.4: version "3.19.3" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + integrity sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q== + unbox-primitive@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" @@ -4439,7 +5127,7 @@ unbox-primitive@^1.1.0: has-symbols "^1.1.0" which-boxed-primitive "^1.1.1" -underscore@~1.13.2: +underscore@^1.13.2, underscore@~1.13.2: version "1.13.7" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.7.tgz#970e33963af9a7dda228f17ebe8399e5fbe63a10" integrity sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== @@ -4477,7 +5165,7 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-to-istanbul@^9.0.1: +v8-to-istanbul@^9.0.0, v8-to-istanbul@^9.0.1: version "9.3.0" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== @@ -4486,6 +5174,42 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^2.0.0" +void-elements@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== + +vue-docgen-api@^3.26.0: + version "3.26.0" + resolved "https://registry.yarnpkg.com/vue-docgen-api/-/vue-docgen-api-3.26.0.tgz#2afc6a39e72862fbbc60ceb8510c681749f05460" + integrity sha512-ujdg4i5ZI/wE46RZQMFzKnDGyhEuPCu+fMA86CAd9EIek/6+OqraSVBm5ZkLrbEd5f8xxdnqMU4yiSGHHeao/Q== + dependencies: + "@babel/parser" "^7.2.3" + "@babel/types" "^7.0.0" + ast-types "^0.12.2" + hash-sum "^1.0.2" + lru-cache "^4.1.5" + pug "^2.0.3" + recast "^0.17.3" + ts-map "^1.0.3" + typescript "^3.2.2" + vue-template-compiler "^2.0.0" + +vue-template-compiler@^2.0.0: + version "2.7.16" + resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz#c81b2d47753264c77ac03b9966a46637482bb03b" + integrity sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ== + dependencies: + de-indent "^1.0.2" + he "^1.2.0" + +vue2-ace-editor@^0.0.15: + version "0.0.15" + resolved "https://registry.yarnpkg.com/vue2-ace-editor/-/vue2-ace-editor-0.0.15.tgz#569b208e54ae771ae1edd3b8902ac42f0edc74e3" + integrity sha512-e3TR9OGXc71cGpvYcW068lNpRcFt3+OONCC81oxHL/0vwl/V3OgqnNMw2/RRolgQkO/CA5AjqVHWmANWKOtNnQ== + dependencies: + brace "^0.11.0" + walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" @@ -4553,6 +5277,24 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + integrity sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg== + +with@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/with/-/with-5.1.1.tgz#fa4daa92daf32c4ea94ed453c81f04686b575dfe" + integrity sha512-uAnSsFGfSpF6DNhBXStvlZILfHJfJu4eUkfbRGk94kGO1Ta7bg6FwfvoOhhyHAJuFbCw+0xk4uJ3u57jLvlCJg== + dependencies: + acorn "^3.1.0" + acorn-globals "^3.0.0" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + integrity sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q== + wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" @@ -4590,6 +5332,11 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== + yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -4600,11 +5347,29 @@ yaml@1.10.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yargs-parser@^20.2.2, yargs-parser@^20.2.9: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yargs@^17.3.1: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" @@ -4618,6 +5383,16 @@ yargs@^17.3.1: y18n "^5.0.5" yargs-parser "^21.1.1" +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + integrity sha512-QFzUah88GAGy9lyDKGBqZdkYApt63rCXYBGYnEP4xDJPXNqXXnBDACnbrXnViV6jRSqAePwrATi2i8mfYm4L1A== + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" From e3c634a47974ce0728bbfe4105d25536de47de89 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 16 Sep 2025 09:23:53 -0700 Subject: [PATCH 189/217] fix: role for scheduler with run task grants --- .../services/doorway-import-listings.ts | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/cdk/lib/doorway_environment/services/doorway-import-listings.ts b/cdk/lib/doorway_environment/services/doorway-import-listings.ts index b733a0d3173..1c563389107 100644 --- a/cdk/lib/doorway_environment/services/doorway-import-listings.ts +++ b/cdk/lib/doorway_environment/services/doorway-import-listings.ts @@ -2,7 +2,7 @@ import { Duration, Fn } from "aws-cdk-lib"; import { ISecurityGroup, ISubnet, SecurityGroup, Subnet } from "aws-cdk-lib/aws-ec2"; import { Cluster, Secret } from "aws-cdk-lib/aws-ecs"; -import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"; +import { PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"; import { Schedule, ScheduleExpression } from "aws-cdk-lib/aws-scheduler"; import { EcsRunFargateTask } from "aws-cdk-lib/aws-scheduler-targets"; import * as secret from "aws-cdk-lib/aws-secretsmanager"; @@ -86,6 +86,25 @@ export class DoorwayImportListings { appSubnetIds.forEach((id) => { appsubnets.push(Subnet.fromSubnetId(scope, id, id)) }) + const schedulerRole = new Role(scope, `scheduler-role-${id}`, { + assumedBy: new ServicePrincipal("scheduler.amazonaws.com"), + }); + + schedulerRole.addToPolicy(new PolicyStatement({ + actions: [ + "ecs:RunTask", + "ecs:DescribeTaskDefinition", + "iam:PassRole" + ], + resources: ["*"] + })); + + // Allow passing the execution role to ECS + schedulerRole.addToPolicy(new PolicyStatement({ + actions: ["iam:PassRole"], + resources: [executionRole.roleArn] + })); + const target = new EcsRunFargateTask(cluster, { taskDefinition: importListings.task, assignPublicIp: false, @@ -93,8 +112,10 @@ export class DoorwayImportListings { vpcSubnets: { subnets: appsubnets, }, + role: schedulerRole, }) - this.schedule = new Schedule(scope, `import-listings-schedule-${props.environment} `, { + + this.schedule = new Schedule(scope, `import-listings-schedule-${props.environment}`, { scheduleName: `import-listings-schedule-${props.environment}`, target: target, schedule: ScheduleExpression.rate(Duration.minutes(30)) From 8c2218284b56e99b7c8f78dd0ba305fb761c0ddc Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 16 Sep 2025 09:57:20 -0700 Subject: [PATCH 190/217] fix: going with autogenerated schedule name --- cdk/lib/doorway_environment/services/doorway-import-listings.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/cdk/lib/doorway_environment/services/doorway-import-listings.ts b/cdk/lib/doorway_environment/services/doorway-import-listings.ts index 1c563389107..3e95ca90d1d 100644 --- a/cdk/lib/doorway_environment/services/doorway-import-listings.ts +++ b/cdk/lib/doorway_environment/services/doorway-import-listings.ts @@ -116,7 +116,6 @@ export class DoorwayImportListings { }) this.schedule = new Schedule(scope, `import-listings-schedule-${props.environment}`, { - scheduleName: `import-listings-schedule-${props.environment}`, target: target, schedule: ScheduleExpression.rate(Duration.minutes(30)) }) From d2af01ac692921586fe9e861309f69a532286c72 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 16 Sep 2025 11:21:49 -0700 Subject: [PATCH 191/217] feat: adding staging --- cdk/dev2.env | 17 ++++++-- .../doorway-build-pipeline-stack.ts | 41 +++++++++++++++++++ cdk/{staging3.env => staging.env} | 12 +++--- 3 files changed, 60 insertions(+), 10 deletions(-) rename cdk/{staging3.env => staging.env} (71%) diff --git a/cdk/dev2.env b/cdk/dev2.env index 5d2a6e78b27..4176119a7ca 100644 --- a/cdk/dev2.env +++ b/cdk/dev2.env @@ -23,6 +23,8 @@ DISABLE_CORS=TRUE SAME_SITE=true HTTPS_OFF=true +# Overriding prod infrastructure definitions for dev +# If you are going to do a performance test you can just comment these out and dev will become prod-like PARTNERS_PORTAL_PORT=3200 PARTNERS_PORTAL_DOMAIN=partners.dev2.housingbayarea.mtc.ca.gov @@ -30,8 +32,17 @@ BACKEND_API_DOMAIN=backend.dev2.housingbayarea.int BACKEND_API_BASE=http://backend.dev2.housingbayarea.int BACKEND_API_PORT=3100 BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVDELIVERY_API_URL -BACKEND_API_MEMORY=4096 -BACKEND_API_CPUS=2 -BACKEND_API_INSTANCES=3 + + +BACKEND_API_MEMORY=2048 +BACKEND_API_CPUS=1 +BACKEND_API_INSTANCES=1 +PARTNERS_PORTAL_MEMORY=2048 +PARTNERS_PORTAL_CPUS=1 +PARTNERS_PORTAL_INSTANCES=1 +PUBLIC_PORTAL_MEMORY=2048 +PUBLIC_PORTAL_CPUS=1 +PUBLIC_PORTAL_INSTANCES=1 + LOG_LEVEL=debug diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 6cf3e7650ca..1c0465646dc 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -166,6 +166,47 @@ export class DoorwayBuildPipelineStack extends Stack { stageName: "Dev", actions: [dbmigrate, ecsDeploy], }) + pipeline.addStage({ + stageName: "Build-Staging", + actions: [ + + new PartnersDockerBuild(this, `doorway-partners`, { + buildspec: "../ci/buildspec/build_partners.yml", + imageName: "partners", + source: sourceArtifact, + dockerHubSecret: dockerSecret, + buildRole: buildRole, + environment: "staging" + }).action, + new PublicDockerBuild(this, "doorway-public", { + buildspec: "../ci/buildspec/build_public.yml", + imageName: "public", + source: sourceArtifact, + dockerHubSecret: dockerSecret, + buildRole: buildRole, + environment: "staging" + }).action, + ], + }) + + const dbmigrateStaging = new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev", { + environment: "staging", + buildspec: "./ci/buildspec/migrate.yml", + source: sourceArtifact, + buildRole: buildRole, + }).action; + + const ecsDeployStaging = new DoorwayECSDeploy(this, "doorway-ecs-deploy-dev", { + buildspec: "./ci/buildspec/update_ecs.yml", + source: sourceArtifact, + buildRole: buildRole, + environment: "staging", + }).action + + pipeline.addStage({ + stageName: "Staging", + actions: [dbmigrateStaging, ecsDeployStaging], + }) } } export interface PipelineProps extends StackProps { diff --git a/cdk/staging3.env b/cdk/staging.env similarity index 71% rename from cdk/staging3.env rename to cdk/staging.env index 8e4f8009d86..66d29b6ef2f 100644 --- a/cdk/staging3.env +++ b/cdk/staging.env @@ -7,7 +7,7 @@ FEATURE_LISTINGS_APPROVAL=TRUE ECR_ACCOUNT_ID=364076391763 ECR_NAMESPACE=doorway ECR_REGION=us-west-2 -CORS_ORIGINS=https://staging3.housingbayarea.mtc.ca.gov, https://partners.staging3.housingbayarea.mtc.ca.gov +CORS_ORIGINS=https://staging2.housingbayarea.mtc.ca.gov, https://partners.staging2.housingbayarea.mtc.ca.gov USE_SECURE_DOWNLOAD_PATHWAY=TRUE GTM_KEY=GTM-KF22FJP @@ -17,7 +17,7 @@ CLOUDINARY_CLOUD_NAME=exygy APP_SECRET="" PUBLIC_PORTAL_PORT=3000 -PUBLIC_PORTAL_DOMAIN=staging3.housingbayarea.mtc.ca.gov +PUBLIC_PORTAL_DOMAIN=staging2.housingbayarea.mtc.ca.gov PUBLIC_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY DISABLE_CORS=FALSE SAME_SITE=true @@ -25,13 +25,11 @@ HTTPS_OFF=true PARTNERS_PORTAL_PORT=3200 -PARTNERS_PORTAL_DOMAIN=partners.staging3.housingbayarea.mtc.ca.gov +PARTNERS_PORTAL_DOMAIN=partners.staging2.housingbayarea.mtc.ca.gov BACKEND_API_DOMAIN=backend.staging3.housingbayarea.int -BACKEND_API_BASE=http://backend.staging3.housingbayarea.int +BACKEND_API_BASE=http://backend.staging2.housingbayarea.int BACKEND_API_PORT=3100 BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVDELIVERY_API_URL -BACKEND_API_MEMORY=4096 -BACKEND_API_CPUS=2 -BACKEND_API_INSTANCES=3 + LOG_LEVEL=info From 7edd079160a6515da7d398259c4f436d185763ff Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 16 Sep 2025 12:46:14 -0700 Subject: [PATCH 192/217] fix: fixing duplicate names --- .../doorway-build-pipeline-stack.ts | 12 +++---- .../doorway-database-migrate.ts | 8 ++--- .../build-pipeline/doorway-docker-build.ts | 34 +++++++++++++++---- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 1c0465646dc..47025717c5f 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -129,7 +129,7 @@ export class DoorwayBuildPipelineStack extends Stack { buildRole: buildRole, environment: "dev2" }).action, - new PartnersDockerBuild(this, `doorway-partners`, { + new PartnersDockerBuild(this, `doorway-partners-dev`, { buildspec: "../ci/buildspec/build_partners.yml", imageName: "partners", source: sourceArtifact, @@ -137,7 +137,7 @@ export class DoorwayBuildPipelineStack extends Stack { buildRole: buildRole, environment: "dev2" }).action, - new PublicDockerBuild(this, "doorway-public", { + new PublicDockerBuild(this, "doorway-public-dev", { buildspec: "../ci/buildspec/build_public.yml", imageName: "public", source: sourceArtifact, @@ -170,7 +170,7 @@ export class DoorwayBuildPipelineStack extends Stack { stageName: "Build-Staging", actions: [ - new PartnersDockerBuild(this, `doorway-partners`, { + new PartnersDockerBuild(this, `doorway-partners-staging`, { buildspec: "../ci/buildspec/build_partners.yml", imageName: "partners", source: sourceArtifact, @@ -178,7 +178,7 @@ export class DoorwayBuildPipelineStack extends Stack { buildRole: buildRole, environment: "staging" }).action, - new PublicDockerBuild(this, "doorway-public", { + new PublicDockerBuild(this, "doorway-public-staging", { buildspec: "../ci/buildspec/build_public.yml", imageName: "public", source: sourceArtifact, @@ -189,14 +189,14 @@ export class DoorwayBuildPipelineStack extends Stack { ], }) - const dbmigrateStaging = new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev", { + const dbmigrateStaging = new DoorwayDatabaseMigrate(this, "doorway-database-migrate-staging", { environment: "staging", buildspec: "./ci/buildspec/migrate.yml", source: sourceArtifact, buildRole: buildRole, }).action; - const ecsDeployStaging = new DoorwayECSDeploy(this, "doorway-ecs-deploy-dev", { + const ecsDeployStaging = new DoorwayECSDeploy(this, "doorway-ecs-deploy-staging", { buildspec: "./ci/buildspec/update_ecs.yml", source: sourceArtifact, buildRole: buildRole, diff --git a/cdk/lib/build-pipeline/doorway-database-migrate.ts b/cdk/lib/build-pipeline/doorway-database-migrate.ts index 8447c448bad..8556d6a38fa 100644 --- a/cdk/lib/build-pipeline/doorway-database-migrate.ts +++ b/cdk/lib/build-pipeline/doorway-database-migrate.ts @@ -29,14 +29,14 @@ export class DoorwayDatabaseMigrate { const securityGroupId = Fn.importValue(`doorway-app-sg-${props.environment}`) const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) const azs = Fn.importValue(`doorway-azs-${props.environment}`).split(", ") - const vpc = Vpc.fromVpcAttributes(scope, "vpc", { + const vpc = Vpc.fromVpcAttributes(scope, `vpc-${props.environment}`, { vpcId: vpcId, availabilityZones: azs, }) - const subnet = Subnet.fromSubnetAttributes(scope, "subnet", { + const subnet = Subnet.fromSubnetAttributes(scope, `subnet-${props.environment}`, { subnetId: subnetId, }) - const sg = SecurityGroup.fromSecurityGroupId(scope, "sg", securityGroupId) + const sg = SecurityGroup.fromSecurityGroupId(scope, `sg-${props.environment}`, securityGroupId) const project = new PipelineProject(scope, `doorway-dbMigrate-${props.environment}`, { vpc: vpc, subnetSelection: { subnets: [subnet] }, @@ -72,7 +72,7 @@ export class DoorwayDatabaseMigrate { }) // Grant permissions to the auto-created role - Secret.fromSecretCompleteArn(scope, "dbSecret", dbSecretArn).grantRead(project.role!) + Secret.fromSecretCompleteArn(scope, `dbSecret-${props.environment}`, dbSecretArn).grantRead(project.role!) project.addToRolePolicy(new PolicyStatement({ actions: ["ecs:*", "ecr:*"], resources: ["*"] diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts index 6b684d444c6..4f3b7fbf98d 100644 --- a/cdk/lib/build-pipeline/doorway-docker-build.ts +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -26,9 +26,7 @@ class DoorwayDockerBuild { constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { // Create ECR repository - const repo = new Repository(scope, `${id}-ECRRepository`, { - repositoryName: `doorway/${props.imageName}`, - }) + dotenv.config({ path: path.resolve(__dirname, `../../${props.environment}.env`) }) this.environmentVariables = { ECR_REGION: { value: Aws.REGION }, @@ -45,8 +43,7 @@ class DoorwayDockerBuild { MAPBOX_TOKEN: { value: process.env.MAPBOX_TOKEN || "" } } - // Grant permissions to the provided role - repo.grantPullPush(props.buildRole) + // Create the CodeBuild project const project = new PipelineProject(scope, `${id}-Project`, { @@ -72,6 +69,13 @@ class DoorwayDockerBuild { export class PartnersDockerBuild extends DoorwayDockerBuild { constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { super(scope, id, props) + if (props.environment.startsWith("dev")) { + // Grant permissions to the provided role + const repo = new Repository(scope, `${id}-ECRRepository`, { + repositoryName: `doorway/${props.imageName}`, + }) + repo.grantPullPush(props.buildRole) + } this.environmentVariables.FEATURE_LISTINGS_APPROVAL = { value: process.env.FEATURE_LISTINGS_APPROVAL || "true" } this.environmentVariables.USE_SECURE_DOWNLOAD_PATHWAY = { value: process.env.USE_SECURE_DOWNLOAD_PATHWAY || "true" } this.environmentVariables.CLOUDINARY_CLOUD_NAME = { value: process.env.CLOUDINARY_CLOUD_NAME || "exygy" } @@ -80,6 +84,13 @@ export class PartnersDockerBuild extends DoorwayDockerBuild { export class PublicDockerBuild extends DoorwayDockerBuild { constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { super(scope, id, props) + if (props.environment.startsWith("dev")) { + // Grant permissions to the provided role + const repo = new Repository(scope, `${id}-ECRRepository`, { + repositoryName: `doorway/${props.imageName}`, + }) + repo.grantPullPush(props.buildRole) + } this.environmentVariables.SHOW_PROFESSIONAL_PARTNERS = { value: process.env.SHOW_PROFESSIONAL_PARTNERS || "true" } this.environmentVariables.BLOOM_API_BASE = { value: process.env.BLOOM_API_BASE || "" } this.environmentVariables.JURISDICTION_NAME = { @@ -94,14 +105,23 @@ export class PublicDockerBuild extends DoorwayDockerBuild { export class BackendDockerBuild extends DoorwayDockerBuild { constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { super(scope, id, props) - // Backend build doesnt have any custom environment variables yety + const repo = new Repository(scope, `${id}-ECRRepository`, { + repositoryName: `doorway/${props.imageName}`, + }) + + // Grant permissions to the provided role + repo.grantPullPush(props.buildRole) } } export class ImportDockerBuild extends DoorwayDockerBuild { constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { super(scope, id, props) - // Import listings build doesnt have any custom environment variables yet + const repo = new Repository(scope, `${id}-ECRRepository`, { + repositoryName: `doorway/${props.imageName}`, + }) + // Grant permissions to the provided role + repo.grantPullPush(props.buildRole) } } \ No newline at end of file From 273696dd39fbea723a5db4e4da971a809a3d4aa0 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 16 Sep 2025 13:13:08 -0700 Subject: [PATCH 193/217] fix: CPU-> CPUS --- cdk/lib/doorway_environment/services/doorway-backend-service.ts | 2 +- cdk/lib/doorway_environment/services/doorway-partners-site.ts | 2 +- cdk/lib/doorway_environment/services/doorway-public-site.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cdk/lib/doorway_environment/services/doorway-backend-service.ts b/cdk/lib/doorway_environment/services/doorway-backend-service.ts index e5ed07e1a57..c746eeba4ec 100644 --- a/cdk/lib/doorway_environment/services/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/services/doorway-backend-service.ts @@ -154,7 +154,7 @@ export class DoorwayBackendService { this.service = new DoorwayService(scope, `doorway-api-service-${props.environment}`, { ...props, memory: Number(process.env.BACKEND_MEMORY || 4096), - cpu: Number(process.env.BACKEND_CPU || 2), + cpu: Number(process.env.BACKEND_CPUS || 2), instances: Number(process.env.BACKEND_INSTANCES || 3), port: Number(process.env.BACKEND_API_PORT || 3100), secrets: secrets, diff --git a/cdk/lib/doorway_environment/services/doorway-partners-site.ts b/cdk/lib/doorway_environment/services/doorway-partners-site.ts index 54853892bc8..bcff70805e9 100644 --- a/cdk/lib/doorway_environment/services/doorway-partners-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-partners-site.ts @@ -76,7 +76,7 @@ export class DoorwayPartnersSite { this.service = new DoorwayService(scope, `${id}-service`, { ...props, memory: Number(process.env.PARTNERS_PORTAL_MEMORY || 4096), - cpu: Number(process.env.PARTNERS_PORTAL_CPU || 2), + cpu: Number(process.env.PARTNERS_PORTAL_CPUS || 2), instances: Number(process.env.PARTNERS_PORTAL_INSTANCES || 3), port: Number(process.env.PARTNERS_PORTAL_PORT || 3000), secrets: secrets, diff --git a/cdk/lib/doorway_environment/services/doorway-public-site.ts b/cdk/lib/doorway_environment/services/doorway-public-site.ts index d2ae2b211f2..817ca178d13 100644 --- a/cdk/lib/doorway_environment/services/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-public-site.ts @@ -80,7 +80,7 @@ export class DoorwayPublicSite { this.service = new DoorwayService(scope, `${id}-service`, { ...props, memory: Number(process.env.PUBLIC_PORTAL_MEMORY || 4096), - cpu: Number(process.env.PUBLIC_PORTAL_CPU || 2), + cpu: Number(process.env.PUBLIC_PORTAL_CPUS || 2), instances: Number(process.env.PUBLIC_PORTAL_INSTANCES || 3), port: Number(process.env.PUBLIC_PORTAL_PORT || 3000), secrets: secrets, From b03a40c389a231848d94615f8c99c99011df51cb Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 16 Sep 2025 19:04:39 -0700 Subject: [PATCH 194/217] fix: fixing roles --- .../doorway_environment/services/doorway-import-listings.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/services/doorway-import-listings.ts b/cdk/lib/doorway_environment/services/doorway-import-listings.ts index 3e95ca90d1d..863496a4539 100644 --- a/cdk/lib/doorway_environment/services/doorway-import-listings.ts +++ b/cdk/lib/doorway_environment/services/doorway-import-listings.ts @@ -38,6 +38,7 @@ export class DoorwayImportListings { const executionRole = new Role(scope, `executionRole-${id}`, { assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), }) + props.logGroup.grantWrite(executionRole) const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) const dbSecret = secret.Secret.fromSecretCompleteArn(scope, "dbSecret-import-listings", dbSecretArn) dbSecret.grantRead(executionRole) @@ -94,7 +95,6 @@ export class DoorwayImportListings { actions: [ "ecs:RunTask", "ecs:DescribeTaskDefinition", - "iam:PassRole" ], resources: ["*"] })); @@ -113,11 +113,15 @@ export class DoorwayImportListings { subnets: appsubnets, }, role: schedulerRole, + }) this.schedule = new Schedule(scope, `import-listings-schedule-${props.environment}`, { target: target, + + schedule: ScheduleExpression.rate(Duration.minutes(30)) }) + } } \ No newline at end of file From 924910ff71ce8aa275cf781d6deb8ea6f21dcffc Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 16 Sep 2025 19:56:07 -0700 Subject: [PATCH 195/217] fix: grants --- .../services/doorway-import-listings.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/services/doorway-import-listings.ts b/cdk/lib/doorway_environment/services/doorway-import-listings.ts index 863496a4539..0d6251d0fe4 100644 --- a/cdk/lib/doorway_environment/services/doorway-import-listings.ts +++ b/cdk/lib/doorway_environment/services/doorway-import-listings.ts @@ -2,7 +2,7 @@ import { Duration, Fn } from "aws-cdk-lib"; import { ISecurityGroup, ISubnet, SecurityGroup, Subnet } from "aws-cdk-lib/aws-ec2"; import { Cluster, Secret } from "aws-cdk-lib/aws-ecs"; -import { PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"; +import { ManagedPolicy, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"; import { Schedule, ScheduleExpression } from "aws-cdk-lib/aws-scheduler"; import { EcsRunFargateTask } from "aws-cdk-lib/aws-scheduler-targets"; import * as secret from "aws-cdk-lib/aws-secretsmanager"; @@ -38,6 +38,18 @@ export class DoorwayImportListings { const executionRole = new Role(scope, `executionRole-${id}`, { assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"), }) + executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy"), + ) + executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess"), + ) + executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("AmazonRDSReadOnlyAccess"), + ) + executionRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryReadOnly"), + ) props.logGroup.grantWrite(executionRole) const dbSecretArn = Fn.importValue(`doorwayDBSecret-${props.environment}`) const dbSecret = secret.Secret.fromSecretCompleteArn(scope, "dbSecret-import-listings", dbSecretArn) From 2950826611bf5a6f0cf5ee58971d33b06d754b4b Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 17 Sep 2025 09:37:55 -0700 Subject: [PATCH 196/217] fix: env variable fix --- cdk/lib/doorway_environment/services/doorway-partners-site.ts | 2 +- cdk/lib/doorway_environment/services/doorway-public-site.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cdk/lib/doorway_environment/services/doorway-partners-site.ts b/cdk/lib/doorway_environment/services/doorway-partners-site.ts index bcff70805e9..d47d9ae10be 100644 --- a/cdk/lib/doorway_environment/services/doorway-partners-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-partners-site.ts @@ -89,7 +89,7 @@ export class DoorwayPartnersSite { logGroup: props.logGroup, apiTargetDomainName: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, apiTargetPort: Number(process.env.BACKEND_API_PORT || 3000), - container: `doorway/partners:run-${process.env.ENVIRONMENT || "dev2"}-${gitHash}`, + container: `doorway/partners:run-${props.environment}-${gitHash}`, securityGroup: privateSG, serviceName: props.partnersServiceName, clusterName: props.clusterName, diff --git a/cdk/lib/doorway_environment/services/doorway-public-site.ts b/cdk/lib/doorway_environment/services/doorway-public-site.ts index 817ca178d13..1aa6d3542ad 100644 --- a/cdk/lib/doorway_environment/services/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-public-site.ts @@ -86,14 +86,14 @@ export class DoorwayPublicSite { secrets: secrets, environmentVariables: environmentVariables, serviceConnectServer: false, - domainName: process.env.PUBLIC_PORTAL_DOMAIN || `public.${props.environment}.housingbayarea.mtc.ca.gov`, + domainName: process.env.PUBLIC_PORTAL_DOMAIN || `${props.environment}.housingbayarea.mtc.ca.gov`, executionRole: executionRole, publicUploads: publicUploads, secureUploads: secureUploads, logGroup: props.logGroup, apiTargetDomainName: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int`, apiTargetPort: Number(process.env.BACKEND_API_PORT || 3000), - container: `doorway/public:run-${process.env.ENVIRONMENT || "dev2"}-${gitHash}`, + container: `doorway/public:run-${props.environment}-${gitHash}`, securityGroup: privateSG, serviceName: props.publicServiceName, clusterName: props.clusterName, From eb516c4c0e8294acdb75a3f27e39ee56cebe9b19 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 17 Sep 2025 09:49:09 -0700 Subject: [PATCH 197/217] fix: import listings s/b run candidate --- cdk/bin/doorway-cdk.ts | 2 +- cdk/lib/doorway_environment/doorway-app-environment-stack.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cdk/bin/doorway-cdk.ts b/cdk/bin/doorway-cdk.ts index 0a039f3c345..f1e37aa5de0 100644 --- a/cdk/bin/doorway-cdk.ts +++ b/cdk/bin/doorway-cdk.ts @@ -26,5 +26,5 @@ const cfCertStack = new DoorwayCloudFrontStack(app, `DoorwayCloudFrontStack-${en }) /** This stack actually creates a doorway environment. */ -new DoorwayAppEnvironmentStack(app, `DoorwayAppEnvironmentStack-${environment}`) +new DoorwayAppEnvironmentStack(app, `DoorwayAppEnvironmentStack-${environment}`, environment) diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index a8ac1c7bb4a..ac16e1161be 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -15,7 +15,7 @@ import { DoorwayS3Stack } from "./services/doorway-s3-stack" import { DoorwaySecrets } from "./services/doorway-secrets-stack" export class DoorwayAppEnvironmentStack extends Stack { - constructor(scope: Construct, id: string) { + constructor(scope: Construct, id: string, environment: string) { super(scope, id, { env: { account: process.env.CDK_DEFAULT_ACCOUNT || "364076391763", @@ -23,7 +23,6 @@ export class DoorwayAppEnvironmentStack extends Stack { }, crossRegionReferences: true }) - const environment = process.env.ENVIRONMENT || "dev2" dotenv.config({ path: path.resolve(__dirname, `../../${environment}.env`) }) From 0c451da831e755d24c403bea148ef852bf6e8e4f Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 17 Sep 2025 09:49:10 -0700 Subject: [PATCH 198/217] fix: import listings --- cdk/lib/doorway_environment/services/doorway-import-listings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/lib/doorway_environment/services/doorway-import-listings.ts b/cdk/lib/doorway_environment/services/doorway-import-listings.ts index 0d6251d0fe4..dca247a203a 100644 --- a/cdk/lib/doorway_environment/services/doorway-import-listings.ts +++ b/cdk/lib/doorway_environment/services/doorway-import-listings.ts @@ -82,7 +82,7 @@ export class DoorwayImportListings { serviceConnectServer: false, domainName: "", executionRole: executionRole, - container: `doorway/import-listings:run-${process.env.GIT_HASH}`, + container: `doorway/import-listings:run-candidate`, securityGroup: privateSG, serviceName: `doorway-import-listings-${props.environment}`, clusterName: props.clusterName, From 3a7bd6f09a1adf8dcd2e2901e376bfd7266bed04 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 17 Sep 2025 12:00:08 -0700 Subject: [PATCH 199/217] fix: dotenv madness --- cdk/bin/doorway-cdk.ts | 5 +++-- cdk/lib/build-pipeline/doorway-docker-build.ts | 3 +-- .../doorway-app-environment-stack.ts | 10 ++++++++-- .../services/doorway-backend-service.ts | 1 + .../doorway_environment/services/doorway-s3-stack.ts | 3 --- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/cdk/bin/doorway-cdk.ts b/cdk/bin/doorway-cdk.ts index f1e37aa5de0..f0dbb60436c 100644 --- a/cdk/bin/doorway-cdk.ts +++ b/cdk/bin/doorway-cdk.ts @@ -1,4 +1,3 @@ -#!/usr/bin/env node import * as cdk from "aws-cdk-lib" import { DoorwayBuildPipelineStack } from "../lib/build-pipeline/doorway-build-pipeline-stack" @@ -10,8 +9,9 @@ import { DoorwayCloudFrontStack } from "../lib/doorway_environment/doorway-cloud * stacks that make up the Doorway Application infrastructure and build pipeline. **/ const app = new cdk.App() - const environment = process.env.ENVIRONMENT || "dev2" + + /** This stack creates the doorway application build pipeline **/ new DoorwayBuildPipelineStack(app, "DoorwayBuildPipelineStack", { githubSecret: "mtc/githubSecret", @@ -26,5 +26,6 @@ const cfCertStack = new DoorwayCloudFrontStack(app, `DoorwayCloudFrontStack-${en }) /** This stack actually creates a doorway environment. */ + new DoorwayAppEnvironmentStack(app, `DoorwayAppEnvironmentStack-${environment}`, environment) diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts index 4f3b7fbf98d..e10ff3e3f47 100644 --- a/cdk/lib/build-pipeline/doorway-docker-build.ts +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -5,7 +5,6 @@ import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" import { Repository } from "aws-cdk-lib/aws-ecr" import { Role } from "aws-cdk-lib/aws-iam" import { Construct } from "constructs/lib/construct" -import dotenv from "dotenv" import fs from "fs" import path from "path" import * as YAML from "yaml" @@ -27,7 +26,7 @@ class DoorwayDockerBuild { constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { // Create ECR repository - dotenv.config({ path: path.resolve(__dirname, `../../${props.environment}.env`) }) + this.environmentVariables = { ECR_REGION: { value: Aws.REGION }, ECR_ACCOUNT_ID: { value: Aws.ACCOUNT_ID }, diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index ac16e1161be..e7ef80294e5 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -2,7 +2,7 @@ import { aws_logs, Fn, RemovalPolicy, Stack } from "aws-cdk-lib" import { Vpc } from "aws-cdk-lib/aws-ec2" import { LogGroup } from "aws-cdk-lib/aws-logs" import { Construct } from "constructs" -import dotenv from "dotenv" +import * as dotenv from "dotenv" import path from "path" import { DoorwayLoadBalancers } from "./doorway-load-balancers" @@ -23,9 +23,15 @@ export class DoorwayAppEnvironmentStack extends Stack { }, crossRegionReferences: true }) - dotenv.config({ path: path.resolve(__dirname, `../../${environment}.env`) }) + console.log(`Creating Application stack for ${environment}`) + console.log(`Using environment file: ${path.resolve(__dirname, `../../${environment}.env`)}`) + dotenv.config({ + path: path.resolve(__dirname, `../../${environment}.env`), + override: true + }) + const logGroup = new LogGroup(this, `doorway-${environment}-tasks`, { logGroupName: `doorway-${environment}-tasks`, removalPolicy: RemovalPolicy.DESTROY, diff --git a/cdk/lib/doorway_environment/services/doorway-backend-service.ts b/cdk/lib/doorway_environment/services/doorway-backend-service.ts index c746eeba4ec..f162974a866 100644 --- a/cdk/lib/doorway_environment/services/doorway-backend-service.ts +++ b/cdk/lib/doorway_environment/services/doorway-backend-service.ts @@ -92,6 +92,7 @@ export class DoorwayBackendService { PGDATABASE: Secret.fromSecretsManager(dbSecret, "dbname"), } const secretNames = process.env.BACKEND_API_SECRETS || "" + console.log(`SECRETS: ${secretNames}`) secretNames.split(",").forEach((secretName) => { secrets[secretName] = Secret.fromSecretsManager( secret.Secret.fromSecretNameV2(scope, secretName, `/doorway/${props.environment}/${secretName}`), diff --git a/cdk/lib/doorway_environment/services/doorway-s3-stack.ts b/cdk/lib/doorway_environment/services/doorway-s3-stack.ts index f630c44604d..af2ffd8c0ae 100644 --- a/cdk/lib/doorway_environment/services/doorway-s3-stack.ts +++ b/cdk/lib/doorway_environment/services/doorway-s3-stack.ts @@ -1,15 +1,12 @@ import { CfnOutput, Stack } from "aws-cdk-lib"; import { Bucket, HttpMethods } from "aws-cdk-lib/aws-s3"; import { Construct } from "constructs"; -import dotenv from "dotenv"; -import path from "path"; import { DoorwayProps } from "../doorway-props"; export class DoorwayS3Stack extends Stack { constructor(scope: Construct, id: string, props: DoorwayProps) { super(scope, id); - dotenv.config({ path: path.resolve(__dirname, `../../${props.environment}.env`) }) const secureUploadsBucket = new Bucket(this, "secureUploadsBucket", { bucketName: `doorway-secure-uploads-${props.environment}`, From 2f9ba3ccdeeb1704489ad569e119d7c19832b0e8 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 17 Sep 2025 14:58:13 -0700 Subject: [PATCH 200/217] fix: wrong api url --- cdk/staging.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/staging.env b/cdk/staging.env index 66d29b6ef2f..12c05e77ae6 100644 --- a/cdk/staging.env +++ b/cdk/staging.env @@ -26,7 +26,7 @@ HTTPS_OFF=true PARTNERS_PORTAL_PORT=3200 PARTNERS_PORTAL_DOMAIN=partners.staging2.housingbayarea.mtc.ca.gov -BACKEND_API_DOMAIN=backend.staging3.housingbayarea.int +BACKEND_API_DOMAIN=backend.staging2.housingbayarea.int BACKEND_API_BASE=http://backend.staging2.housingbayarea.int BACKEND_API_PORT=3100 BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVDELIVERY_API_URL From 17455e972a1cf7b7b55488a3f24e370f11c536b5 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Wed, 17 Sep 2025 15:54:41 -0700 Subject: [PATCH 201/217] fix: switching to ssm parameters for lb dns name --- cdk/bin/doorway-cdk.ts | 8 +-- .../doorway-cloudfront-stack.ts | 52 +++++++++++++------ .../doorway-load-balancers.ts | 6 +++ 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/cdk/bin/doorway-cdk.ts b/cdk/bin/doorway-cdk.ts index f0dbb60436c..f0619dfe240 100644 --- a/cdk/bin/doorway-cdk.ts +++ b/cdk/bin/doorway-cdk.ts @@ -1,5 +1,4 @@ import * as cdk from "aws-cdk-lib" - import { DoorwayBuildPipelineStack } from "../lib/build-pipeline/doorway-build-pipeline-stack" import { DoorwayAppEnvironmentStack } from "../lib/doorway_environment/doorway-app-environment-stack" import { DoorwayCloudFrontStack } from "../lib/doorway_environment/doorway-cloudfront-stack" @@ -18,12 +17,7 @@ new DoorwayBuildPipelineStack(app, "DoorwayBuildPipelineStack", { dockerHubSecret: "mtc/dockerHub", }) /** This stack creates the cert used by the cloudfront distribution. It sits in it's own stack because cloudfront certs have to be created in us-east-1 **/ -const cfCertStack = new DoorwayCloudFrontStack(app, `DoorwayCloudFrontStack-${environment}`, { - environment, - publicDomainName: process.env.PUBLIC_PORTAL_DOMAIN || `${environment}.housingbayarea.mtc.ca.gov`, - partnersDomainName: process.env.PARTNERS_PORTAL_DOMAIN || `partners.${environment}.housingbayarea.mtc.ca.gov`, - loadBalancerDnsName: process.env.LOAD_BALANCER_DNS_NAME -}) +const cfCertStack = new DoorwayCloudFrontStack(app, `DoorwayCloudFrontStack-${environment}`, environment) /** This stack actually creates a doorway environment. */ diff --git a/cdk/lib/doorway_environment/doorway-cloudfront-stack.ts b/cdk/lib/doorway_environment/doorway-cloudfront-stack.ts index f2ec2bcb088..aa8a8da7ddf 100644 --- a/cdk/lib/doorway_environment/doorway-cloudfront-stack.ts +++ b/cdk/lib/doorway_environment/doorway-cloudfront-stack.ts @@ -1,10 +1,13 @@ -import { Duration, Fn, Stack, StackProps, CfnParameter } from "aws-cdk-lib"; +import { Duration, Fn, Stack, StackProps } from "aws-cdk-lib"; import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager"; import { AllowedMethods, CachePolicy, Distribution, OriginRequestPolicy, PriceClass, SecurityPolicyProtocol, ViewerProtocolPolicy } from "aws-cdk-lib/aws-cloudfront"; import { LoadBalancerV2Origin } from "aws-cdk-lib/aws-cloudfront-origins"; import { ApplicationLoadBalancer } from "aws-cdk-lib/aws-elasticloadbalancingv2"; import { HostedZone } from "aws-cdk-lib/aws-route53"; +import { StringParameter } from "aws-cdk-lib/aws-ssm"; import { Construct } from "constructs"; +import * as dotenv from 'dotenv'; +import * as path from 'path'; export interface CloudFrontCertStackProps extends StackProps { environment: string; @@ -15,38 +18,55 @@ export interface CloudFrontCertStackProps extends StackProps { export class DoorwayCloudFrontStack extends Stack { public cloudFrontCertArn: string; - constructor(scope: Construct, id: string, props: CloudFrontCertStackProps) { + constructor(scope: Construct, id: string, environment: string) { super(scope, id, { - ...props, env: { + env: { region: 'us-east-1', account: process.env.CDK_DEFAULT_ACCOUNT || "364076391763", }, crossRegionReferences: true }); + console.log(`Creating Application stack for ${environment}`) + console.log(`Using environment file: ${path.resolve(__dirname, `../../${environment}.env`)}`) + dotenv.config({ + path: path.resolve(__dirname, `../../${environment}.env`), + override: true + }) + console.log(process.env) + + - const dnsZone = HostedZone.fromLookup(this, `publicDnsZone-${props.environment}`, { + + const dnsZone = HostedZone.fromLookup(this, `publicDnsZone-${environment}`, { domainName: "housingbayarea.mtc.ca.gov", }); - const publicDomainName = process.env.PUBLIC_PORTAL_DOMAIN || `${props.environment}.housingbayarea.mtc.ca.gov` - const partnersDomainName = process.env.PARTNERS_PORTAL_DOMAIN || `partners.${props.environment}.housingbayarea.mtc.ca.gov` - const lbDnsName = props.loadBalancerDnsName || 'doorway-lbs-dev2-734646298.us-west-2.elb.amazonaws.com' + const publicDomainName = process.env.PUBLIC_PORTAL_DOMAIN || `${environment}.housingbayarea.mtc.ca.gov` + const partnersDomainName = process.env.PARTNERS_PORTAL_DOMAIN || `partners.${environment}.housingbayarea.mtc.ca.gov` + const cert = new Certificate(this, "CloudFrontCertificate", { - domainName: props.publicDomainName, + domainName: publicDomainName, validation: CertificateValidation.fromDns(dnsZone), - subjectAlternativeNames: [props.partnersDomainName] + subjectAlternativeNames: [partnersDomainName] }); - const cfOrigin = new LoadBalancerV2Origin(ApplicationLoadBalancer.fromApplicationLoadBalancerAttributes(this, `PublicLB-${props.environment}`, { - loadBalancerArn: `arn:aws:elasticloadbalancing:us-west-2:364076391763:loadbalancer/app/doorway-lbs-${props.environment}/dummy`, + // Get load balancer DNS from SSM parameter (cross-region compatible) + const loadBalancerDnsName = StringParameter.fromStringParameterName( + this, + `lb-dns-param-${environment}`, + `/doorway/${environment}/public-lb-dns` + ).stringValue; + + const cfOrigin = new LoadBalancerV2Origin(ApplicationLoadBalancer.fromApplicationLoadBalancerAttributes(this, `PublicLB-${environment}`, { + loadBalancerArn: `arn:aws:elasticloadbalancing:us-west-2:364076391763:loadbalancer/app/doorway-lbs-${environment}/dummy`, securityGroupId: 'sg-dummy', - loadBalancerDnsName: lbDnsName + loadBalancerDnsName: loadBalancerDnsName })) - const cachePolicy = new CachePolicy(this, `CachePolicy-images-${props.environment}`, { - cachePolicyName: `CachePolicy-images-${props.environment}`, + const cachePolicy = new CachePolicy(this, `CachePolicy-images-${environment}`, { + cachePolicyName: `CachePolicy-images-${environment}`, enableAcceptEncodingGzip: false, enableAcceptEncodingBrotli: false, minTtl: Duration.seconds(60), @@ -64,7 +84,7 @@ export class DoorwayCloudFrontStack extends Stack { }, }); - new Distribution(this, `CloudFront-${props.environment}`, { + new Distribution(this, `CloudFront-${environment}`, { domainNames: [publicDomainName, partnersDomainName], certificate: cert, minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021, @@ -75,7 +95,7 @@ export class DoorwayCloudFrontStack extends Stack { logIncludesCookies: true, logFilePrefix: "cloudfront", - comment: `Doorway Cloudfront for ${props.environment}`, + comment: `Doorway Cloudfront for ${environment}`, priceClass: PriceClass.PRICE_CLASS_100, geoRestriction: { diff --git a/cdk/lib/doorway_environment/doorway-load-balancers.ts b/cdk/lib/doorway_environment/doorway-load-balancers.ts index a0bf77b4bfb..4d64d2c2688 100644 --- a/cdk/lib/doorway_environment/doorway-load-balancers.ts +++ b/cdk/lib/doorway_environment/doorway-load-balancers.ts @@ -206,6 +206,12 @@ export class DoorwayLoadBalancers { value: this.loadBalancer.loadBalancerDnsName, exportName: `public-lb-dns-${props.environment}` }) + + // Store DNS name in SSM for cross-region access + new StringParameter(scope, `public-lb-dns-param-${props.environment}`, { + parameterName: `/doorway/${props.environment}/public-lb-dns`, + stringValue: this.loadBalancer.loadBalancerDnsName + }) new CfnOutput(scope, `public-lb-arn-${props.environment}`, { value: this.loadBalancer.loadBalancerArn, exportName: `doorway-public-lb-${props.environment}-arn` From 4d66146b47828ff330763e008f2fac235b1e7cd4 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 18 Sep 2025 10:00:17 -0700 Subject: [PATCH 202/217] fix: going with dev for env name but keeping dev2 urls. Plus fixing cloudfront --- api/src/controllers/auth.controller.ts | 54 ++++++++-------- api/src/main.ts | 62 +++++++------------ api/src/services/auth.service.ts | 10 +-- cdk/bin/doorway-cdk.ts | 14 +++-- cdk/{dev2.env => dev.env} | 0 .../doorway-build-pipeline-stack.ts | 16 ++--- .../doorway-app-environment-stack.ts | 4 ++ .../doorway-cloudfront-stack.ts | 14 ++--- .../doorway-load-balancers.ts | 6 -- ci/buildspec/build_backend.yml | 7 +-- ci/buildspec/cdk_deploy.yml | 15 +++++ ci/buildspec/update_ecs.yml | 10 +-- 12 files changed, 103 insertions(+), 109 deletions(-) rename cdk/{dev2.env => dev.env} (100%) create mode 100644 ci/buildspec/cdk_deploy.yml diff --git a/api/src/controllers/auth.controller.ts b/api/src/controllers/auth.controller.ts index 8ad28fb610a..38d7e95f07c 100644 --- a/api/src/controllers/auth.controller.ts +++ b/api/src/controllers/auth.controller.ts @@ -1,32 +1,44 @@ -import { BadRequestException, Body, Controller, Get, Logger, Post, Put, Request, Response, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common'; +import { + Controller, + Get, + Request, + Response, + Post, + UsePipes, + ValidationPipe, + Body, + BadRequestException, + Put, + UseGuards, +} from '@nestjs/common'; import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { Request as ExpressRequest, Response as ExpressResponse } from 'express'; - +import { + Response as ExpressResponse, + Request as ExpressRequest, +} from 'express'; +import { defaultValidationPipeOptions } from '../utilities/default-validation-pipe-options'; +import { SuccessDTO } from '../dtos/shared/success.dto'; +import { AuthService, REFRESH_COOKIE_NAME } from '../services/auth.service'; +import { RequestMfaCode } from '../dtos/mfa/request-mfa-code.dto'; +import { RequestMfaCodeResponse } from '../dtos/mfa/request-mfa-code-response.dto'; import { Confirm } from '../dtos/auth/confirm.dto'; -import { LoginViaSingleUseCode } from '../dtos/auth/login-single-use-code.dto'; -import { Login } from '../dtos/auth/login.dto'; import { UpdatePassword } from '../dtos/auth/update-password.dto'; -import { RequestMfaCodeResponse } from '../dtos/mfa/request-mfa-code-response.dto'; -import { RequestMfaCode } from '../dtos/mfa/request-mfa-code.dto'; -import { SuccessDTO } from '../dtos/shared/success.dto'; -import { User } from '../dtos/users/user.dto'; -import { ApiKeyGuard } from '../guards/api-key.guard'; -import { JwtAuthGuard } from '../guards/jwt.guard'; import { MfaAuthGuard } from '../guards/mfa.guard'; +import { JwtAuthGuard } from '../guards/jwt.guard'; import { OptionalAuthGuard } from '../guards/optional.guard'; -import { SingleUseCodeAuthGuard } from '../guards/single-use-code.guard'; - -import { AuthService, REFRESH_COOKIE_NAME } from '../services/auth.service'; -import { defaultValidationPipeOptions } from '../utilities/default-validation-pipe-options'; +import { Login } from '../dtos/auth/login.dto'; import { mapTo } from '../utilities/mapTo'; +import { User } from '../dtos/users/user.dto'; +import { LoginViaSingleUseCode } from '../dtos/auth/login-single-use-code.dto'; +import { ApiKeyGuard } from '../guards/api-key.guard'; +import { SingleUseCodeAuthGuard } from '../guards/single-use-code.guard'; @Controller('auth') @ApiTags('auth') @UsePipes(new ValidationPipe(defaultValidationPipeOptions)) @UseGuards(ApiKeyGuard) export class AuthController { - private readonly logger = new Logger(AuthController.name); - constructor(private readonly authService: AuthService) { } + constructor(private readonly authService: AuthService) {} @Post('login') @ApiOperation({ summary: 'Login', operationId: 'login' }) @@ -38,14 +50,6 @@ export class AuthController { @Response({ passthrough: true }) res: ExpressResponse, @Body() dto: Login, ): Promise { - - this.logger.log(`Login attempt for user: ${dto.email}`); - this.logger.log(`Origin: ${req.headers.origin}`); - this.logger.log(`Headers: ${JSON.stringify(req.headers)}`) - this.logger.log(`Cookies: ${JSON.stringify(req.cookies)}`) - this.logger.log(`Body: ${JSON.stringify(req.body)}`) - - return await this.authService.setCredentials( res, mapTo(User, req['user']), diff --git a/api/src/main.ts b/api/src/main.ts index e0b8f0c0c33..dd1e018bd6f 100644 --- a/api/src/main.ts +++ b/api/src/main.ts @@ -5,28 +5,28 @@ try { } catch { // Pass } +import { NestFactory, HttpAdapterHost } from '@nestjs/core'; import { Logger } from '@nestjs/common'; +import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { ConfigService } from '@nestjs/config'; -import { HttpAdapterHost, NestFactory } from '@nestjs/core'; -import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; -import compression from 'compression'; import cookieParser from 'cookie-parser'; +import compression from 'compression'; import { json } from 'express'; -import { WinstonModule } from 'nest-winston'; -import { instance } from './logger/winston.logger'; -import { logger } from './middleware/logger.middleware'; import { AppModule } from './modules/app.module'; import { CustomExceptionFilter } from './utilities/custom-exception-filter'; +import { logger } from './middleware/logger.middleware'; +import { WinstonModule } from 'nest-winston'; +import { instance } from './logger/winston.logger'; async function bootstrap() { const app = await NestFactory.create(AppModule, { // In local development use the built in logger for better readability logger: process.env.NODE_ENV === 'development' - ? ['log', 'debug', 'error', 'warn'] + ? ['log'] : WinstonModule.createLogger({ - instance: instance, - }), + instance: instance, + }), }); const allowList = process.env.CORS_ORIGINS || []; const allowListRegex = process.env.CORS_REGEX @@ -39,39 +39,21 @@ async function bootstrap() { const { httpAdapter } = app.get(HttpAdapterHost); const inUselogger: Logger = app.get(Logger); app.useGlobalFilters(new CustomExceptionFilter(httpAdapter, inUselogger)); - if (process.env.DISABLE_CORS === 'TRUE') { - inUselogger.warn('CORS is disabled'); - } else { - - app.enableCors((req, cb) => { - const options = { - credentials: true, - origin: false, - }; + app.enableCors((req, cb) => { + const options = { + credentials: true, + origin: false, + }; - if ( - - allowList.indexOf(req.header('Origin')) !== -1 || - regexAllowList.some((regex) => regex.test(req.header('Origin'))) - ) { - options.origin = true; - } - cb(null, options); - }); - } - app.use((req, res, next) => { - inUselogger.debug('=== RAW REQUEST DEBUG ==='); - inUselogger.debug(`Method: ${req.method}`); - inUselogger.debug(`URL: ${req.url}`); - inUselogger.debug(`Origin: ${req.headers.origin}`); - inUselogger.debug(`Host: ${req.headers.host}`); - inUselogger.debug(`All Headers: ${JSON.stringify(req.headers, null, 2)}`); - inUselogger.debug(`Cookies: ${JSON.stringify(req.cookies, null, 2)}`); - inUselogger.debug(`Body: ${JSON.stringify(req.body, null, 2)}`); - inUselogger.debug('========================'); - next(); + if ( + process.env.DISABLE_CORS === 'TRUE' || + allowList.indexOf(req.header('Origin')) !== -1 || + regexAllowList.some((regex) => regex.test(req.header('Origin'))) + ) { + options.origin = true; + } + cb(null, options); }); - app.use(logger); app.use( cookieParser(), diff --git a/api/src/services/auth.service.ts b/api/src/services/auth.service.ts index 9050f3e58f5..fda7344e9d7 100644 --- a/api/src/services/auth.service.ts +++ b/api/src/services/auth.service.ts @@ -43,7 +43,7 @@ export const AUTH_COOKIE_OPTIONS: CookieOptions = { secure, sameSite, maxAge: TOKEN_COOKIE_MAXAGE / 8, // access token should last 3 hr - domain: process.env.COOKIE_DOMAIN ?? "", + domain: process.env.COOKIE_DOMAIN ?? undefined, }; export const REFRESH_COOKIE_OPTIONS: CookieOptions = { ...AUTH_COOKIE_OPTIONS, @@ -65,7 +65,7 @@ export class AuthService { private userService: UserService, private smsService: SmsService, private emailsService: EmailService, - ) { } + ) {} /* generates a signed token for a user willBeRefreshToken changes the TTL of the token with true being longer and false being shorter @@ -300,9 +300,9 @@ export class AuthService { return dto.mfaType === MfaType.email ? { email: user.email, phoneNumberVerified: user.phoneNumberVerified } : { - phoneNumber: user.phoneNumber, - phoneNumberVerified: user.phoneNumberVerified, - }; + phoneNumber: user.phoneNumber, + phoneNumberVerified: user.phoneNumberVerified, + }; } /* updates a user's password and logs them in diff --git a/cdk/bin/doorway-cdk.ts b/cdk/bin/doorway-cdk.ts index f0619dfe240..3258e129fd0 100644 --- a/cdk/bin/doorway-cdk.ts +++ b/cdk/bin/doorway-cdk.ts @@ -8,7 +8,7 @@ import { DoorwayCloudFrontStack } from "../lib/doorway_environment/doorway-cloud * stacks that make up the Doorway Application infrastructure and build pipeline. **/ const app = new cdk.App() -const environment = process.env.ENVIRONMENT || "dev2" +const environment = process.env.ENVIRONMENT || "dev" /** This stack creates the doorway application build pipeline **/ @@ -16,10 +16,14 @@ new DoorwayBuildPipelineStack(app, "DoorwayBuildPipelineStack", { githubSecret: "mtc/githubSecret", dockerHubSecret: "mtc/dockerHub", }) -/** This stack creates the cert used by the cloudfront distribution. It sits in it's own stack because cloudfront certs have to be created in us-east-1 **/ -const cfCertStack = new DoorwayCloudFrontStack(app, `DoorwayCloudFrontStack-${environment}`, environment) - /** This stack actually creates a doorway environment. */ +const appStack = new DoorwayAppEnvironmentStack(app, `DoorwayAppEnvironmentStack-${environment}`, environment) -new DoorwayAppEnvironmentStack(app, `DoorwayAppEnvironmentStack-${environment}`, environment) +/** This stack creates the cert used by the cloudfront distribution. It sits in it's own stack because cloudfront certs have to be created in us-east-1 **/ +const cfCertStack = new DoorwayCloudFrontStack(app, `DoorwayCloudFrontStack-${environment}`, { + environment: environment, + publicDomainName: process.env.PUBLIC_PORTAL_DOMAIN || `${environment}.housingbayarea.mtc.ca.gov`, + partnersDomainName: process.env.PARTNERS_PORTAL_DOMAIN || `partners.${environment}.housingbayarea.mtc.ca.gov`, + loadBalancerDnsName: appStack.loadBalancerDnsName +}) diff --git a/cdk/dev2.env b/cdk/dev.env similarity index 100% rename from cdk/dev2.env rename to cdk/dev.env diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 47025717c5f..3e534bf14ac 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -119,7 +119,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, - environment: "dev2" + environment: "dev" }).action, new ImportDockerBuild(this, "doorway-import-listings", { buildspec: "../ci/buildspec/build_import_listings.yml", @@ -127,7 +127,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, - environment: "dev2" + environment: "dev" }).action, new PartnersDockerBuild(this, `doorway-partners-dev`, { buildspec: "../ci/buildspec/build_partners.yml", @@ -135,7 +135,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, - environment: "dev2" + environment: "dev" }).action, new PublicDockerBuild(this, "doorway-public-dev", { buildspec: "../ci/buildspec/build_public.yml", @@ -143,23 +143,23 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, - environment: "dev2" + environment: "dev" }).action, ], }) const dbmigrate = new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev", { - environment: "dev2", + environment: "dev", buildspec: "./ci/buildspec/migrate_stop_backend.yml", source: sourceArtifact, buildRole: buildRole, // This parameter is now ignored }).action const ecsDeploy = new DoorwayECSDeploy(this, "doorway-ecs-deploy-dev", { - buildspec: "./ci/buildspec/update_ecs.yml", + buildspec: "./ci/buildspec/cdk_deploy.yml", source: sourceArtifact, buildRole: buildRole, - environment: "dev2", + environment: "dev", }).action pipeline.addStage({ @@ -197,7 +197,7 @@ export class DoorwayBuildPipelineStack extends Stack { }).action; const ecsDeployStaging = new DoorwayECSDeploy(this, "doorway-ecs-deploy-staging", { - buildspec: "./ci/buildspec/update_ecs.yml", + buildspec: "./ci/buildspec/cdk_deploy.yml", source: sourceArtifact, buildRole: buildRole, environment: "staging", diff --git a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts index e7ef80294e5..c59295e150b 100644 --- a/cdk/lib/doorway_environment/doorway-app-environment-stack.ts +++ b/cdk/lib/doorway_environment/doorway-app-environment-stack.ts @@ -15,6 +15,8 @@ import { DoorwayS3Stack } from "./services/doorway-s3-stack" import { DoorwaySecrets } from "./services/doorway-secrets-stack" export class DoorwayAppEnvironmentStack extends Stack { + public loadBalancerDnsName: string; + constructor(scope: Construct, id: string, environment: string) { super(scope, id, { env: { @@ -77,5 +79,7 @@ export class DoorwayAppEnvironmentStack extends Stack { const scheduler = new DoorwayImportListings(this, `doorway-import-listings-scheduler-${environment}`, dwProps) scheduler.schedule.node.addDependency(api.service) + // Expose load balancer DNS name for cross-region access + this.loadBalancerDnsName = lb.loadBalancer.loadBalancerDnsName; } } diff --git a/cdk/lib/doorway_environment/doorway-cloudfront-stack.ts b/cdk/lib/doorway_environment/doorway-cloudfront-stack.ts index aa8a8da7ddf..1676afb3362 100644 --- a/cdk/lib/doorway_environment/doorway-cloudfront-stack.ts +++ b/cdk/lib/doorway_environment/doorway-cloudfront-stack.ts @@ -13,12 +13,12 @@ export interface CloudFrontCertStackProps extends StackProps { environment: string; publicDomainName: string; partnersDomainName: string; - loadBalancerDnsName?: string; + loadBalancerDnsName: string; } export class DoorwayCloudFrontStack extends Stack { public cloudFrontCertArn: string; - constructor(scope: Construct, id: string, environment: string) { + constructor(scope: Construct, id: string, props: CloudFrontCertStackProps) { super(scope, id, { env: { region: 'us-east-1', @@ -26,6 +26,7 @@ export class DoorwayCloudFrontStack extends Stack { }, crossRegionReferences: true }); + const environment = props.environment; console.log(`Creating Application stack for ${environment}`) console.log(`Using environment file: ${path.resolve(__dirname, `../../${environment}.env`)}`) @@ -53,17 +54,10 @@ export class DoorwayCloudFrontStack extends Stack { subjectAlternativeNames: [partnersDomainName] }); - // Get load balancer DNS from SSM parameter (cross-region compatible) - const loadBalancerDnsName = StringParameter.fromStringParameterName( - this, - `lb-dns-param-${environment}`, - `/doorway/${environment}/public-lb-dns` - ).stringValue; - const cfOrigin = new LoadBalancerV2Origin(ApplicationLoadBalancer.fromApplicationLoadBalancerAttributes(this, `PublicLB-${environment}`, { loadBalancerArn: `arn:aws:elasticloadbalancing:us-west-2:364076391763:loadbalancer/app/doorway-lbs-${environment}/dummy`, securityGroupId: 'sg-dummy', - loadBalancerDnsName: loadBalancerDnsName + loadBalancerDnsName: props.loadBalancerDnsName })) const cachePolicy = new CachePolicy(this, `CachePolicy-images-${environment}`, { cachePolicyName: `CachePolicy-images-${environment}`, diff --git a/cdk/lib/doorway_environment/doorway-load-balancers.ts b/cdk/lib/doorway_environment/doorway-load-balancers.ts index 4d64d2c2688..a0bf77b4bfb 100644 --- a/cdk/lib/doorway_environment/doorway-load-balancers.ts +++ b/cdk/lib/doorway_environment/doorway-load-balancers.ts @@ -206,12 +206,6 @@ export class DoorwayLoadBalancers { value: this.loadBalancer.loadBalancerDnsName, exportName: `public-lb-dns-${props.environment}` }) - - // Store DNS name in SSM for cross-region access - new StringParameter(scope, `public-lb-dns-param-${props.environment}`, { - parameterName: `/doorway/${props.environment}/public-lb-dns`, - stringValue: this.loadBalancer.loadBalancerDnsName - }) new CfnOutput(scope, `public-lb-arn-${props.environment}`, { value: this.loadBalancer.loadBalancerArn, exportName: `doorway-public-lb-${props.environment}-arn` diff --git a/ci/buildspec/build_backend.yml b/ci/buildspec/build_backend.yml index bc67445dcd1..f35060a3e2e 100644 --- a/ci/buildspec/build_backend.yml +++ b/ci/buildspec/build_backend.yml @@ -18,17 +18,14 @@ phases: build: commands: - - echo "BUILD PHASE STARTING - DEBUG TEST" - export ECR_HOST="${ECR_ACCOUNT_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com" - export ECR_REPO="${ECR_HOST}/${ECR_NAMESPACE}" - - echo "ECR_REPO is ${ECR_REPO}" - - ls -la build/docker/ # Skip the test step as it is handle before merge. But re-add if you want more verification # - docker build . -f build/docker/Dockerfile.backend --target test -t api:test # - docker run api:test yarn test - - docker build --no-cache . -f build/docker/Dockerfile.backend --build-arg ECR_REGION=${ECR_REGION} --build-arg ECR_ACCOUNT_ID=${ECR_ACCOUNT_ID} --build-arg CACHE_BUST=$(date +%s) --target run -t api:run-candidate - - docker build --no-cache . -f build/docker/Dockerfile.backend --build-arg ECR_REGION=${ECR_REGION} --build-arg ECR_ACCOUNT_ID=${ECR_ACCOUNT_ID} --build-arg CACHE_BUST=$(date +%s) --target migrate -t api:migrate-candidate + - docker build . -f build/docker/Dockerfile.backend --build-arg ECR_REGION=${ECR_REGION} --build-arg ECR_ACCOUNT_ID=${ECR_ACCOUNT_ID} --target run -t api:run-candidate + - docker build . -f build/docker/Dockerfile.backend --build-arg ECR_REGION=${ECR_REGION} --build-arg ECR_ACCOUNT_ID=${ECR_ACCOUNT_ID} --target migrate -t api:migrate-candidate # Add hash and candidate tags to migration image - docker tag api:migrate-candidate "${ECR_REPO}/backend:migrate-${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8}" diff --git a/ci/buildspec/cdk_deploy.yml b/ci/buildspec/cdk_deploy.yml new file mode 100644 index 00000000000..d99663fef27 --- /dev/null +++ b/ci/buildspec/cdk_deploy.yml @@ -0,0 +1,15 @@ +# AWS CodeBuild config +# This file is referenced in the infra repo as a CodeBuild buildspec target. +## The environment variables in this file are passed in through the buildbuild resource. +## Check the terraform scripts to see where these variables originate from. +version: 0.2 +env: + shell: bash +phases: + build: + commands: + - npm install -g yarn + - npm install -g aws-cdk + - cd ${CODEBUILD_SRC_DIR}/cdk + - yarn install + - cdk deploy DoorwayAppEnvironmentStack-${ENVIRONMENT} --require-approval never diff --git a/ci/buildspec/update_ecs.yml b/ci/buildspec/update_ecs.yml index d99663fef27..a4c7a485559 100644 --- a/ci/buildspec/update_ecs.yml +++ b/ci/buildspec/update_ecs.yml @@ -8,8 +8,8 @@ env: phases: build: commands: - - npm install -g yarn - - npm install -g aws-cdk - - cd ${CODEBUILD_SRC_DIR}/cdk - - yarn install - - cdk deploy DoorwayAppEnvironmentStack-${ENVIRONMENT} --require-approval never + # Force ECS to use the new images + - aws ecs update-service --force-new-deployment --cluster "${ECS_BACKEND_CLUSTER:-${ECS_DEFAULT_CLUSTER}}" --service "${ECS_BACKEND_SERVICE}" + - aws ecs update-service --force-new-deployment --cluster "${ECS_PUBLIC_CLUSTER:-${ECS_DEFAULT_CLUSTER}}" --service "${ECS_PUBLIC_SERVICE}" + - aws ecs update-service --force-new-deployment --cluster "${ECS_PARTNER_CLUSTER:-${ECS_DEFAULT_CLUSTER}}" --service "${ECS_PARTNER_SERVICE}" + - aws ecs update-service --force-new-deployment --cluster "${ECS_BACKEND_CLUSTER:-${ECS_DEFAULT_CLUSTER}}" --service "${ECS_PRIVATE_BACKEND_SERVICE}" From f271f1cb0b4c075b1af2207f1c4c570c77defe31 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 18 Sep 2025 12:38:47 -0700 Subject: [PATCH 203/217] fix: back to dev2 --- cdk/bin/doorway-cdk.ts | 2 +- cdk/{dev.env => dev2.env} | 0 .../doorway-build-pipeline-stack.ts | 20 +++++++++---------- .../build-pipeline/doorway-docker-build.ts | 5 ++--- .../services/doorway-partners-site.ts | 2 +- .../services/doorway-public-site.ts | 2 +- 6 files changed, 15 insertions(+), 16 deletions(-) rename cdk/{dev.env => dev2.env} (100%) diff --git a/cdk/bin/doorway-cdk.ts b/cdk/bin/doorway-cdk.ts index 3258e129fd0..fc326fd8034 100644 --- a/cdk/bin/doorway-cdk.ts +++ b/cdk/bin/doorway-cdk.ts @@ -8,7 +8,7 @@ import { DoorwayCloudFrontStack } from "../lib/doorway_environment/doorway-cloud * stacks that make up the Doorway Application infrastructure and build pipeline. **/ const app = new cdk.App() -const environment = process.env.ENVIRONMENT || "dev" +const environment = process.env.ENVIRONMENT || "dev2" /** This stack creates the doorway application build pipeline **/ diff --git a/cdk/dev.env b/cdk/dev2.env similarity index 100% rename from cdk/dev.env rename to cdk/dev2.env diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 3e534bf14ac..1096e3650bb 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -119,7 +119,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, - environment: "dev" + environment: "dev2" }).action, new ImportDockerBuild(this, "doorway-import-listings", { buildspec: "../ci/buildspec/build_import_listings.yml", @@ -127,39 +127,39 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, - environment: "dev" + environment: "dev2" }).action, - new PartnersDockerBuild(this, `doorway-partners-dev`, { + new PartnersDockerBuild(this, `doorway-partners-dev2`, { buildspec: "../ci/buildspec/build_partners.yml", imageName: "partners", source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, - environment: "dev" + environment: "dev2" }).action, - new PublicDockerBuild(this, "doorway-public-dev", { + new PublicDockerBuild(this, "doorway-public-dev2", { buildspec: "../ci/buildspec/build_public.yml", imageName: "public", source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, - environment: "dev" + environment: "dev2" }).action, ], }) - const dbmigrate = new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev", { - environment: "dev", + const dbmigrate = new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev2", { + environment: "dev2", buildspec: "./ci/buildspec/migrate_stop_backend.yml", source: sourceArtifact, buildRole: buildRole, // This parameter is now ignored }).action - const ecsDeploy = new DoorwayECSDeploy(this, "doorway-ecs-deploy-dev", { + const ecsDeploy = new DoorwayECSDeploy(this, "doorway-ecs-deploy-dev2", { buildspec: "./ci/buildspec/cdk_deploy.yml", source: sourceArtifact, buildRole: buildRole, - environment: "dev", + environment: "dev2", }).action pipeline.addStage({ diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts index e10ff3e3f47..fe8eb280fc4 100644 --- a/cdk/lib/build-pipeline/doorway-docker-build.ts +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -6,7 +6,6 @@ import { Repository } from "aws-cdk-lib/aws-ecr" import { Role } from "aws-cdk-lib/aws-iam" import { Construct } from "constructs/lib/construct" import fs from "fs" -import path from "path" import * as YAML from "yaml" export interface DoorwayDockerBuildProps { @@ -68,7 +67,7 @@ class DoorwayDockerBuild { export class PartnersDockerBuild extends DoorwayDockerBuild { constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { super(scope, id, props) - if (props.environment.startsWith("dev")) { + if (props.environment.startsWith("dev2")) { // Grant permissions to the provided role const repo = new Repository(scope, `${id}-ECRRepository`, { repositoryName: `doorway/${props.imageName}`, @@ -83,7 +82,7 @@ export class PartnersDockerBuild extends DoorwayDockerBuild { export class PublicDockerBuild extends DoorwayDockerBuild { constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { super(scope, id, props) - if (props.environment.startsWith("dev")) { + if (props.environment.startsWith("dev2")) { // Grant permissions to the provided role const repo = new Repository(scope, `${id}-ECRRepository`, { repositoryName: `doorway/${props.imageName}`, diff --git a/cdk/lib/doorway_environment/services/doorway-partners-site.ts b/cdk/lib/doorway_environment/services/doorway-partners-site.ts index d47d9ae10be..7fc90db3614 100644 --- a/cdk/lib/doorway_environment/services/doorway-partners-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-partners-site.ts @@ -61,7 +61,7 @@ export class DoorwayPartnersSite { LOG_LEVEL: process.env.LOG_LEVEL || "info", NEXTJS_PORT: String(port), MAPBOX_TOKEN: process.env.MAPBOX_TOKEN || "", - NODE_ENV: process.env.NODE_ENV || "development", + NODE_ENV: process.env.NODE_ENV || "dev2elopment", SHOW_DUPLICATES: process.env.SHOW_DUPLICATES || "FALSE", SHOW_LM_LINKS: process.env.SHOW_LM_LINKS || "TRUE", USE_SECURE_DOWNLOAD_PATHWAY: process.env.USE_SECURE_DOWNLOAD_PATHWAY || "TRUE", diff --git a/cdk/lib/doorway_environment/services/doorway-public-site.ts b/cdk/lib/doorway_environment/services/doorway-public-site.ts index 1aa6d3542ad..750d026e506 100644 --- a/cdk/lib/doorway_environment/services/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-public-site.ts @@ -63,7 +63,7 @@ export class DoorwayPublicSite { LANGUAGES: process.env.LANGUAGES || "en,es,zh,vi,tl", LISTINGS_QUERY: process.env.LISTINGS_QUERY || "/listings", NEXTJS_PORT: String(port), - NODE_ENV: process.env.NODE_ENV || "development", + NODE_ENV: process.env.NODE_ENV || "dev2elopment", NOTIFICATIONS_SIGNUP_URL: process.env.NOTIFICATIONS_SIGNUP_URL || "https://public.govdelivery.com/accounts/CAMTC/signup/36832", From 52e51358eb97a2037862961c971b49cdbe2db540 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 18 Sep 2025 14:09:50 -0700 Subject: [PATCH 204/217] fix: adding cloudfront --- ci/buildspec/cdk_deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/buildspec/cdk_deploy.yml b/ci/buildspec/cdk_deploy.yml index d99663fef27..4e8f2115e11 100644 --- a/ci/buildspec/cdk_deploy.yml +++ b/ci/buildspec/cdk_deploy.yml @@ -12,4 +12,4 @@ phases: - npm install -g aws-cdk - cd ${CODEBUILD_SRC_DIR}/cdk - yarn install - - cdk deploy DoorwayAppEnvironmentStack-${ENVIRONMENT} --require-approval never + - cdk deploy DoorwayCloudFrontStack-${ENVIRONMENT} --require-approval never From da976041776103a2e68d6962961ea14fda7bdcd7 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 18 Sep 2025 15:35:50 -0700 Subject: [PATCH 205/217] fix: bad env variable in build --- cdk/lib/build-pipeline/doorway-docker-build.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts index fe8eb280fc4..57825654274 100644 --- a/cdk/lib/build-pipeline/doorway-docker-build.ts +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -8,6 +8,7 @@ import { Construct } from "constructs/lib/construct" import fs from "fs" import * as YAML from "yaml" + export interface DoorwayDockerBuildProps { buildspec: string imageName: string @@ -33,7 +34,7 @@ class DoorwayDockerBuild { IMAGE_NAME: { value: props.imageName }, DOCKER_HUB_SECRET_ARN: { value: props.dockerHubSecret }, ENVIRONMENT: { value: props.environment }, - BACKEND_API_BASE: { value: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int:3100` }, + BACKEND_API_BASE: { value: process.env.BACKEND_API_BASE || `http://backend.${props.environment}.housingbayarea.int` }, CACHE_REVALIDATE: { value: process.env.CACHE_REVALIDATE || "30" }, LANGUAGES: { value: process.env.LANGUAGES || "en,es,zh,vi,tl" From eba1b85e774a8ea9550f48f45e13e80a942b9385 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 18 Sep 2025 19:29:40 -0700 Subject: [PATCH 206/217] fix: prod! --- cdk/cdk.context.json | 5 ++- .../doorway-build-pipeline-stack.ts | 42 +++++++++++++++++++ cdk/prod.env | 35 ++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 cdk/prod.env diff --git a/cdk/cdk.context.json b/cdk/cdk.context.json index 2a2ba61df13..3dc80320fc9 100644 --- a/cdk/cdk.context.json +++ b/cdk/cdk.context.json @@ -10,5 +10,8 @@ "hosted-zone:account=364076391763:domainName=housingbayarea.mtc.ca.gov:region=us-west-1": { "Id": "/hostedzone/Z01682742VM0KIXZ4Y3W5", "Name": "housingbayarea.mtc.ca.gov." - } + }, + "acknowledged-issue-numbers": [ + 34892 + ] } diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 1096e3650bb..a073d3cc199 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -207,7 +207,49 @@ export class DoorwayBuildPipelineStack extends Stack { stageName: "Staging", actions: [dbmigrateStaging, ecsDeployStaging], }) + pipeline.addStage({ + stageName: "Build-Prod", + actions: [ + + new PartnersDockerBuild(this, `doorway-partners-prod`, { + buildspec: "../ci/buildspec/build_partners.yml", + imageName: "partners", + source: sourceArtifact, + dockerHubSecret: dockerSecret, + buildRole: buildRole, + environment: "prod" + }).action, + new PublicDockerBuild(this, "doorway-public-prod", { + buildspec: "../ci/buildspec/build_public.yml", + imageName: "public", + source: sourceArtifact, + dockerHubSecret: dockerSecret, + buildRole: buildRole, + environment: "prod" + }).action, + ], + }) + + const dbmigrateProd = new DoorwayDatabaseMigrate(this, "doorway-database-migrate-prod", { + environment: "prod", + buildspec: "./ci/buildspec/migrate.yml", + source: sourceArtifact, + buildRole: buildRole, + }).action; + + const ecsDeployProd = new DoorwayECSDeploy(this, "doorway-ecs-deploy-prod", { + buildspec: "./ci/buildspec/cdk_deploy.yml", + source: sourceArtifact, + buildRole: buildRole, + environment: "prod", + }).action + + pipeline.addStage({ + stageName: "Prod", + actions: [dbmigrateProd, ecsDeployProd], + }) } + } export interface PipelineProps extends StackProps { githubSecret: string diff --git a/cdk/prod.env b/cdk/prod.env new file mode 100644 index 00000000000..b673b8a1e2a --- /dev/null +++ b/cdk/prod.env @@ -0,0 +1,35 @@ +MAINTENANCE_WINDOW=2024-03-26 18:00 -08:00,2024-04-25 18:00 -08:00 +DEST_REPO_NAMESPACE=doorway +CACHE_REVALIDATE=60 +LANGUAGES=en,es,zh,vi,tl +FEATURE_LISTINGS_APPROVAL=TRUE + +ECR_ACCOUNT_ID=364076391763 +ECR_NAMESPACE=doorway +ECR_REGION=us-west-2 +CORS_ORIGINS=https://prod2.housingbayarea.mtc.ca.gov, https://partners.prod2.housingbayarea.mtc.ca.gov + +USE_SECURE_DOWNLOAD_PATHWAY=TRUE +GTM_KEY=GTM-KF22FJP +SOURCE_REPO_NAMESPACE=doorway +IDLE_TIMEOUT=5 +CLOUDINARY_CLOUD_NAME=exygy +APP_SECRET="" + +PUBLIC_PORTAL_PORT=3000 +PUBLIC_PORTAL_DOMAIN=prod2.housingbayarea.mtc.ca.gov +PUBLIC_PORTAL_SECRETS=GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY +DISABLE_CORS=FALSE +SAME_SITE=true +HTTPS_OFF=true + + +PARTNERS_PORTAL_PORT=3200 +PARTNERS_PORTAL_DOMAIN=partners.prod2.housingbayarea.mtc.ca.gov +BACKEND_API_DOMAIN=backend.prod2.housingbayarea.int +BACKEND_API_BASE=http://backend.prod2.housingbayarea.int +BACKEND_API_PORT=3100 +BACKEND_API_SECRETS=CLOUDINARY_KEY,CLOUDINARY_SECRET,EMAIL_API_KEY,GOOGLE_API_ID,GOOGLE_API_KEY,GOOGLE_MAPS_MAP_ID,GOOGLE_MAPS_API_KEY,GOVDELIVERY_PASSWORD,GOVDELIVERY_USERNAME,GOVDELIVERY_API_URL + +LOG_LEVEL=info + From 92f44d620b2f8eb557f396f41b42c0f997ade984 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 18 Sep 2025 20:49:48 -0700 Subject: [PATCH 207/217] fix: dev2->dev --- cdk/bin/doorway-cdk.ts | 2 +- cdk/{dev2.env => dev.env} | 0 .../doorway-build-pipeline-stack.ts | 21 +++++++++---------- .../build-pipeline/doorway-docker-build.ts | 4 ++-- .../services/doorway-partners-site.ts | 2 +- .../services/doorway-public-site.ts | 2 +- 6 files changed, 15 insertions(+), 16 deletions(-) rename cdk/{dev2.env => dev.env} (100%) diff --git a/cdk/bin/doorway-cdk.ts b/cdk/bin/doorway-cdk.ts index fc326fd8034..3258e129fd0 100644 --- a/cdk/bin/doorway-cdk.ts +++ b/cdk/bin/doorway-cdk.ts @@ -8,7 +8,7 @@ import { DoorwayCloudFrontStack } from "../lib/doorway_environment/doorway-cloud * stacks that make up the Doorway Application infrastructure and build pipeline. **/ const app = new cdk.App() -const environment = process.env.ENVIRONMENT || "dev2" +const environment = process.env.ENVIRONMENT || "dev" /** This stack creates the doorway application build pipeline **/ diff --git a/cdk/dev2.env b/cdk/dev.env similarity index 100% rename from cdk/dev2.env rename to cdk/dev.env diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index a073d3cc199..218a81d4c5b 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -4,7 +4,6 @@ import { GitHubSourceAction, GitHubTrigger } from "aws-cdk-lib/aws-codepipeline- import { PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Secret } from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" - import { DoorwayDatabaseMigrate } from "./doorway-database-migrate" import { BackendDockerBuild, ImportDockerBuild, PartnersDockerBuild, PublicDockerBuild } from "./doorway-docker-build" import { DoorwayECSDeploy } from "./doorway-ecs-deploy" @@ -119,7 +118,7 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, - environment: "dev2" + environment: "dev" }).action, new ImportDockerBuild(this, "doorway-import-listings", { buildspec: "../ci/buildspec/build_import_listings.yml", @@ -127,39 +126,39 @@ export class DoorwayBuildPipelineStack extends Stack { source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, - environment: "dev2" + environment: "dev" }).action, - new PartnersDockerBuild(this, `doorway-partners-dev2`, { + new PartnersDockerBuild(this, `doorway-partners-dev`, { buildspec: "../ci/buildspec/build_partners.yml", imageName: "partners", source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, - environment: "dev2" + environment: "dev" }).action, - new PublicDockerBuild(this, "doorway-public-dev2", { + new PublicDockerBuild(this, "doorway-public-dev", { buildspec: "../ci/buildspec/build_public.yml", imageName: "public", source: sourceArtifact, dockerHubSecret: dockerSecret, buildRole: buildRole, - environment: "dev2" + environment: "dev" }).action, ], }) - const dbmigrate = new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev2", { - environment: "dev2", + const dbmigrate = new DoorwayDatabaseMigrate(this, "doorway-database-migrate-dev", { + environment: "dev", buildspec: "./ci/buildspec/migrate_stop_backend.yml", source: sourceArtifact, buildRole: buildRole, // This parameter is now ignored }).action - const ecsDeploy = new DoorwayECSDeploy(this, "doorway-ecs-deploy-dev2", { + const ecsDeploy = new DoorwayECSDeploy(this, "doorway-ecs-deploy-dev", { buildspec: "./ci/buildspec/cdk_deploy.yml", source: sourceArtifact, buildRole: buildRole, - environment: "dev2", + environment: "dev", }).action pipeline.addStage({ diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts index 57825654274..647c7e1be1b 100644 --- a/cdk/lib/build-pipeline/doorway-docker-build.ts +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -68,7 +68,7 @@ class DoorwayDockerBuild { export class PartnersDockerBuild extends DoorwayDockerBuild { constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { super(scope, id, props) - if (props.environment.startsWith("dev2")) { + if (props.environment.startsWith("dev")) { // Grant permissions to the provided role const repo = new Repository(scope, `${id}-ECRRepository`, { repositoryName: `doorway/${props.imageName}`, @@ -83,7 +83,7 @@ export class PartnersDockerBuild extends DoorwayDockerBuild { export class PublicDockerBuild extends DoorwayDockerBuild { constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { super(scope, id, props) - if (props.environment.startsWith("dev2")) { + if (props.environment.startsWith("dev")) { // Grant permissions to the provided role const repo = new Repository(scope, `${id}-ECRRepository`, { repositoryName: `doorway/${props.imageName}`, diff --git a/cdk/lib/doorway_environment/services/doorway-partners-site.ts b/cdk/lib/doorway_environment/services/doorway-partners-site.ts index 7fc90db3614..d47d9ae10be 100644 --- a/cdk/lib/doorway_environment/services/doorway-partners-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-partners-site.ts @@ -61,7 +61,7 @@ export class DoorwayPartnersSite { LOG_LEVEL: process.env.LOG_LEVEL || "info", NEXTJS_PORT: String(port), MAPBOX_TOKEN: process.env.MAPBOX_TOKEN || "", - NODE_ENV: process.env.NODE_ENV || "dev2elopment", + NODE_ENV: process.env.NODE_ENV || "development", SHOW_DUPLICATES: process.env.SHOW_DUPLICATES || "FALSE", SHOW_LM_LINKS: process.env.SHOW_LM_LINKS || "TRUE", USE_SECURE_DOWNLOAD_PATHWAY: process.env.USE_SECURE_DOWNLOAD_PATHWAY || "TRUE", diff --git a/cdk/lib/doorway_environment/services/doorway-public-site.ts b/cdk/lib/doorway_environment/services/doorway-public-site.ts index 750d026e506..1aa6d3542ad 100644 --- a/cdk/lib/doorway_environment/services/doorway-public-site.ts +++ b/cdk/lib/doorway_environment/services/doorway-public-site.ts @@ -63,7 +63,7 @@ export class DoorwayPublicSite { LANGUAGES: process.env.LANGUAGES || "en,es,zh,vi,tl", LISTINGS_QUERY: process.env.LISTINGS_QUERY || "/listings", NEXTJS_PORT: String(port), - NODE_ENV: process.env.NODE_ENV || "dev2elopment", + NODE_ENV: process.env.NODE_ENV || "development", NOTIFICATIONS_SIGNUP_URL: process.env.NOTIFICATIONS_SIGNUP_URL || "https://public.govdelivery.com/accounts/CAMTC/signup/36832", From 4a54f54d54e77996bb5c40797dcbeec6f482ad93 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Thu, 18 Sep 2025 20:50:42 -0700 Subject: [PATCH 208/217] fix: temporarily remove db migrate --- cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 218a81d4c5b..7f70eef70c5 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -4,6 +4,7 @@ import { GitHubSourceAction, GitHubTrigger } from "aws-cdk-lib/aws-codepipeline- import { PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam" import { Secret } from "aws-cdk-lib/aws-secretsmanager" import { Construct } from "constructs" + import { DoorwayDatabaseMigrate } from "./doorway-database-migrate" import { BackendDockerBuild, ImportDockerBuild, PartnersDockerBuild, PublicDockerBuild } from "./doorway-docker-build" import { DoorwayECSDeploy } from "./doorway-ecs-deploy" @@ -163,7 +164,7 @@ export class DoorwayBuildPipelineStack extends Stack { pipeline.addStage({ stageName: "Dev", - actions: [dbmigrate, ecsDeploy], + actions: [ecsDeploy], }) pipeline.addStage({ stageName: "Build-Staging", From bc35592f89a03c6bfcb3aa03b310ba010e2471d0 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 19 Sep 2025 11:01:08 -0700 Subject: [PATCH 209/217] fix: adding dev dbmigrate back --- cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 7f70eef70c5..038fcc53b44 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -164,7 +164,7 @@ export class DoorwayBuildPipelineStack extends Stack { pipeline.addStage({ stageName: "Dev", - actions: [ecsDeploy], + actions: [dbmigrate, ecsDeploy], }) pipeline.addStage({ stageName: "Build-Staging", From e4a9e2727ce99b095ef4643427667aa5458a523a Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Fri, 19 Sep 2025 12:55:40 -0700 Subject: [PATCH 210/217] fix: correcting url issues --- cdk/lib/build-pipeline/doorway-docker-build.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cdk/lib/build-pipeline/doorway-docker-build.ts b/cdk/lib/build-pipeline/doorway-docker-build.ts index 647c7e1be1b..3806ea41d07 100644 --- a/cdk/lib/build-pipeline/doorway-docker-build.ts +++ b/cdk/lib/build-pipeline/doorway-docker-build.ts @@ -5,10 +5,11 @@ import { CodeBuildAction } from "aws-cdk-lib/aws-codepipeline-actions" import { Repository } from "aws-cdk-lib/aws-ecr" import { Role } from "aws-cdk-lib/aws-iam" import { Construct } from "constructs/lib/construct" +import dotenv from "dotenv" import fs from "fs" +import path from "path" import * as YAML from "yaml" - export interface DoorwayDockerBuildProps { buildspec: string imageName: string @@ -23,7 +24,16 @@ class DoorwayDockerBuild { + constructor(scope: Construct, id: string, props: DoorwayDockerBuildProps) { + console.log(`Creating Application stack for ${props.environment}`) + console.log(`Using environment file: ${path.resolve(__dirname, `../../${props.environment}.env`)}`) + + + dotenv.config({ + path: path.resolve(__dirname, `../../${props.environment}.env`), + override: true + }) // Create ECR repository From b617e1499e16e77dfcd74200baf9b483ba00bcd5 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Tue, 23 Sep 2025 16:52:00 -0700 Subject: [PATCH 211/217] feat: make ses region configurable --- api/src/services/email.service.ts | 60 ++++++++++++++----------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/api/src/services/email.service.ts b/api/src/services/email.service.ts index 2704bff0859..b2b0bcea0e1 100644 --- a/api/src/services/email.service.ts +++ b/api/src/services/email.service.ts @@ -1,34 +1,31 @@ +import * as aws from '@aws-sdk/client-sesv2'; +import { HttpService } from '@nestjs/axios'; import { Inject, Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { HttpService } from '@nestjs/axios'; +import { + LanguagesEnum, + ListingEventsTypeEnum, + ReviewOrderTypeEnum, +} from '@prisma/client'; +import dayjs from 'dayjs'; +import advanced from 'dayjs/plugin/advancedFormat'; +import tz from 'dayjs/plugin/timezone'; +import utc from 'dayjs/plugin/utc'; import fs from 'fs'; import Handlebars from 'handlebars'; import juice from 'juice'; import Polyglot from 'node-polyglot'; -import { firstValueFrom } from 'rxjs'; import path from 'path'; -import dayjs from 'dayjs'; -import utc from 'dayjs/plugin/utc'; -import tz from 'dayjs/plugin/timezone'; -import advanced from 'dayjs/plugin/advancedFormat'; -import * as aws from '@aws-sdk/client-sesv2'; -import { TranslationService } from './translation.service'; -import { JurisdictionService } from './jurisdiction.service'; +import { firstValueFrom } from 'rxjs'; +import { ApplicationCreate } from '../dtos/applications/application-create.dto'; import { Jurisdiction } from '../dtos/jurisdictions/jurisdiction.dto'; -import { - LanguagesEnum, - ListingEventsTypeEnum, - ReviewOrderTypeEnum, -} from '@prisma/client'; -import { IdDTO } from '../dtos/shared/id.dto'; import { Listing } from '../dtos/listings/listing.dto'; -import { ApplicationCreate } from '../dtos/applications/application-create.dto'; -import { User } from '../dtos/users/user.dto'; +import { IdDTO } from '../dtos/shared/id.dto'; import Unit from '../dtos/units/unit.dto'; +import { User } from '../dtos/users/user.dto'; import { getPublicEmailURL } from '../utilities/get-public-email-url'; - -dayjs.extend(utc); -dayjs.extend(tz); +import { JurisdictionService } from './jurisdiction.service'; +import { TranslationService } from './translation.service'; dayjs.extend(advanced); type listingInfo = { @@ -53,7 +50,7 @@ export class EmailService { private logger = new Logger(EmailService.name), ) { const sesClient = new aws.SESv2Client({ - region: process.env.AWS_REGION, + region: process.env.SES_REGION || process.env.AWS_REGION, }); this.client = sesClient; @@ -138,7 +135,7 @@ export class EmailService { // juice inlines css to allow for email styling const inlineHtml = juice(rawHtml); - const govEmailXml = `\n ${subject}\n \n ${subject}\n \n \n ${fromEmailAddress}\n false\n true\n true\n true\n \n \n ${GOVDELIVERY_TOPIC}\n \n \n \n `; await firstValueFrom( @@ -177,8 +174,8 @@ export class EmailService { }, Text: mailOptions.text ? { - Data: mailOptions.text, - } + Data: mailOptions.text, + } : undefined, }, }, @@ -659,9 +656,8 @@ export class EmailService { ); tableRows.push({ label: this.polyglot.t(`rentalOpportunity.${key}`), - value: `${bedroom?.length} unit${ - bedroom.length > 1 ? 's' : '' - }${bathroomstring}${sqFtString}`, + value: `${bedroom?.length} unit${bedroom.length > 1 ? 's' : '' + }${bathroomstring}${sqFtString}`, }); }); if (units?.rent?.length) { @@ -854,9 +850,8 @@ export class EmailService { formatPricing = (values: number[]): string => { const minPrice = Math.min(...values); const maxPrice = Math.max(...values); - return `$${minPrice.toLocaleString()}${ - minPrice !== maxPrice ? ' - $' + maxPrice.toLocaleString() : '' - } per month`; + return `$${minPrice.toLocaleString()}${minPrice !== maxPrice ? ' - $' + maxPrice.toLocaleString() : '' + } per month`; }; formatUnitDetails = ( @@ -874,9 +869,8 @@ export class EmailService { if (mappedField?.length) { const minValue = Math.min(...mappedField); const maxValue = Math.max(...mappedField); - return `, ${minValue.toLocaleString()}${ - minValue !== maxValue ? ' - ' + maxValue.toLocaleString() : '' - } ${pluralLabel && maxValue === 1 ? pluralLabel : label}`; + return `, ${minValue.toLocaleString()}${minValue !== maxValue ? ' - ' + maxValue.toLocaleString() : '' + } ${pluralLabel && maxValue === 1 ? pluralLabel : label}`; } return ''; }; From 1b50ff97edac126106861f09796ff6788ef4db66 Mon Sep 17 00:00:00 2001 From: Morgan Ludtke <42942267+ludtkemorgan@users.noreply.github.com> Date: Fri, 26 Sep 2025 12:11:07 -0500 Subject: [PATCH 212/217] add additional logging (#1429) --- sites/public/src/pages/listing/[id].tsx | 1 + sites/public/src/pages/listing/[id]/[slug].tsx | 2 ++ 2 files changed, 3 insertions(+) diff --git a/sites/public/src/pages/listing/[id].tsx b/sites/public/src/pages/listing/[id].tsx index b4f028998f4..f82c9a98b64 100644 --- a/sites/public/src/pages/listing/[id].tsx +++ b/sites/public/src/pages/listing/[id].tsx @@ -32,6 +32,7 @@ export async function getServerSideProps(context: { headers, }) } catch (e) { + console.log(e) return { notFound: true } } diff --git a/sites/public/src/pages/listing/[id]/[slug].tsx b/sites/public/src/pages/listing/[id]/[slug].tsx index 97f5362fec4..3f97794cc5d 100644 --- a/sites/public/src/pages/listing/[id]/[slug].tsx +++ b/sites/public/src/pages/listing/[id]/[slug].tsx @@ -100,6 +100,7 @@ export const getStaticProps: GetStaticProps = async (context: { const listingServiceUrl = runtimeConfig.getListingServiceUrl() try { + console.log(`requesting URL: ${listingServiceUrl}/${context.params.id}`) response = await axios.get(`${listingServiceUrl}/${context.params.id}`, { headers: { language: context.locale, @@ -110,6 +111,7 @@ export const getStaticProps: GetStaticProps = async (context: { console.error("slug notFound Error:", e) return { notFound: true, revalidate: Number(process.env.cacheRevalidate) } } + console.log("response data", response.data) return { props: { listing: response.data, From 0c559a3fbc5586fd2e44c27b26dc40ff422253e2 Mon Sep 17 00:00:00 2001 From: Morgan Ludtke <42942267+ludtkemorgan@users.noreply.github.com> Date: Fri, 26 Sep 2025 14:00:44 -0500 Subject: [PATCH 213/217] fix: switch log from console to logger (#1430) --- sites/public/src/pages/listing/[id]/[slug].tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sites/public/src/pages/listing/[id]/[slug].tsx b/sites/public/src/pages/listing/[id]/[slug].tsx index 3f97794cc5d..13fdd772a57 100644 --- a/sites/public/src/pages/listing/[id]/[slug].tsx +++ b/sites/public/src/pages/listing/[id]/[slug].tsx @@ -18,6 +18,7 @@ import dayjs from "dayjs" import { runtimeConfig } from "../../../lib/runtime-config" import { Jurisdiction, Listing } from "@bloom-housing/shared-helpers/src/types/backend-swagger" import { ListingViewSeeds } from "../../../components/listing/ListingViewSeeds" +import { logger } from "../../../logger" interface ListingProps { listing: Listing @@ -100,7 +101,7 @@ export const getStaticProps: GetStaticProps = async (context: { const listingServiceUrl = runtimeConfig.getListingServiceUrl() try { - console.log(`requesting URL: ${listingServiceUrl}/${context.params.id}`) + logger.info(`requesting URL: ${listingServiceUrl}/${context.params.id}`) response = await axios.get(`${listingServiceUrl}/${context.params.id}`, { headers: { language: context.locale, @@ -108,10 +109,10 @@ export const getStaticProps: GetStaticProps = async (context: { }, }) } catch (e) { - console.error("slug notFound Error:", e) + logger.error("slug notFound Error:", e) return { notFound: true, revalidate: Number(process.env.cacheRevalidate) } } - console.log("response data", response.data) + logger.info("response data", response.data) return { props: { listing: response.data, From f700a8845f468c08e49f642f104d05174a003c2d Mon Sep 17 00:00:00 2001 From: Morgan Ludtke Date: Wed, 29 Oct 2025 09:16:17 -0500 Subject: [PATCH 214/217] fix: try some more logging --- .../page_components/listing/ListingCard.tsx | 2 +- sites/public/logger.ts | 8 ++-- .../src/components/core/LinkComponent.tsx | 2 +- sites/public/src/lib/runtime-config.ts | 2 +- .../public/src/pages/listing/[id]/[slug].tsx | 4 +- sites/public/src/pages/listing/ext/[id].tsx | 44 ++++++++++++++++--- 6 files changed, 49 insertions(+), 13 deletions(-) diff --git a/doorway-ui-components/src/page_components/listing/ListingCard.tsx b/doorway-ui-components/src/page_components/listing/ListingCard.tsx index 019993d8ff2..da92dc0bbaa 100644 --- a/doorway-ui-components/src/page_components/listing/ListingCard.tsx +++ b/doorway-ui-components/src/page_components/listing/ListingCard.tsx @@ -139,7 +139,7 @@ const ListingCard = (props: ListingCardProps) => { return ( {header.href ? ( - + {header.content} ) : ( diff --git a/sites/public/logger.ts b/sites/public/logger.ts index ab02d2ca7b0..c5e6fe6207d 100644 --- a/sites/public/logger.ts +++ b/sites/public/logger.ts @@ -1,8 +1,8 @@ import * as winston from "winston" -let log_level = "info" -if (process.env.LOG_LEVEL) { - log_level = process.env.LOG_LEVEL -} +const log_level = "info" +// if (process.env.LOG_LEVEL) { +// log_level = process.env.LOG_LEVEL +// } const logger = winston.createLogger({ level: log_level, format: winston.format.json(), diff --git a/sites/public/src/components/core/LinkComponent.tsx b/sites/public/src/components/core/LinkComponent.tsx index 6f8d1c064dd..f27315fd3eb 100644 --- a/sites/public/src/components/core/LinkComponent.tsx +++ b/sites/public/src/components/core/LinkComponent.tsx @@ -6,7 +6,7 @@ const LinkComponent = (props: PropsWithChildren + return } export default LinkComponent diff --git a/sites/public/src/lib/runtime-config.ts b/sites/public/src/lib/runtime-config.ts index 0d43de114f1..01e0bcb7ba6 100644 --- a/sites/public/src/lib/runtime-config.ts +++ b/sites/public/src/lib/runtime-config.ts @@ -60,7 +60,7 @@ export const runtimeConfig = { }, getListingServiceUrl() { - return `${this.getBackendApiBase()}${this.env.LISTINGS_QUERY}` + return `${this.getBackendApiBase()}/listings` }, getJurisdictionName() { diff --git a/sites/public/src/pages/listing/[id]/[slug].tsx b/sites/public/src/pages/listing/[id]/[slug].tsx index 13fdd772a57..ad7d42e06f2 100644 --- a/sites/public/src/pages/listing/[id]/[slug].tsx +++ b/sites/public/src/pages/listing/[id]/[slug].tsx @@ -33,6 +33,8 @@ export default function ListingPage(props: ListingProps) { const pageTitle = `${listing.name} - ${t("nav.siteTitle")}` const { profile } = useContext(AuthContext) + // logger.info(`listing name - ${listing.name}`) + useEffect(() => { if (!listing.id) return pushGtmEvent({ @@ -112,7 +114,7 @@ export const getStaticProps: GetStaticProps = async (context: { logger.error("slug notFound Error:", e) return { notFound: true, revalidate: Number(process.env.cacheRevalidate) } } - logger.info("response data", response.data) + logger.info(`response data - ${response.data?.name}`) return { props: { listing: response.data, diff --git a/sites/public/src/pages/listing/ext/[id].tsx b/sites/public/src/pages/listing/ext/[id].tsx index 6504a06b2b8..ea55dc65a19 100644 --- a/sites/public/src/pages/listing/ext/[id].tsx +++ b/sites/public/src/pages/listing/ext/[id].tsx @@ -17,6 +17,7 @@ import { ErrorPage } from "../../_error" import dayjs from "dayjs" import { runtimeConfig } from "../../../lib/runtime-config" import { Jurisdiction, Listing } from "@bloom-housing/shared-helpers/src/types/backend-swagger" +import { logger } from "../../../logger" interface ListingProps { listing: Listing @@ -84,21 +85,25 @@ export default function ListingPage(props: ListingProps) { ) } -export const getStaticPaths: GetStaticPaths = () => { - return { paths: [], fallback: "blocking" } -} +// export const getStaticPaths: GetStaticPaths = () => { +// return { paths: [], fallback: "blocking" } +// } -export const getStaticProps: GetStaticProps = async (context: { +export async function getServerSideProps(context: { params: Record locale: string -}) => { +}) { let response: AxiosResponse try { + logger.info( + `requesting URL: ${process.env.BLOOM_API_BASE}/listings/external/${context.params.id}` + ) const extUrl = `${process.env.BLOOM_API_BASE}/listings/external/${context.params.id}` response = await axios.get(extUrl, { headers: { language: context.locale }, }) } catch (e) { + logger.error("external notFound Error") return { notFound: true } } @@ -112,3 +117,32 @@ export const getStaticProps: GetStaticProps = async (context: { revalidate: Number(process.env.cacheRevalidate), } } + +// export const getStaticProps: GetStaticProps = async (context: { +// params: Record +// locale: string +// }) => { +// let response: AxiosResponse +// try { +// logger.info( +// `requesting URL: ${process.env.BLOOM_API_BASE}/listings/external/${context.params.id}` +// ) +// const extUrl = `${process.env.BLOOM_API_BASE}/listings/external/${context.params.id}` +// response = await axios.get(extUrl, { +// headers: { language: context.locale }, +// }) +// } catch (e) { +// logger.error("external notFound Error") +// return { notFound: true } +// } + +// return { +// props: { +// listing: response.data, +// jurisdiction: response.data.jurisdiction, +// googleMapsApiKey: runtimeConfig.getGoogleMapsApiKey() || null, +// googleMapsMapId: runtimeConfig.getGoogleMapsMapId() || null, +// }, +// revalidate: Number(process.env.cacheRevalidate), +// } +// } From 672189e21cbeb20f1ef90a7776c97eeeb83ce952 Mon Sep 17 00:00:00 2001 From: Morgan Ludtke Date: Wed, 29 Oct 2025 15:23:41 -0500 Subject: [PATCH 215/217] fix: switch to serversideprops --- .../public/src/pages/listing/[id]/[slug].tsx | 22 ++++++++++--------- sites/public/src/pages/listing/ext/[id].tsx | 4 +++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/sites/public/src/pages/listing/[id]/[slug].tsx b/sites/public/src/pages/listing/[id]/[slug].tsx index ad7d42e06f2..dde95533b79 100644 --- a/sites/public/src/pages/listing/[id]/[slug].tsx +++ b/sites/public/src/pages/listing/[id]/[slug].tsx @@ -91,28 +91,30 @@ export default function ListingPage(props: ListingProps) { ) } -export const getStaticPaths: GetStaticPaths = () => { - return { paths: [], fallback: "blocking" } -} +// export const getStaticPaths: GetStaticPaths = () => { +// return { paths: [], fallback: "blocking" } +// } -export const getStaticProps: GetStaticProps = async (context: { +export async function getServerSideProps(context: { params: Record locale: string -}) => { +}) { let response const listingServiceUrl = runtimeConfig.getListingServiceUrl() try { - logger.info(`requesting URL: ${listingServiceUrl}/${context.params.id}`) - response = await axios.get(`${listingServiceUrl}/${context.params.id}`, { + logger.info(`requesting URL: ${listingServiceUrl}s/${context.params.id}`) + logger.info(`context.locale: ${context.locale}`) + response = await axios.get(`${listingServiceUrl}s/${context.params.id}`, { headers: { language: context.locale, passkey: process.env.API_PASS_KEY, }, }) } catch (e) { - logger.error("slug notFound Error:", e) - return { notFound: true, revalidate: Number(process.env.cacheRevalidate) } + logger.error("slug notFound Error:") + logger.error(e) + return { notFound: true } } logger.info(`response data - ${response.data?.name}`) return { @@ -122,6 +124,6 @@ export const getStaticProps: GetStaticProps = async (context: { googleMapsApiKey: runtimeConfig.getGoogleMapsApiKey() || null, googleMapsMapId: runtimeConfig.getGoogleMapsMapId() || null, }, - revalidate: Number(process.env.cacheRevalidate), + // revalidate: Number(process.env.cacheRevalidate), } } diff --git a/sites/public/src/pages/listing/ext/[id].tsx b/sites/public/src/pages/listing/ext/[id].tsx index ea55dc65a19..75bc3ca7855 100644 --- a/sites/public/src/pages/listing/ext/[id].tsx +++ b/sites/public/src/pages/listing/ext/[id].tsx @@ -98,12 +98,14 @@ export async function getServerSideProps(context: { logger.info( `requesting URL: ${process.env.BLOOM_API_BASE}/listings/external/${context.params.id}` ) + logger.info(`context.locale: ${context.locale}`) const extUrl = `${process.env.BLOOM_API_BASE}/listings/external/${context.params.id}` response = await axios.get(extUrl, { headers: { language: context.locale }, }) } catch (e) { logger.error("external notFound Error") + logger.error(e) return { notFound: true } } @@ -114,7 +116,7 @@ export async function getServerSideProps(context: { googleMapsApiKey: runtimeConfig.getGoogleMapsApiKey() || null, googleMapsMapId: runtimeConfig.getGoogleMapsMapId() || null, }, - revalidate: Number(process.env.cacheRevalidate), + // revalidate: Number(process.env.cacheRevalidate), } } From 1f19d49fc55044d8601af96127361be4a5846b9a Mon Sep 17 00:00:00 2001 From: Morgan Ludtke Date: Wed, 29 Oct 2025 15:47:26 -0500 Subject: [PATCH 216/217] fix: accidental s removed --- sites/public/src/pages/listing/[id]/[slug].tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sites/public/src/pages/listing/[id]/[slug].tsx b/sites/public/src/pages/listing/[id]/[slug].tsx index dde95533b79..5e18bd070bb 100644 --- a/sites/public/src/pages/listing/[id]/[slug].tsx +++ b/sites/public/src/pages/listing/[id]/[slug].tsx @@ -103,9 +103,9 @@ export async function getServerSideProps(context: { const listingServiceUrl = runtimeConfig.getListingServiceUrl() try { - logger.info(`requesting URL: ${listingServiceUrl}s/${context.params.id}`) + logger.info(`requesting URL: ${listingServiceUrl}/${context.params.id}`) logger.info(`context.locale: ${context.locale}`) - response = await axios.get(`${listingServiceUrl}s/${context.params.id}`, { + response = await axios.get(`${listingServiceUrl}/${context.params.id}`, { headers: { language: context.locale, passkey: process.env.API_PASS_KEY, From 382e0fce5ccbfb7cf71ba619b16174a1ac2673b7 Mon Sep 17 00:00:00 2001 From: Glenn Brown Date: Mon, 3 Nov 2025 14:43:33 -0800 Subject: [PATCH 217/217] Merge branch 'main' into feat/tf_to_cdk --- cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts index 038fcc53b44..c96bdf066d1 100644 --- a/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts +++ b/cdk/lib/build-pipeline/doorway-build-pipeline-stack.ts @@ -101,7 +101,7 @@ export class DoorwayBuildPipelineStack extends Stack { oauthToken: githubSecret, owner: "metrotranscom", repo: "doorway", - branch: "feat/tf_to_cdk", + branch: "main", output: sourceArtifact, trigger: GitHubTrigger.WEBHOOK, variablesNamespace: "SourceVariables",