diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 528e7cd..030f592 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -85,6 +85,18 @@ jobs:
steps:
- uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: npm
+ cache-dependency-path: apps/api/package-lock.json
+
+ - name: Build scheduled sync Lambda artifact
+ working-directory: apps/api
+ run: |
+ npm ci
+ npm run build:lambda
+
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.8.5
diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml
index 06a7cf3..4e2d3ae 100644
--- a/.github/workflows/deploy-production.yml
+++ b/.github/workflows/deploy-production.yml
@@ -40,6 +40,12 @@ jobs:
- uses: aws-actions/amazon-ecr-login@v2
id: login-ecr
+ - name: Build scheduled sync Lambda artifact
+ working-directory: apps/api
+ run: |
+ npm ci
+ npm run build:lambda
+
- name: Terraform init
working-directory: ${{ env.TF_ENV_DIR }}
env:
@@ -50,6 +56,7 @@ jobs:
TF_VAR_web_bucket_name: ${{ vars.WEB_BUCKET_NAME }}
TF_VAR_api_image_uri: bootstrap-placeholder
TF_VAR_worker_image_uri: bootstrap-placeholder
+ TF_VAR_scheduled_sync_interval_hours: ${{ vars.SCHEDULED_SYNC_INTERVAL_HOURS || '6' }}
TF_VAR_db_name: ${{ vars.DB_NAME }}
TF_VAR_db_username: ${{ vars.DB_USERNAME }}
TF_VAR_db_password: ${{ secrets.DB_PASSWORD }}
@@ -83,6 +90,7 @@ jobs:
TF_VAR_web_bucket_name: ${{ vars.WEB_BUCKET_NAME }}
TF_VAR_api_image_uri: bootstrap-placeholder
TF_VAR_worker_image_uri: bootstrap-placeholder
+ TF_VAR_scheduled_sync_interval_hours: ${{ vars.SCHEDULED_SYNC_INTERVAL_HOURS || '6' }}
TF_VAR_db_name: ${{ vars.DB_NAME }}
TF_VAR_db_username: ${{ vars.DB_USERNAME }}
TF_VAR_db_password: ${{ secrets.DB_PASSWORD }}
@@ -116,6 +124,7 @@ jobs:
TF_VAR_web_bucket_name: ${{ vars.WEB_BUCKET_NAME }}
TF_VAR_api_image_uri: ${{ steps.tf-bootstrap.outputs.repository_url }}:${{ github.sha }}
TF_VAR_worker_image_uri: ${{ steps.tf-bootstrap.outputs.repository_url }}:${{ github.sha }}
+ TF_VAR_scheduled_sync_interval_hours: ${{ vars.SCHEDULED_SYNC_INTERVAL_HOURS || '6' }}
TF_VAR_db_name: ${{ vars.DB_NAME }}
TF_VAR_db_username: ${{ vars.DB_USERNAME }}
TF_VAR_db_password: ${{ secrets.DB_PASSWORD }}
diff --git a/README.md b/README.md
index 8411987..7f87cf9 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@ flowchart LR
API --> Customer["Customer AWS Accounts
AssumeRole"]
```
-Underflow separates the customer-facing frontend, the API, and the background worker so cost syncs, alert evaluation, and notification delivery can run independently from the UI. The backend assumes customer roles only when needed, while synced reporting data stays in PostgreSQL for fast dashboard queries.
+Underflow separates the customer-facing frontend, the API, the scheduled sync runtime, and the alert worker so cost collection, alert evaluation, and notification delivery can run independently from the UI. The backend assumes customer roles only when needed, while synced reporting data stays in PostgreSQL for fast dashboard queries.
## Why This Project Is Credible
@@ -99,7 +99,8 @@ Underflow is deployed as:
- a React frontend served from S3 through CloudFront
- an Express API running on ECS Fargate
-- a background worker service for sync and alert execution
+- a scheduled Lambda for verified-account cost sync
+- a background worker service for alert execution
- a PostgreSQL database on Amazon RDS
- AWS-managed DNS, certificates, and email infrastructure
diff --git a/apps/api/.dockerignore b/apps/api/.dockerignore
index b8042f4..76abf91 100644
--- a/apps/api/.dockerignore
+++ b/apps/api/.dockerignore
@@ -1,5 +1,6 @@
node_modules
dist
+build
.env
.env.*
coverage
diff --git a/apps/api/package-lock.json b/apps/api/package-lock.json
index e4a0279..8f5e8bc 100644
--- a/apps/api/package-lock.json
+++ b/apps/api/package-lock.json
@@ -165,48 +165,48 @@
}
},
"node_modules/@aws-sdk/client-cost-explorer": {
- "version": "3.1027.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/client-cost-explorer/-/client-cost-explorer-3.1027.0.tgz",
- "integrity": "sha512-oPGf+IilUKu2M/CNdK2vDGkUihzGqGGRaUl6cg0+TRro+TKtKktK9AySG2OrPUzo/UkCfVp5kwOLgxlnlJy+2A==",
+ "version": "3.1045.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-cost-explorer/-/client-cost-explorer-3.1045.0.tgz",
+ "integrity": "sha512-jNSrIw0J6/zzLRAie4eZHQxB7vmtsRNmdqxD6QDsThE3EHD7aKp+JAbhyidz2EIpKi54wVGDT9ZixloGo5ezFQ==",
"license": "Apache-2.0",
"dependencies": {
"@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0",
- "@aws-sdk/core": "^3.973.27",
- "@aws-sdk/credential-provider-node": "^3.972.30",
- "@aws-sdk/middleware-host-header": "^3.972.9",
- "@aws-sdk/middleware-logger": "^3.972.9",
- "@aws-sdk/middleware-recursion-detection": "^3.972.10",
- "@aws-sdk/middleware-user-agent": "^3.972.29",
- "@aws-sdk/region-config-resolver": "^3.972.11",
- "@aws-sdk/types": "^3.973.7",
- "@aws-sdk/util-endpoints": "^3.996.6",
- "@aws-sdk/util-user-agent-browser": "^3.972.9",
- "@aws-sdk/util-user-agent-node": "^3.973.15",
- "@smithy/config-resolver": "^4.4.14",
- "@smithy/core": "^3.23.14",
- "@smithy/fetch-http-handler": "^5.3.16",
- "@smithy/hash-node": "^4.2.13",
- "@smithy/invalid-dependency": "^4.2.13",
- "@smithy/middleware-content-length": "^4.2.13",
- "@smithy/middleware-endpoint": "^4.4.29",
- "@smithy/middleware-retry": "^4.5.0",
- "@smithy/middleware-serde": "^4.2.17",
- "@smithy/middleware-stack": "^4.2.13",
- "@smithy/node-config-provider": "^4.3.13",
- "@smithy/node-http-handler": "^4.5.2",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/smithy-client": "^4.12.9",
- "@smithy/types": "^4.14.0",
- "@smithy/url-parser": "^4.2.13",
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/credential-provider-node": "^3.972.39",
+ "@aws-sdk/middleware-host-header": "^3.972.10",
+ "@aws-sdk/middleware-logger": "^3.972.10",
+ "@aws-sdk/middleware-recursion-detection": "^3.972.11",
+ "@aws-sdk/middleware-user-agent": "^3.972.38",
+ "@aws-sdk/region-config-resolver": "^3.972.13",
+ "@aws-sdk/types": "^3.973.8",
+ "@aws-sdk/util-endpoints": "^3.996.8",
+ "@aws-sdk/util-user-agent-browser": "^3.972.10",
+ "@aws-sdk/util-user-agent-node": "^3.973.24",
+ "@smithy/config-resolver": "^4.4.17",
+ "@smithy/core": "^3.23.17",
+ "@smithy/fetch-http-handler": "^5.3.17",
+ "@smithy/hash-node": "^4.2.14",
+ "@smithy/invalid-dependency": "^4.2.14",
+ "@smithy/middleware-content-length": "^4.2.14",
+ "@smithy/middleware-endpoint": "^4.4.32",
+ "@smithy/middleware-retry": "^4.5.7",
+ "@smithy/middleware-serde": "^4.2.20",
+ "@smithy/middleware-stack": "^4.2.14",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/node-http-handler": "^4.6.1",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
+ "@smithy/url-parser": "^4.2.14",
"@smithy/util-base64": "^4.3.2",
"@smithy/util-body-length-browser": "^4.2.2",
"@smithy/util-body-length-node": "^4.2.3",
- "@smithy/util-defaults-mode-browser": "^4.3.45",
- "@smithy/util-defaults-mode-node": "^4.2.49",
- "@smithy/util-endpoints": "^3.3.4",
- "@smithy/util-middleware": "^4.2.13",
- "@smithy/util-retry": "^4.3.0",
+ "@smithy/util-defaults-mode-browser": "^4.3.49",
+ "@smithy/util-defaults-mode-node": "^4.2.54",
+ "@smithy/util-endpoints": "^3.4.2",
+ "@smithy/util-middleware": "^4.2.14",
+ "@smithy/util-retry": "^4.3.6",
"@smithy/util-utf8": "^4.2.2",
"tslib": "^2.6.2"
},
@@ -215,50 +215,50 @@
}
},
"node_modules/@aws-sdk/client-ses": {
- "version": "3.1029.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.1029.0.tgz",
- "integrity": "sha512-9VFXUR0IoddbVIFhkX9jTt7PePlvi/udfyXVl1ev4anshkJYI6Aa6X6geiepHxdoBY9sHKjVZHF2B0+tf21kDQ==",
+ "version": "3.1045.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.1045.0.tgz",
+ "integrity": "sha512-a5ju4XkRwPa1cZmH3tVDR7kwcRhfUv77VLP+piE/vIFS+PcIpIGrzNhTPLORzW6tXzt7zF6XTNB1YuzYrHHZAg==",
"license": "Apache-2.0",
"dependencies": {
"@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0",
- "@aws-sdk/core": "^3.973.27",
- "@aws-sdk/credential-provider-node": "^3.972.30",
- "@aws-sdk/middleware-host-header": "^3.972.9",
- "@aws-sdk/middleware-logger": "^3.972.9",
- "@aws-sdk/middleware-recursion-detection": "^3.972.10",
- "@aws-sdk/middleware-user-agent": "^3.972.29",
- "@aws-sdk/region-config-resolver": "^3.972.11",
- "@aws-sdk/types": "^3.973.7",
- "@aws-sdk/util-endpoints": "^3.996.6",
- "@aws-sdk/util-user-agent-browser": "^3.972.9",
- "@aws-sdk/util-user-agent-node": "^3.973.15",
- "@smithy/config-resolver": "^4.4.14",
- "@smithy/core": "^3.23.14",
- "@smithy/fetch-http-handler": "^5.3.16",
- "@smithy/hash-node": "^4.2.13",
- "@smithy/invalid-dependency": "^4.2.13",
- "@smithy/middleware-content-length": "^4.2.13",
- "@smithy/middleware-endpoint": "^4.4.29",
- "@smithy/middleware-retry": "^4.5.0",
- "@smithy/middleware-serde": "^4.2.17",
- "@smithy/middleware-stack": "^4.2.13",
- "@smithy/node-config-provider": "^4.3.13",
- "@smithy/node-http-handler": "^4.5.2",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/smithy-client": "^4.12.9",
- "@smithy/types": "^4.14.0",
- "@smithy/url-parser": "^4.2.13",
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/credential-provider-node": "^3.972.39",
+ "@aws-sdk/middleware-host-header": "^3.972.10",
+ "@aws-sdk/middleware-logger": "^3.972.10",
+ "@aws-sdk/middleware-recursion-detection": "^3.972.11",
+ "@aws-sdk/middleware-user-agent": "^3.972.38",
+ "@aws-sdk/region-config-resolver": "^3.972.13",
+ "@aws-sdk/types": "^3.973.8",
+ "@aws-sdk/util-endpoints": "^3.996.8",
+ "@aws-sdk/util-user-agent-browser": "^3.972.10",
+ "@aws-sdk/util-user-agent-node": "^3.973.24",
+ "@smithy/config-resolver": "^4.4.17",
+ "@smithy/core": "^3.23.17",
+ "@smithy/fetch-http-handler": "^5.3.17",
+ "@smithy/hash-node": "^4.2.14",
+ "@smithy/invalid-dependency": "^4.2.14",
+ "@smithy/middleware-content-length": "^4.2.14",
+ "@smithy/middleware-endpoint": "^4.4.32",
+ "@smithy/middleware-retry": "^4.5.7",
+ "@smithy/middleware-serde": "^4.2.20",
+ "@smithy/middleware-stack": "^4.2.14",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/node-http-handler": "^4.6.1",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
+ "@smithy/url-parser": "^4.2.14",
"@smithy/util-base64": "^4.3.2",
"@smithy/util-body-length-browser": "^4.2.2",
"@smithy/util-body-length-node": "^4.2.3",
- "@smithy/util-defaults-mode-browser": "^4.3.45",
- "@smithy/util-defaults-mode-node": "^4.2.49",
- "@smithy/util-endpoints": "^3.3.4",
- "@smithy/util-middleware": "^4.2.13",
- "@smithy/util-retry": "^4.3.0",
+ "@smithy/util-defaults-mode-browser": "^4.3.49",
+ "@smithy/util-defaults-mode-node": "^4.2.54",
+ "@smithy/util-endpoints": "^3.4.2",
+ "@smithy/util-middleware": "^4.2.14",
+ "@smithy/util-retry": "^4.3.6",
"@smithy/util-utf8": "^4.2.2",
- "@smithy/util-waiter": "^4.2.15",
+ "@smithy/util-waiter": "^4.3.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -266,48 +266,49 @@
}
},
"node_modules/@aws-sdk/client-sts": {
- "version": "3.1027.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.1027.0.tgz",
- "integrity": "sha512-8csy0Q+is2BJjMr/3g5RWu41F+t7nfi1TysU8fPtXcuVBLNL1R6AOLv69jY2Pt86Ep005WpY3CroJuLMruUTUw==",
+ "version": "3.1045.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.1045.0.tgz",
+ "integrity": "sha512-oDJJ7rM1osvfBdfZuhQ5DM6lHD9iuypL9m2LsEiA/lB8xuE5uPYsftNDcS0J9VRXFSvYTqC14K7Y5vMMKMg0vw==",
"license": "Apache-2.0",
"dependencies": {
"@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0",
- "@aws-sdk/core": "^3.973.27",
- "@aws-sdk/credential-provider-node": "^3.972.30",
- "@aws-sdk/middleware-host-header": "^3.972.9",
- "@aws-sdk/middleware-logger": "^3.972.9",
- "@aws-sdk/middleware-recursion-detection": "^3.972.10",
- "@aws-sdk/middleware-user-agent": "^3.972.29",
- "@aws-sdk/region-config-resolver": "^3.972.11",
- "@aws-sdk/types": "^3.973.7",
- "@aws-sdk/util-endpoints": "^3.996.6",
- "@aws-sdk/util-user-agent-browser": "^3.972.9",
- "@aws-sdk/util-user-agent-node": "^3.973.15",
- "@smithy/config-resolver": "^4.4.14",
- "@smithy/core": "^3.23.14",
- "@smithy/fetch-http-handler": "^5.3.16",
- "@smithy/hash-node": "^4.2.13",
- "@smithy/invalid-dependency": "^4.2.13",
- "@smithy/middleware-content-length": "^4.2.13",
- "@smithy/middleware-endpoint": "^4.4.29",
- "@smithy/middleware-retry": "^4.5.0",
- "@smithy/middleware-serde": "^4.2.17",
- "@smithy/middleware-stack": "^4.2.13",
- "@smithy/node-config-provider": "^4.3.13",
- "@smithy/node-http-handler": "^4.5.2",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/smithy-client": "^4.12.9",
- "@smithy/types": "^4.14.0",
- "@smithy/url-parser": "^4.2.13",
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/credential-provider-node": "^3.972.39",
+ "@aws-sdk/middleware-host-header": "^3.972.10",
+ "@aws-sdk/middleware-logger": "^3.972.10",
+ "@aws-sdk/middleware-recursion-detection": "^3.972.11",
+ "@aws-sdk/middleware-user-agent": "^3.972.38",
+ "@aws-sdk/region-config-resolver": "^3.972.13",
+ "@aws-sdk/signature-v4-multi-region": "^3.996.25",
+ "@aws-sdk/types": "^3.973.8",
+ "@aws-sdk/util-endpoints": "^3.996.8",
+ "@aws-sdk/util-user-agent-browser": "^3.972.10",
+ "@aws-sdk/util-user-agent-node": "^3.973.24",
+ "@smithy/config-resolver": "^4.4.17",
+ "@smithy/core": "^3.23.17",
+ "@smithy/fetch-http-handler": "^5.3.17",
+ "@smithy/hash-node": "^4.2.14",
+ "@smithy/invalid-dependency": "^4.2.14",
+ "@smithy/middleware-content-length": "^4.2.14",
+ "@smithy/middleware-endpoint": "^4.4.32",
+ "@smithy/middleware-retry": "^4.5.7",
+ "@smithy/middleware-serde": "^4.2.20",
+ "@smithy/middleware-stack": "^4.2.14",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/node-http-handler": "^4.6.1",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
+ "@smithy/url-parser": "^4.2.14",
"@smithy/util-base64": "^4.3.2",
"@smithy/util-body-length-browser": "^4.2.2",
"@smithy/util-body-length-node": "^4.2.3",
- "@smithy/util-defaults-mode-browser": "^4.3.45",
- "@smithy/util-defaults-mode-node": "^4.2.49",
- "@smithy/util-endpoints": "^3.3.4",
- "@smithy/util-middleware": "^4.2.13",
- "@smithy/util-retry": "^4.3.0",
+ "@smithy/util-defaults-mode-browser": "^4.3.49",
+ "@smithy/util-defaults-mode-node": "^4.2.54",
+ "@smithy/util-endpoints": "^3.4.2",
+ "@smithy/util-middleware": "^4.2.14",
+ "@smithy/util-retry": "^4.3.6",
"@smithy/util-utf8": "^4.2.2",
"tslib": "^2.6.2"
},
@@ -316,22 +317,23 @@
}
},
"node_modules/@aws-sdk/core": {
- "version": "3.973.27",
- "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.27.tgz",
- "integrity": "sha512-CUZ5m8hwMCH6OYI4Li/WgMfIEx10Q2PLI9Y3XOUTPGZJ53aZ0007jCv+X/ywsaERyKPdw5MRZWk877roQksQ4A==",
+ "version": "3.974.8",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.8.tgz",
+ "integrity": "sha512-njR2qoG6ZuB0kvAS2FyICsFZJ6gmCcf2X/7JcD14sUvGDm26wiZ5BrA6LOiUxKFEF+IVe7kdroxyE00YlkiYsw==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.7",
- "@aws-sdk/xml-builder": "^3.972.17",
- "@smithy/core": "^3.23.14",
- "@smithy/node-config-provider": "^4.3.13",
- "@smithy/property-provider": "^4.2.13",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/signature-v4": "^5.3.13",
- "@smithy/smithy-client": "^4.12.9",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/types": "^3.973.8",
+ "@aws-sdk/xml-builder": "^3.972.22",
+ "@smithy/core": "^3.23.17",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/signature-v4": "^5.3.14",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
"@smithy/util-base64": "^4.3.2",
- "@smithy/util-middleware": "^4.2.13",
+ "@smithy/util-middleware": "^4.2.14",
+ "@smithy/util-retry": "^4.3.6",
"@smithy/util-utf8": "^4.2.2",
"tslib": "^2.6.2"
},
@@ -340,15 +342,15 @@
}
},
"node_modules/@aws-sdk/credential-provider-env": {
- "version": "3.972.25",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.25.tgz",
- "integrity": "sha512-6QfI0wv4jpG5CrdO/AO0JfZ2ux+tKwJPrUwmvxXF50vI5KIypKVGNF6b4vlkYEnKumDTI1NX2zUBi8JoU5QU3A==",
+ "version": "3.972.34",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.34.tgz",
+ "integrity": "sha512-XT0jtf8Fw9JE6ppsQeoNnZRiG+jqRixMT1v1ZR17G60UvVdsQmTG8nbEyHuEPfMxDXEhfdARaM/XiEhca4lGHQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.27",
- "@aws-sdk/types": "^3.973.7",
- "@smithy/property-provider": "^4.2.13",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -356,20 +358,20 @@
}
},
"node_modules/@aws-sdk/credential-provider-http": {
- "version": "3.972.27",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.27.tgz",
- "integrity": "sha512-3V3Usj9Gs93h865DqN4M2NWJhC5kXU9BvZskfN3+69omuYlE3TZxOEcVQtBGLOloJB7BVfJKXVLqeNhOzHqSlQ==",
+ "version": "3.972.36",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.36.tgz",
+ "integrity": "sha512-DPoGWfy7J7RKxvbf5kOKIGQkD2ek3dbKgzKIGrnLuvZBz5myU+Im/H6pmc14QcnFbqHMqxvtWSgRDSJW3qXLQg==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.27",
- "@aws-sdk/types": "^3.973.7",
- "@smithy/fetch-http-handler": "^5.3.16",
- "@smithy/node-http-handler": "^4.5.2",
- "@smithy/property-provider": "^4.2.13",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/smithy-client": "^4.12.9",
- "@smithy/types": "^4.14.0",
- "@smithy/util-stream": "^4.5.22",
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/fetch-http-handler": "^5.3.17",
+ "@smithy/node-http-handler": "^4.6.1",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
+ "@smithy/util-stream": "^4.5.25",
"tslib": "^2.6.2"
},
"engines": {
@@ -377,24 +379,24 @@
}
},
"node_modules/@aws-sdk/credential-provider-ini": {
- "version": "3.972.29",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.29.tgz",
- "integrity": "sha512-SiBuAnXecCbT/OpAf3vqyI/AVE3mTaYr9ShXLybxZiPLBiPCCOIWSGAtYYGQWMRvobBTiqOewaB+wcgMMZI2Aw==",
+ "version": "3.972.38",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.38.tgz",
+ "integrity": "sha512-oDzUBu2MGJFgoar05sPMCwSrhw44ASyccrHzj66vO69OZqi7I6hZZxXfuPLC8OCzW7C+sU+bI73XHij41yekgQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.27",
- "@aws-sdk/credential-provider-env": "^3.972.25",
- "@aws-sdk/credential-provider-http": "^3.972.27",
- "@aws-sdk/credential-provider-login": "^3.972.29",
- "@aws-sdk/credential-provider-process": "^3.972.25",
- "@aws-sdk/credential-provider-sso": "^3.972.29",
- "@aws-sdk/credential-provider-web-identity": "^3.972.29",
- "@aws-sdk/nested-clients": "^3.996.19",
- "@aws-sdk/types": "^3.973.7",
- "@smithy/credential-provider-imds": "^4.2.13",
- "@smithy/property-provider": "^4.2.13",
- "@smithy/shared-ini-file-loader": "^4.4.8",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/credential-provider-env": "^3.972.34",
+ "@aws-sdk/credential-provider-http": "^3.972.36",
+ "@aws-sdk/credential-provider-login": "^3.972.38",
+ "@aws-sdk/credential-provider-process": "^3.972.34",
+ "@aws-sdk/credential-provider-sso": "^3.972.38",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.38",
+ "@aws-sdk/nested-clients": "^3.997.6",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/credential-provider-imds": "^4.2.14",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/shared-ini-file-loader": "^4.4.9",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -402,18 +404,18 @@
}
},
"node_modules/@aws-sdk/credential-provider-login": {
- "version": "3.972.29",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.29.tgz",
- "integrity": "sha512-OGOslTbOlxXexKMqhxCEbBQbUIfuhGxU5UXw3Fm56ypXHvrXH4aTt/xb5Y884LOoteP1QST1lVZzHfcTnWhiPQ==",
+ "version": "3.972.38",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.38.tgz",
+ "integrity": "sha512-g1NosS8qe4OF++G2UFCM5ovSkgipC7YYor5KCWatG0UoMSO5YFj9C8muePlyVmOBV/WTI16Jo3/s1NUo/o1Bww==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.27",
- "@aws-sdk/nested-clients": "^3.996.19",
- "@aws-sdk/types": "^3.973.7",
- "@smithy/property-provider": "^4.2.13",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/shared-ini-file-loader": "^4.4.8",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/nested-clients": "^3.997.6",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/shared-ini-file-loader": "^4.4.9",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -421,22 +423,22 @@
}
},
"node_modules/@aws-sdk/credential-provider-node": {
- "version": "3.972.30",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.30.tgz",
- "integrity": "sha512-FMnAnWxc8PG+ZrZ2OBKzY4luCUJhe9CG0B9YwYr4pzrYGLXBS2rl+UoUvjGbAwiptxRL6hyA3lFn03Bv1TLqTw==",
+ "version": "3.972.39",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.39.tgz",
+ "integrity": "sha512-HEswDQyxUtadoZ/bJsPPENHg7R0Lzym5LuMksJeHvqhCOpP+rtkDLKI4/ZChH4w3cf5kG8n6bZuI8PzajoiqMg==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/credential-provider-env": "^3.972.25",
- "@aws-sdk/credential-provider-http": "^3.972.27",
- "@aws-sdk/credential-provider-ini": "^3.972.29",
- "@aws-sdk/credential-provider-process": "^3.972.25",
- "@aws-sdk/credential-provider-sso": "^3.972.29",
- "@aws-sdk/credential-provider-web-identity": "^3.972.29",
- "@aws-sdk/types": "^3.973.7",
- "@smithy/credential-provider-imds": "^4.2.13",
- "@smithy/property-provider": "^4.2.13",
- "@smithy/shared-ini-file-loader": "^4.4.8",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/credential-provider-env": "^3.972.34",
+ "@aws-sdk/credential-provider-http": "^3.972.36",
+ "@aws-sdk/credential-provider-ini": "^3.972.38",
+ "@aws-sdk/credential-provider-process": "^3.972.34",
+ "@aws-sdk/credential-provider-sso": "^3.972.38",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.38",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/credential-provider-imds": "^4.2.14",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/shared-ini-file-loader": "^4.4.9",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -444,16 +446,16 @@
}
},
"node_modules/@aws-sdk/credential-provider-process": {
- "version": "3.972.25",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.25.tgz",
- "integrity": "sha512-HR7ynNRdNhNsdVCOCegy1HsfsRzozCOPtD3RzzT1JouuaHobWyRfJzCBue/3jP7gECHt+kQyZUvwg/cYLWurNQ==",
+ "version": "3.972.34",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.34.tgz",
+ "integrity": "sha512-T3IFs4EVmVi1dVN5RciFnklCANSzvrQd/VuHY9ThHSQmYkTogjcGkoJEr+oNUPQZnso52183088NqysMPji1/Q==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.27",
- "@aws-sdk/types": "^3.973.7",
- "@smithy/property-provider": "^4.2.13",
- "@smithy/shared-ini-file-loader": "^4.4.8",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/shared-ini-file-loader": "^4.4.9",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -461,18 +463,18 @@
}
},
"node_modules/@aws-sdk/credential-provider-sso": {
- "version": "3.972.29",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.29.tgz",
- "integrity": "sha512-HWv4SEq3jZDYPlwryZVef97+U8CxxRos5mK8sgGO1dQaFZpV5giZLzqGE5hkDmh2csYcBO2uf5XHjPTpZcJlig==",
+ "version": "3.972.38",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.38.tgz",
+ "integrity": "sha512-5ZxG+t0+3Q3QPh8KEjX6syskhgNf7I0MN7oGioTf6Lm1NTjfP7sIcYGNsthXC2qR8vcD3edNZwCr2ovfSSWuRA==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.27",
- "@aws-sdk/nested-clients": "^3.996.19",
- "@aws-sdk/token-providers": "3.1026.0",
- "@aws-sdk/types": "^3.973.7",
- "@smithy/property-provider": "^4.2.13",
- "@smithy/shared-ini-file-loader": "^4.4.8",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/nested-clients": "^3.997.6",
+ "@aws-sdk/token-providers": "3.1041.0",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/shared-ini-file-loader": "^4.4.9",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -480,17 +482,17 @@
}
},
"node_modules/@aws-sdk/credential-provider-web-identity": {
- "version": "3.972.29",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.29.tgz",
- "integrity": "sha512-PdMBza1WEKEUPFEmMGCfnU2RYCz9MskU2e8JxjyUOsMKku7j9YaDKvbDi2dzC0ihFoM6ods2SbhfAAro+Gwlew==",
+ "version": "3.972.38",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.38.tgz",
+ "integrity": "sha512-lYHFF30DGI20jZcYX8cm6Ns0V7f1dDN6g/MBDLTyD/5iw+bXs3yBr2iAiHDkx4RFU5JgsnZvCHYKiRVPRdmOgw==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.27",
- "@aws-sdk/nested-clients": "^3.996.19",
- "@aws-sdk/types": "^3.973.7",
- "@smithy/property-provider": "^4.2.13",
- "@smithy/shared-ini-file-loader": "^4.4.8",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/nested-clients": "^3.997.6",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/shared-ini-file-loader": "^4.4.9",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -498,14 +500,14 @@
}
},
"node_modules/@aws-sdk/middleware-host-header": {
- "version": "3.972.9",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.9.tgz",
- "integrity": "sha512-je5vRdNw4SkuTnmRbFZLdye4sQ0faLt8kwka5wnnSU30q1mHO4X+idGEJOOE+Tn1ME7Oryn05xxkDvIb3UaLaQ==",
+ "version": "3.972.10",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.10.tgz",
+ "integrity": "sha512-IJSsIMeVQ8MMCPbuh1AbltkFhLBLXn7aejzfX5YKT/VLDHn++Dcz8886tXckE+wQssyPUhaXrJhdakO2VilRhg==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.7",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -513,13 +515,13 @@
}
},
"node_modules/@aws-sdk/middleware-logger": {
- "version": "3.972.9",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.9.tgz",
- "integrity": "sha512-HsVgDrruhqI28RkaXALm8grJ7Agc1wF6Et0xh6pom8NdO2VdO/SD9U/tPwUjewwK/pVoka+EShBxyCvgsPCtog==",
+ "version": "3.972.10",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.10.tgz",
+ "integrity": "sha512-OOuGvvz1Dm20SjZo5oEBePFqxt5nf8AwkNDSyUHvD9/bfNASmstcYxFAHUowy4n6Io7mWUZ04JURZwSBvyQanQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.7",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -527,15 +529,40 @@
}
},
"node_modules/@aws-sdk/middleware-recursion-detection": {
- "version": "3.972.10",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.10.tgz",
- "integrity": "sha512-RVQQbq5orQ/GHUnXvqEOj2HHPBJm+mM+ySwZKS5UaLBwra5ugRtiH09PLUoOZRl7a1YzaOzXSuGbn9iD5j60WQ==",
+ "version": "3.972.11",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.11.tgz",
+ "integrity": "sha512-+zz6f79Kj9V5qFK2P+D8Ehjnw4AhphAlCAsPjUqEcInA9umtSSKMrHbSagEeOIsDNuvVrH98bjRHcyQukTrhaQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.7",
+ "@aws-sdk/types": "^3.973.8",
"@aws/lambda-invoke-store": "^0.2.2",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/types": "^4.14.0",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-sdk-s3": {
+ "version": "3.972.37",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.37.tgz",
+ "integrity": "sha512-Km7M+i8DrLArVzrid1gfxeGhYHBd3uxvE77g0s5a52zPSVosxzQBnJ0gwWb6NIp/DOk8gsBMhi7V+cpJG0ndTA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/types": "^3.973.8",
+ "@aws-sdk/util-arn-parser": "^3.972.3",
+ "@smithy/core": "^3.23.17",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/signature-v4": "^5.3.14",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
+ "@smithy/util-config-provider": "^4.2.2",
+ "@smithy/util-middleware": "^4.2.14",
+ "@smithy/util-stream": "^4.5.25",
+ "@smithy/util-utf8": "^4.2.2",
"tslib": "^2.6.2"
},
"engines": {
@@ -543,18 +570,18 @@
}
},
"node_modules/@aws-sdk/middleware-user-agent": {
- "version": "3.972.29",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.29.tgz",
- "integrity": "sha512-f/sIRzuTfEjg6NsbMYvye2VsmnQoNgntntleQyx5uGacUYzszbfIlO3GcI6G6daWUmTm0IDZc11qMHWwF0o0mQ==",
+ "version": "3.972.38",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.38.tgz",
+ "integrity": "sha512-iz+B29TXcAZsJpwB+AwG/TTGA5l/VnmMZ2UxtiySOZjI6gCdmviXPwdgzcmuazMy16rXoPY4mYCGe7zdNKfx5A==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.27",
- "@aws-sdk/types": "^3.973.7",
- "@aws-sdk/util-endpoints": "^3.996.6",
- "@smithy/core": "^3.23.14",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/types": "^4.14.0",
- "@smithy/util-retry": "^4.3.0",
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/types": "^3.973.8",
+ "@aws-sdk/util-endpoints": "^3.996.8",
+ "@smithy/core": "^3.23.17",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
+ "@smithy/util-retry": "^4.3.6",
"tslib": "^2.6.2"
},
"engines": {
@@ -562,47 +589,48 @@
}
},
"node_modules/@aws-sdk/nested-clients": {
- "version": "3.996.19",
- "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.19.tgz",
- "integrity": "sha512-uFkmCDXvmQYLanlYdOFS0+MQWkrj9wPMt/ZCc/0J0fjPim6F5jBVBmEomvGY/j77ILW6GTPwN22Jc174Mhkw6Q==",
+ "version": "3.997.6",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.6.tgz",
+ "integrity": "sha512-WBDnqatJl+kGObpfmfSxqnXeYTu3Me8wx8WCtvoxX3pfWrrTv8I4WTMSSs7PZqcRcVh8WeUKMgGFjMG+52SR1w==",
"license": "Apache-2.0",
"dependencies": {
"@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0",
- "@aws-sdk/core": "^3.973.27",
- "@aws-sdk/middleware-host-header": "^3.972.9",
- "@aws-sdk/middleware-logger": "^3.972.9",
- "@aws-sdk/middleware-recursion-detection": "^3.972.10",
- "@aws-sdk/middleware-user-agent": "^3.972.29",
- "@aws-sdk/region-config-resolver": "^3.972.11",
- "@aws-sdk/types": "^3.973.7",
- "@aws-sdk/util-endpoints": "^3.996.6",
- "@aws-sdk/util-user-agent-browser": "^3.972.9",
- "@aws-sdk/util-user-agent-node": "^3.973.15",
- "@smithy/config-resolver": "^4.4.14",
- "@smithy/core": "^3.23.14",
- "@smithy/fetch-http-handler": "^5.3.16",
- "@smithy/hash-node": "^4.2.13",
- "@smithy/invalid-dependency": "^4.2.13",
- "@smithy/middleware-content-length": "^4.2.13",
- "@smithy/middleware-endpoint": "^4.4.29",
- "@smithy/middleware-retry": "^4.5.0",
- "@smithy/middleware-serde": "^4.2.17",
- "@smithy/middleware-stack": "^4.2.13",
- "@smithy/node-config-provider": "^4.3.13",
- "@smithy/node-http-handler": "^4.5.2",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/smithy-client": "^4.12.9",
- "@smithy/types": "^4.14.0",
- "@smithy/url-parser": "^4.2.13",
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/middleware-host-header": "^3.972.10",
+ "@aws-sdk/middleware-logger": "^3.972.10",
+ "@aws-sdk/middleware-recursion-detection": "^3.972.11",
+ "@aws-sdk/middleware-user-agent": "^3.972.38",
+ "@aws-sdk/region-config-resolver": "^3.972.13",
+ "@aws-sdk/signature-v4-multi-region": "^3.996.25",
+ "@aws-sdk/types": "^3.973.8",
+ "@aws-sdk/util-endpoints": "^3.996.8",
+ "@aws-sdk/util-user-agent-browser": "^3.972.10",
+ "@aws-sdk/util-user-agent-node": "^3.973.24",
+ "@smithy/config-resolver": "^4.4.17",
+ "@smithy/core": "^3.23.17",
+ "@smithy/fetch-http-handler": "^5.3.17",
+ "@smithy/hash-node": "^4.2.14",
+ "@smithy/invalid-dependency": "^4.2.14",
+ "@smithy/middleware-content-length": "^4.2.14",
+ "@smithy/middleware-endpoint": "^4.4.32",
+ "@smithy/middleware-retry": "^4.5.7",
+ "@smithy/middleware-serde": "^4.2.20",
+ "@smithy/middleware-stack": "^4.2.14",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/node-http-handler": "^4.6.1",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
+ "@smithy/url-parser": "^4.2.14",
"@smithy/util-base64": "^4.3.2",
"@smithy/util-body-length-browser": "^4.2.2",
"@smithy/util-body-length-node": "^4.2.3",
- "@smithy/util-defaults-mode-browser": "^4.3.45",
- "@smithy/util-defaults-mode-node": "^4.2.49",
- "@smithy/util-endpoints": "^3.3.4",
- "@smithy/util-middleware": "^4.2.13",
- "@smithy/util-retry": "^4.3.0",
+ "@smithy/util-defaults-mode-browser": "^4.3.49",
+ "@smithy/util-defaults-mode-node": "^4.2.54",
+ "@smithy/util-endpoints": "^3.4.2",
+ "@smithy/util-middleware": "^4.2.14",
+ "@smithy/util-retry": "^4.3.6",
"@smithy/util-utf8": "^4.2.2",
"tslib": "^2.6.2"
},
@@ -611,15 +639,32 @@
}
},
"node_modules/@aws-sdk/region-config-resolver": {
- "version": "3.972.11",
- "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.11.tgz",
- "integrity": "sha512-6Q8B1dcx6BBqUTY1Mc/eROKA0FImEEY5VPSd6AGPEUf0ErjExz4snVqa9kNJSoVDV1rKaNf3qrWojgcKW+SdDg==",
+ "version": "3.972.13",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.13.tgz",
+ "integrity": "sha512-CvJ2ZIjK/jVD/lbOpowBVElJyC1YxLTIJ13yM0AEo0t2v7swOzGjSA6lJGH+DwZXQhcjUjoYwc8bVYCX5MDr1A==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.7",
- "@smithy/config-resolver": "^4.4.14",
- "@smithy/node-config-provider": "^4.3.13",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/config-resolver": "^4.4.17",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/types": "^4.14.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/signature-v4-multi-region": {
+ "version": "3.996.25",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.25.tgz",
+ "integrity": "sha512-+CMIt3e1VzlklAECmG+DtP1sV8iKq25FuA0OKpnJ4KA0kxUtd7CgClY7/RU6VzJBQwbN4EJ9Ue6plvqx1qGadw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/middleware-sdk-s3": "^3.972.37",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/signature-v4": "^5.3.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -627,17 +672,17 @@
}
},
"node_modules/@aws-sdk/token-providers": {
- "version": "3.1026.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1026.0.tgz",
- "integrity": "sha512-Ieq/HiRrbEtrYP387Nes0XlR7H1pJiJOZKv+QyQzMYpvTiDs0VKy2ZB3E2Zf+aFovWmeE7lRE4lXyF7dYM6GgA==",
+ "version": "3.1041.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1041.0.tgz",
+ "integrity": "sha512-Th7kPI6YPtvJUcdznooXJMy+9rQWjmEF81LxaJssngBzuysK4a/x+l8kjm1zb7nYsUPbndnBdUnwng/3PLvtGw==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.27",
- "@aws-sdk/nested-clients": "^3.996.19",
- "@aws-sdk/types": "^3.973.7",
- "@smithy/property-provider": "^4.2.13",
- "@smithy/shared-ini-file-loader": "^4.4.8",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/core": "^3.974.8",
+ "@aws-sdk/nested-clients": "^3.997.6",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/shared-ini-file-loader": "^4.4.9",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -645,12 +690,24 @@
}
},
"node_modules/@aws-sdk/types": {
- "version": "3.973.7",
- "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.7.tgz",
- "integrity": "sha512-reXRwoJ6CfChoqAsBszUYajAF8Z2LRE+CRcKocvFSMpIiLOtYU3aJ9trmn6VVPAzbbY5LXF+FfmUslbXk1SYFg==",
+ "version": "3.973.8",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.8.tgz",
+ "integrity": "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.14.1",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-arn-parser": {
+ "version": "3.972.3",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.3.tgz",
+ "integrity": "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.14.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -658,15 +715,15 @@
}
},
"node_modules/@aws-sdk/util-endpoints": {
- "version": "3.996.6",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.6.tgz",
- "integrity": "sha512-2nUQ+2ih7CShuKHpGSIYvvAIOHy52dOZguYG36zptBukhw6iFwcvGfG0tes0oZFWQqEWvgZe9HLWaNlvXGdOrg==",
+ "version": "3.996.8",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.8.tgz",
+ "integrity": "sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.7",
- "@smithy/types": "^4.14.0",
- "@smithy/url-parser": "^4.2.13",
- "@smithy/util-endpoints": "^3.3.4",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/types": "^4.14.1",
+ "@smithy/url-parser": "^4.2.14",
+ "@smithy/util-endpoints": "^3.4.2",
"tslib": "^2.6.2"
},
"engines": {
@@ -686,27 +743,27 @@
}
},
"node_modules/@aws-sdk/util-user-agent-browser": {
- "version": "3.972.9",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.9.tgz",
- "integrity": "sha512-sn/LMzTbGjYqCCF24390WxPd6hkpoSptiUn5DzVp4cD71yqw+yGEGm1YCxyEoPXyc8qciM8UzLJcZBFslxo5Uw==",
+ "version": "3.972.10",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.10.tgz",
+ "integrity": "sha512-FAzqXvfEssGdSIz8ejatan0bOdx1qefBWKF/gWmVBXIP1HkS7v/wjjaqrAGGKvyihrXTXW00/2/1nTJtxpXz7g==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.7",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/types": "^4.14.1",
"bowser": "^2.11.0",
"tslib": "^2.6.2"
}
},
"node_modules/@aws-sdk/util-user-agent-node": {
- "version": "3.973.15",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.15.tgz",
- "integrity": "sha512-fYn3s9PtKdgQkczGZCFMgkNEe8aq1JCVbnRqjqN9RSVW43xn2RV9xdcZ3z01a48Jpkuh/xCmBKJxdLOo4Ozg7w==",
+ "version": "3.973.24",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.24.tgz",
+ "integrity": "sha512-ZWwlkjcIp7cEL8ZfTpTAPNkwx25p7xol0xlKoWVVf22+nsjwmLcHYtTPjIV1cSpmB/b6DaK4cb1fSkvCXHgRdw==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/middleware-user-agent": "^3.972.29",
- "@aws-sdk/types": "^3.973.7",
- "@smithy/node-config-provider": "^4.3.13",
- "@smithy/types": "^4.14.0",
+ "@aws-sdk/middleware-user-agent": "^3.972.38",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/types": "^4.14.1",
"@smithy/util-config-provider": "^4.2.2",
"tslib": "^2.6.2"
},
@@ -723,13 +780,14 @@
}
},
"node_modules/@aws-sdk/xml-builder": {
- "version": "3.972.17",
- "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.17.tgz",
- "integrity": "sha512-Ra7hjqAZf1OXRRMueB13qex7mFJRDK/pgCvdSFemXBT8KCGnQDPoKzHY1SjN+TjJVmnpSF14W5tJ1vDamFu+Gg==",
+ "version": "3.972.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.22.tgz",
+ "integrity": "sha512-PMYKKtJd70IsSG0yHrdAbxBr+ZWBKLvzFZfD3/urxgf6hXVMzuU5M+3MJ5G67RpOmLBu1fAUN65SbWuKUCOlAA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.14.0",
- "fast-xml-parser": "5.5.8",
+ "@nodable/entities": "2.1.0",
+ "@smithy/types": "^4.14.1",
+ "fast-xml-parser": "5.7.2",
"tslib": "^2.6.2"
},
"engines": {
@@ -1241,6 +1299,18 @@
"url": "https://paulmillr.com/funding/"
}
},
+ "node_modules/@nodable/entities": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz",
+ "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodable"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/@paralleldrive/cuid2": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz",
@@ -1261,16 +1331,16 @@
}
},
"node_modules/@smithy/config-resolver": {
- "version": "4.4.14",
- "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.14.tgz",
- "integrity": "sha512-N55f8mPEccpzKetUagdvmAy8oohf0J5cuj9jLI1TaSceRlq0pJsIZepY3kmAXAhyxqXPV6hDerDQhqQPKWgAoQ==",
+ "version": "4.4.17",
+ "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.17.tgz",
+ "integrity": "sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/node-config-provider": "^4.3.13",
- "@smithy/types": "^4.14.0",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/types": "^4.14.1",
"@smithy/util-config-provider": "^4.2.2",
- "@smithy/util-endpoints": "^3.3.4",
- "@smithy/util-middleware": "^4.2.13",
+ "@smithy/util-endpoints": "^3.4.2",
+ "@smithy/util-middleware": "^4.2.14",
"tslib": "^2.6.2"
},
"engines": {
@@ -1278,18 +1348,18 @@
}
},
"node_modules/@smithy/core": {
- "version": "3.23.14",
- "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.14.tgz",
- "integrity": "sha512-vJ0IhpZxZAkFYOegMKSrxw7ujhhT2pass/1UEcZ4kfl5srTAqtPU5I7MdYQoreVas3204ykCiNhY1o7Xlz6Yyg==",
+ "version": "3.23.17",
+ "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.17.tgz",
+ "integrity": "sha512-x7BlLbUFL8NWCGjMF9C+1N5cVCxcPa7g6Tv9B4A2luWx3be3oU8hQ96wIwxe/s7OhIzvoJH73HAUSg5JXVlEtQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/types": "^4.14.0",
- "@smithy/url-parser": "^4.2.13",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
+ "@smithy/url-parser": "^4.2.14",
"@smithy/util-base64": "^4.3.2",
"@smithy/util-body-length-browser": "^4.2.2",
- "@smithy/util-middleware": "^4.2.13",
- "@smithy/util-stream": "^4.5.22",
+ "@smithy/util-middleware": "^4.2.14",
+ "@smithy/util-stream": "^4.5.25",
"@smithy/util-utf8": "^4.2.2",
"@smithy/uuid": "^1.1.2",
"tslib": "^2.6.2"
@@ -1299,15 +1369,15 @@
}
},
"node_modules/@smithy/credential-provider-imds": {
- "version": "4.2.13",
- "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.13.tgz",
- "integrity": "sha512-wboCPijzf6RJKLOvnjDAiBxGSmSnGXj35o5ZAWKDaHa/cvQ5U3ZJ13D4tMCE8JG4dxVAZFy/P0x/V9CwwdfULQ==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.14.tgz",
+ "integrity": "sha512-Au28zBN48ZAoXdooGUHemuVBrkE+Ie6RPmGNIAJsFqj33Vhb6xAgRifUydZ2aY+M+KaMAETAlKk5NC5h1G7wpg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/node-config-provider": "^4.3.13",
- "@smithy/property-provider": "^4.2.13",
- "@smithy/types": "^4.14.0",
- "@smithy/url-parser": "^4.2.13",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/types": "^4.14.1",
+ "@smithy/url-parser": "^4.2.14",
"tslib": "^2.6.2"
},
"engines": {
@@ -1315,14 +1385,14 @@
}
},
"node_modules/@smithy/fetch-http-handler": {
- "version": "5.3.16",
- "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.16.tgz",
- "integrity": "sha512-nYDRUIvNd4mFmuXraRWt6w5UsZTNqtj4hXJA/iiOD4tuseIdLP9Lq38teH/SZTcIFCa2f+27o7hYpIsWktJKEQ==",
+ "version": "5.3.17",
+ "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.17.tgz",
+ "integrity": "sha512-bXOvQzaSm6MnmLaWA1elgfQcAtN4UP3vXqV97bHuoOrHQOJiLT3ds6o9eo5bqd0TJfRFpzdGnDQdW3FACiAVdw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/querystring-builder": "^4.2.13",
- "@smithy/types": "^4.14.0",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/querystring-builder": "^4.2.14",
+ "@smithy/types": "^4.14.1",
"@smithy/util-base64": "^4.3.2",
"tslib": "^2.6.2"
},
@@ -1331,12 +1401,12 @@
}
},
"node_modules/@smithy/hash-node": {
- "version": "4.2.13",
- "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.13.tgz",
- "integrity": "sha512-4/oy9h0jjmY80a2gOIo75iLl8TOPhmtx4E2Hz+PfMjvx/vLtGY4TMU/35WRyH2JHPfT5CVB38u4JRow7gnmzJA==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.14.tgz",
+ "integrity": "sha512-8ZBDY2DD4wr+GGjTpPtiglEsqr0lUP+KHqgZcWczFf6qeZ/YRjMIOoQWVQlmwu7EtxKTd8YXD8lblmYcpBIA1g==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.14.0",
+ "@smithy/types": "^4.14.1",
"@smithy/util-buffer-from": "^4.2.2",
"@smithy/util-utf8": "^4.2.2",
"tslib": "^2.6.2"
@@ -1346,12 +1416,12 @@
}
},
"node_modules/@smithy/invalid-dependency": {
- "version": "4.2.13",
- "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.13.tgz",
- "integrity": "sha512-jvC0RB/8BLj2SMIkY0Npl425IdnxZJxInpZJbu563zIRnVjpDMXevU3VMCRSabaLB0kf/eFIOusdGstrLJ8IDg==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.14.tgz",
+ "integrity": "sha512-c21qJiTSb25xvvOp+H2TNZzPCngrvl5vIPqPB8zQ/DmJF4QWXO19x1dWfMJZ6wZuuWUPPm0gV8C0cU3+ifcWuw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.14.0",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1371,13 +1441,13 @@
}
},
"node_modules/@smithy/middleware-content-length": {
- "version": "4.2.13",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.13.tgz",
- "integrity": "sha512-IPMLm/LE4AZwu6qiE8Rr8vJsWhs9AtOdySRXrOM7xnvclp77Tyh7hMs/FRrMf26kgIe67vFJXXOSmVxS7oKeig==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.14.tgz",
+ "integrity": "sha512-xhHq7fX4/3lv5NHxLUk3OeEvl0xZ+Ek3qIbWaCL4f9JwgDZEclPBElljaZCAItdGPQl/kSM4LPMOpy1MYgprpw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/types": "^4.14.0",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1385,18 +1455,18 @@
}
},
"node_modules/@smithy/middleware-endpoint": {
- "version": "4.4.29",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.29.tgz",
- "integrity": "sha512-R9Q/58U+qBiSARGWbAbFLczECg/RmysRksX6Q8BaQEpt75I7LI6WGDZnjuC9GXSGKljEbA7N118LhGaMbfrTXw==",
+ "version": "4.4.32",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.32.tgz",
+ "integrity": "sha512-ZZkgyjnJppiZbIm6Qbx92pbXYi1uzenIvGhBSCDlc7NwuAkiqSgS75j1czAD25ZLs2FjMjYy1q7gyRVWG6JA0Q==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/core": "^3.23.14",
- "@smithy/middleware-serde": "^4.2.17",
- "@smithy/node-config-provider": "^4.3.13",
- "@smithy/shared-ini-file-loader": "^4.4.8",
- "@smithy/types": "^4.14.0",
- "@smithy/url-parser": "^4.2.13",
- "@smithy/util-middleware": "^4.2.13",
+ "@smithy/core": "^3.23.17",
+ "@smithy/middleware-serde": "^4.2.20",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/shared-ini-file-loader": "^4.4.9",
+ "@smithy/types": "^4.14.1",
+ "@smithy/url-parser": "^4.2.14",
+ "@smithy/util-middleware": "^4.2.14",
"tslib": "^2.6.2"
},
"engines": {
@@ -1404,19 +1474,19 @@
}
},
"node_modules/@smithy/middleware-retry": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.5.0.tgz",
- "integrity": "sha512-/NzISn4grj/BRFVua/xnQwF+7fakYZgimpw2dfmlPgcqecBMKxpB9g5mLYRrmBD5OrPoODokw4Vi1hrSR4zRyw==",
+ "version": "4.5.7",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.5.7.tgz",
+ "integrity": "sha512-bRt6ZImqVSeTk39Nm81K20ObIiAZ3WefY7G6+iz/0tZjs4dgRRjvRX2sgsH+zi6iDCRR/aQvQofLKxxz4rPBZg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/core": "^3.23.14",
- "@smithy/node-config-provider": "^4.3.13",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/service-error-classification": "^4.2.13",
- "@smithy/smithy-client": "^4.12.9",
- "@smithy/types": "^4.14.0",
- "@smithy/util-middleware": "^4.2.13",
- "@smithy/util-retry": "^4.3.0",
+ "@smithy/core": "^3.23.17",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/service-error-classification": "^4.3.1",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
+ "@smithy/util-middleware": "^4.2.14",
+ "@smithy/util-retry": "^4.3.6",
"@smithy/uuid": "^1.1.2",
"tslib": "^2.6.2"
},
@@ -1425,14 +1495,14 @@
}
},
"node_modules/@smithy/middleware-serde": {
- "version": "4.2.17",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.17.tgz",
- "integrity": "sha512-0T2mcaM6v9W1xku86Dk0bEW7aEseG6KenFkPK98XNw0ZhOqOiD1MrMsdnQw9QsL3/Oa85T53iSMlm0SZdSuIEQ==",
+ "version": "4.2.20",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.20.tgz",
+ "integrity": "sha512-Lx9JMO9vArPtiChE3wbEZ5akMIDQpWQtlu90lhACQmNOXcGXRbaDywMHDzuDZ2OkZzP+9wQfZi3YJT9F67zTQQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/core": "^3.23.14",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/types": "^4.14.0",
+ "@smithy/core": "^3.23.17",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1440,12 +1510,12 @@
}
},
"node_modules/@smithy/middleware-stack": {
- "version": "4.2.13",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.13.tgz",
- "integrity": "sha512-g72jN/sGDLyTanrCLH9fhg3oysO3f7tQa6eWWsMyn2BiYNCgjF24n4/I9wff/5XidFvjj9ilipAoQrurTUrLvw==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.14.tgz",
+ "integrity": "sha512-2dvkUKLuFdKsCRmOE4Mn63co0Djtsm+JMh0bYZQupN1pJwMeE8FmQmRLLzzEMN0dnNi7CDCYYH8F0EVwWiPBeA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.14.0",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1453,14 +1523,14 @@
}
},
"node_modules/@smithy/node-config-provider": {
- "version": "4.3.13",
- "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.13.tgz",
- "integrity": "sha512-iGxQ04DsKXLckbgnX4ipElrOTk+IHgTyu0q0WssZfYhDm9CQWHmu6cOeI5wmWRxpXbBDhIIfXMWz5tPEtcVqbw==",
+ "version": "4.3.14",
+ "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.14.tgz",
+ "integrity": "sha512-S+gFjyo/weSVL0P1b9Ts8C/CwIfNCgUPikk3sl6QVsfE/uUuO+QsF+NsE/JkpvWqqyz1wg7HFdiaZuj5CoBMRg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/property-provider": "^4.2.13",
- "@smithy/shared-ini-file-loader": "^4.4.8",
- "@smithy/types": "^4.14.0",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/shared-ini-file-loader": "^4.4.9",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1468,14 +1538,14 @@
}
},
"node_modules/@smithy/node-http-handler": {
- "version": "4.5.2",
- "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.5.2.tgz",
- "integrity": "sha512-/oD7u8M0oj2ZTFw7GkuuHWpIxtWdLlnyNkbrWcyVYhd5RJNDuczdkb0wfnQICyNFrVPlr8YHOhamjNy3zidhmA==",
+ "version": "4.6.1",
+ "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.6.1.tgz",
+ "integrity": "sha512-iB+orM4x3xrr57X3YaXazfKnntl0LHlZB1kcXSGzMV1Tt0+YwEjGlbjk/44qEGtBzXAz6yFDzkYTKSV6Pj2HUg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/querystring-builder": "^4.2.13",
- "@smithy/types": "^4.14.0",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/querystring-builder": "^4.2.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1483,12 +1553,12 @@
}
},
"node_modules/@smithy/property-provider": {
- "version": "4.2.13",
- "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.13.tgz",
- "integrity": "sha512-bGzUCthxRmezuxkbu9wD33wWg9KX3hJpCXpQ93vVkPrHn9ZW6KNNdY5xAUWNuRCwQ+VyboFuWirG1lZhhkcyRQ==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.14.tgz",
+ "integrity": "sha512-WuM31CgfsnQ/10i7NYr0PyxqknD72Y5uMfUMVSniPjbEPceiTErb4eIqJQ+pdxNEAUEWrewrGjIRjVbVHsxZiQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.14.0",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1496,12 +1566,12 @@
}
},
"node_modules/@smithy/protocol-http": {
- "version": "5.3.13",
- "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.13.tgz",
- "integrity": "sha512-+HsmuJUF4u8POo6s8/a2Yb/AQ5t/YgLovCuHF9oxbocqv+SZ6gd8lC2duBFiCA/vFHoHQhoq7QjqJqZC6xOxxg==",
+ "version": "5.3.14",
+ "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.14.tgz",
+ "integrity": "sha512-dN5F8kHx8RNU0r+pCwNmFZyz6ChjMkzShy/zup6MtkRmmix4vZzJdW+di7x//b1LiynIev88FM18ie+wwPcQtQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.14.0",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1509,12 +1579,12 @@
}
},
"node_modules/@smithy/querystring-builder": {
- "version": "4.2.13",
- "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.13.tgz",
- "integrity": "sha512-tG4aOYFCZdPMjbgfhnIQ322H//ojujldp1SrHPHpBSb3NqgUp3dwiUGRJzie87hS1DYwWGqDuPaowoDF+rYCbQ==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.14.tgz",
+ "integrity": "sha512-XYA5Z0IqTeF+5XDdh4BBmSA0HvbgVZIyv4cmOoUheDNR57K1HgBp9ukUMx3Cr3XpDHHpLBnexPE3LAtDsZkj2A==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.14.0",
+ "@smithy/types": "^4.14.1",
"@smithy/util-uri-escape": "^4.2.2",
"tslib": "^2.6.2"
},
@@ -1523,12 +1593,12 @@
}
},
"node_modules/@smithy/querystring-parser": {
- "version": "4.2.13",
- "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.13.tgz",
- "integrity": "sha512-hqW3Q4P+CDzUyQ87GrboGMeD7XYNMOF+CuTwu936UQRB/zeYn3jys8C3w+wMkDfY7CyyyVwZQ5cNFoG0x1pYmA==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.14.tgz",
+ "integrity": "sha512-hr+YyqBD23GVvRxGGrcc/oOeNlK3PzT5Fu4dzrDXxzS1LpFiuL2PQQqKPs87M79aW7ziMs+nvB3qdw77SqE7Lw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.14.0",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1536,24 +1606,24 @@
}
},
"node_modules/@smithy/service-error-classification": {
- "version": "4.2.13",
- "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.13.tgz",
- "integrity": "sha512-a0s8XZMfOC/qpqq7RCPvJlk93rWFrElH6O++8WJKz0FqnA4Y7fkNi/0mnGgSH1C4x6MFsuBA8VKu4zxFrMe5Vw==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.3.1.tgz",
+ "integrity": "sha512-aUQuDGh760ts/8MU+APjIZhlLPKhIIfqyzZaJikLEIMrdxFvxuLYD0WxWzaYWpmLbQlXDe9p7EWM3HsBe0K6Gw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.14.0"
+ "@smithy/types": "^4.14.1"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@smithy/shared-ini-file-loader": {
- "version": "4.4.8",
- "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.8.tgz",
- "integrity": "sha512-VZCZx2bZasxdqxVgEAhREvDSlkatTPnkdWy1+Kiy8w7kYPBosW0V5IeDwzDUMvWBt56zpK658rx1cOBFOYaPaw==",
+ "version": "4.4.9",
+ "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.9.tgz",
+ "integrity": "sha512-495/V2I15SHgedSJoDPD23JuSfKAp726ZI1V0wtjB07Wh7q/0tri/0e0DLefZCHgxZonrGKt/OCTpAtP1wE1kQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.14.0",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1561,16 +1631,16 @@
}
},
"node_modules/@smithy/signature-v4": {
- "version": "5.3.13",
- "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.13.tgz",
- "integrity": "sha512-YpYSyM0vMDwKbHD/JA7bVOF6kToVRpa+FM5ateEVRpsTNu564g1muBlkTubXhSKKYXInhpADF46FPyrZcTLpXg==",
+ "version": "5.3.14",
+ "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.14.tgz",
+ "integrity": "sha512-1D9Y/nmlVjCeSivCbhZ7hgEpmHyY1h0GvpSZt3l0xcD9JjmjVC1CHOozS6+Gh+/ldMH8JuJ6cujObQqfayAVFA==",
"license": "Apache-2.0",
"dependencies": {
"@smithy/is-array-buffer": "^4.2.2",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/types": "^4.14.0",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
"@smithy/util-hex-encoding": "^4.2.2",
- "@smithy/util-middleware": "^4.2.13",
+ "@smithy/util-middleware": "^4.2.14",
"@smithy/util-uri-escape": "^4.2.2",
"@smithy/util-utf8": "^4.2.2",
"tslib": "^2.6.2"
@@ -1580,17 +1650,17 @@
}
},
"node_modules/@smithy/smithy-client": {
- "version": "4.12.9",
- "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.9.tgz",
- "integrity": "sha512-ovaLEcTU5olSeHcRXcxV6viaKtpkHZumn6Ps0yn7dRf2rRSfy794vpjOtrWDO0d1auDSvAqxO+lyhERSXQ03EQ==",
+ "version": "4.12.13",
+ "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.13.tgz",
+ "integrity": "sha512-y/Pcj1V9+qG98gyu1gvftHB7rDpdh+7kIBIggs55yGm3JdtBV8GT8IFF3a1qxZ79QnaJHX9GXzvBG6tAd+czJA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/core": "^3.23.14",
- "@smithy/middleware-endpoint": "^4.4.29",
- "@smithy/middleware-stack": "^4.2.13",
- "@smithy/protocol-http": "^5.3.13",
- "@smithy/types": "^4.14.0",
- "@smithy/util-stream": "^4.5.22",
+ "@smithy/core": "^3.23.17",
+ "@smithy/middleware-endpoint": "^4.4.32",
+ "@smithy/middleware-stack": "^4.2.14",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
+ "@smithy/util-stream": "^4.5.25",
"tslib": "^2.6.2"
},
"engines": {
@@ -1598,9 +1668,9 @@
}
},
"node_modules/@smithy/types": {
- "version": "4.14.0",
- "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.0.tgz",
- "integrity": "sha512-OWgntFLW88kx2qvf/c/67Vno1yuXm/f9M7QFAtVkkO29IJXGBIg0ycEaBTH0kvCtwmvZxRujrgP5a86RvsXJAQ==",
+ "version": "4.14.1",
+ "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.1.tgz",
+ "integrity": "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.6.2"
@@ -1610,13 +1680,13 @@
}
},
"node_modules/@smithy/url-parser": {
- "version": "4.2.13",
- "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.13.tgz",
- "integrity": "sha512-2G03yoboIRZlZze2+PT4GZEjgwQsJjUgn6iTsvxA02bVceHR6vp4Cuk7TUnPFWKF+ffNUk3kj4COwkENS2K3vw==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.14.tgz",
+ "integrity": "sha512-p06BiBigJ8bTA3MgnOfCtDUWnAMY0YfedO/GRpmc7p+wg3KW8vbXy1xwSu5ASy0wV7rRYtlfZOIKH4XqfhjSQQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/querystring-parser": "^4.2.13",
- "@smithy/types": "^4.14.0",
+ "@smithy/querystring-parser": "^4.2.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1687,14 +1757,14 @@
}
},
"node_modules/@smithy/util-defaults-mode-browser": {
- "version": "4.3.45",
- "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.45.tgz",
- "integrity": "sha512-ag9sWc6/nWZAuK3Wm9KlFJUnRkXLrXn33RFjIAmCTFThqLHY+7wCst10BGq56FxslsDrjhSie46c8OULS+BiIw==",
+ "version": "4.3.49",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.49.tgz",
+ "integrity": "sha512-a5bNrdiONYB/qE2BuKegvUMd/+ZDwdg4vsNuuSzYE8qs2EYAdK9CynL+Rzn29PbPiUqoz/cbpRbcLzD5lEevHw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/property-provider": "^4.2.13",
- "@smithy/smithy-client": "^4.12.9",
- "@smithy/types": "^4.14.0",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1702,17 +1772,17 @@
}
},
"node_modules/@smithy/util-defaults-mode-node": {
- "version": "4.2.49",
- "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.49.tgz",
- "integrity": "sha512-jlN6vHwE8gY5AfiFBavtD3QtCX2f7lM3BKkz7nFKSNfFR5nXLXLg6sqXTJEEyDwtxbztIDBQCfjsGVXlIru2lQ==",
+ "version": "4.2.54",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.54.tgz",
+ "integrity": "sha512-g1cvrJvOnzeJgEdf7AE4luI7gp6L8weE0y9a9wQUSGtjb8QRHDbCJYuE4Sy0SD9N8RrnNPFsPltAz/OSoBR9Zw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/config-resolver": "^4.4.14",
- "@smithy/credential-provider-imds": "^4.2.13",
- "@smithy/node-config-provider": "^4.3.13",
- "@smithy/property-provider": "^4.2.13",
- "@smithy/smithy-client": "^4.12.9",
- "@smithy/types": "^4.14.0",
+ "@smithy/config-resolver": "^4.4.17",
+ "@smithy/credential-provider-imds": "^4.2.14",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1720,13 +1790,13 @@
}
},
"node_modules/@smithy/util-endpoints": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.4.tgz",
- "integrity": "sha512-BKoR/ubPp9KNKFxPpg1J28N1+bgu8NGAtJblBP7yHy8yQPBWhIAv9+l92SlQLpolGm71CVO+btB60gTgzT0wog==",
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.4.2.tgz",
+ "integrity": "sha512-a55Tr+3OKld4TTtnT+RhKOQHyPxm3j/xL4OR83WBUhLJaKDS9dnJ7arRMOp3t31dcLhApwG9bgvrRXBHlLdIkg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/node-config-provider": "^4.3.13",
- "@smithy/types": "^4.14.0",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1746,12 +1816,12 @@
}
},
"node_modules/@smithy/util-middleware": {
- "version": "4.2.13",
- "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.13.tgz",
- "integrity": "sha512-GTooyrlmRTqvUen4eK7/K1p6kryF7bnDfq6XsAbIsf2mo51B/utaH+XThY6dKgNCWzMAaH/+OLmqaBuLhLWRow==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.14.tgz",
+ "integrity": "sha512-1Su2vj9RYNDEv/V+2E+jXkkwGsgR7dc4sfHn9Z7ruzQHJIEni9zzw5CauvRXlFJfmgcqYP8fWa0dkh2Q2YaQyw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.14.0",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1759,13 +1829,13 @@
}
},
"node_modules/@smithy/util-retry": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.3.0.tgz",
- "integrity": "sha512-tSOPQNT/4KfbvqeMovWC3g23KSYy8czHd3tlN+tOYVNIDLSfxIsrPJihYi5TpNcoV789KWtgChUVedh2y6dDPg==",
+ "version": "4.3.8",
+ "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.3.8.tgz",
+ "integrity": "sha512-LUIxbTBi+OpvXpg91poGA6BdyoleMDLnfXjVDqyi2RvZmTveY5loE/FgYUBCR5LU2BThW2SoZRh8dTIIy38IPw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/service-error-classification": "^4.2.13",
- "@smithy/types": "^4.14.0",
+ "@smithy/service-error-classification": "^4.3.1",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1773,14 +1843,14 @@
}
},
"node_modules/@smithy/util-stream": {
- "version": "4.5.22",
- "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.22.tgz",
- "integrity": "sha512-3H8iq/0BfQjUs2/4fbHZ9aG9yNzcuZs24LPkcX1Q7Z+qpqaGM8+qbGmE8zo9m2nCRgamyvS98cHdcWvR6YUsew==",
+ "version": "4.5.25",
+ "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.25.tgz",
+ "integrity": "sha512-/PFpG4k8Ze8Ei+mMKj3oiPICYekthuzePZMgZbCqMiXIHHf4n2aZ4Ps0aSRShycFTGuj/J6XldmC0x0DwednIA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/fetch-http-handler": "^5.3.16",
- "@smithy/node-http-handler": "^4.5.2",
- "@smithy/types": "^4.14.0",
+ "@smithy/fetch-http-handler": "^5.3.17",
+ "@smithy/node-http-handler": "^4.6.1",
+ "@smithy/types": "^4.14.1",
"@smithy/util-base64": "^4.3.2",
"@smithy/util-buffer-from": "^4.2.2",
"@smithy/util-hex-encoding": "^4.2.2",
@@ -1817,12 +1887,12 @@
}
},
"node_modules/@smithy/util-waiter": {
- "version": "4.2.15",
- "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.15.tgz",
- "integrity": "sha512-oUt9o7n8hBv3BL56sLSneL0XeigZSuem0Hr78JaoK33D9oKieyCvVP8eTSe3j7g2mm/S1DvzxKieG7JEWNJUNg==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.3.0.tgz",
+ "integrity": "sha512-JyjYmLAfS+pdxF92o4yLgEoy0zhayKTw73FU1aofLWwLcJw7iSqIY2exGmMTrl/lmZugP5p/zxdFSippJDfKWA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.14.0",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1975,12 +2045,12 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "25.5.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz",
- "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==",
+ "version": "25.6.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.2.tgz",
+ "integrity": "sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw==",
"license": "MIT",
"dependencies": {
- "undici-types": "~7.18.0"
+ "undici-types": "~7.19.0"
}
},
"node_modules/@types/pg": {
@@ -1996,9 +2066,9 @@
}
},
"node_modules/@types/qs": {
- "version": "6.15.0",
- "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz",
- "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==",
+ "version": "6.15.1",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz",
+ "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==",
"dev": true,
"license": "MIT"
},
@@ -2208,9 +2278,9 @@
"license": "MIT"
},
"node_modules/brace-expansion": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
- "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2338,9 +2408,9 @@
"license": "MIT"
},
"node_modules/content-disposition": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
- "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz",
+ "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -2707,9 +2777,9 @@
"license": "MIT"
},
"node_modules/fast-xml-builder": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz",
- "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz",
+ "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==",
"funding": [
{
"type": "github",
@@ -2718,13 +2788,14 @@
],
"license": "MIT",
"dependencies": {
- "path-expression-matcher": "^1.1.3"
+ "path-expression-matcher": "^1.5.0",
+ "xml-naming": "^0.1.0"
}
},
"node_modules/fast-xml-parser": {
- "version": "5.5.8",
- "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.8.tgz",
- "integrity": "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==",
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.2.tgz",
+ "integrity": "sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==",
"funding": [
{
"type": "github",
@@ -2733,9 +2804,10 @@
],
"license": "MIT",
"dependencies": {
- "fast-xml-builder": "^1.1.4",
- "path-expression-matcher": "^1.2.0",
- "strnum": "^2.2.0"
+ "@nodable/entities": "^2.1.0",
+ "fast-xml-builder": "^1.1.5",
+ "path-expression-matcher": "^1.5.0",
+ "strnum": "^2.2.3"
},
"bin": {
"fxparser": "src/cli/cli.js"
@@ -2920,9 +2992,9 @@
}
},
"node_modules/get-tsconfig": {
- "version": "4.13.7",
- "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz",
- "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==",
+ "version": "4.14.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz",
+ "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3008,9 +3080,9 @@
}
},
"node_modules/hasown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
+ "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -3096,13 +3168,13 @@
}
},
"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==",
+ "version": "2.16.2",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz",
+ "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "hasown": "^2.0.2"
+ "hasown": "^2.0.3"
},
"engines": {
"node": ">= 0.4"
@@ -3462,9 +3534,9 @@
}
},
"node_modules/path-expression-matcher": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.4.0.tgz",
- "integrity": "sha512-s4DQMxIdhj3jLFWd9LxHOplj4p9yQ4ffMGowFf3cpEgrrJjEhN0V5nxw4Ye1EViAGDoL4/1AeO6qHpqYPOzE4Q==",
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz",
+ "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==",
"funding": [
{
"type": "github",
@@ -3658,9 +3730,9 @@
}
},
"node_modules/qs": {
- "version": "6.15.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
- "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
+ "version": "6.15.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz",
+ "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
@@ -3710,12 +3782,13 @@
}
},
"node_modules/resolve": {
- "version": "1.22.11",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
- "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "version": "1.22.12",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
+ "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
"dev": true,
"license": "MIT",
"dependencies": {
+ "es-errors": "^1.3.0",
"is-core-module": "^2.16.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
@@ -3797,9 +3870,9 @@
"license": "MIT"
},
"node_modules/semver": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
- "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
+ "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -3879,13 +3952,13 @@
}
},
"node_modules/side-channel-list": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
- "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
- "object-inspect": "^1.13.3"
+ "object-inspect": "^1.13.4"
},
"engines": {
"node": ">= 0.4"
@@ -4004,9 +4077,9 @@
}
},
"node_modules/strnum": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz",
- "integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz",
+ "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==",
"funding": [
{
"type": "github",
@@ -4239,9 +4312,9 @@
}
},
"node_modules/typescript": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
- "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
+ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -4253,9 +4326,9 @@
}
},
"node_modules/undici-types": {
- "version": "7.18.2",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
- "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
+ "version": "7.19.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
+ "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
"license": "MIT"
},
"node_modules/unpipe": {
@@ -4289,6 +4362,21 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
},
+ "node_modules/xml-naming": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz",
+ "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
diff --git a/apps/api/package.json b/apps/api/package.json
index 8faaad4..f14fe22 100644
--- a/apps/api/package.json
+++ b/apps/api/package.json
@@ -12,6 +12,7 @@
"migrate": "tsx src/db/migrate.ts",
"seed:demo": "node src/scripts/seed-demo.js",
"build": "tsc -p tsconfig.json && node scripts/copy-build-assets.mjs",
+ "build:lambda": "npm run build && node scripts/build-lambda-package.mjs",
"start": "node dist/server.js",
"start:worker": "node dist/jobs/worker.js",
"start:migrate": "node dist/db/migrate.js",
diff --git a/apps/api/scripts/build-lambda-package.mjs b/apps/api/scripts/build-lambda-package.mjs
new file mode 100644
index 0000000..f036b25
--- /dev/null
+++ b/apps/api/scripts/build-lambda-package.mjs
@@ -0,0 +1,57 @@
+import fs from "node:fs/promises";
+import path from "node:path";
+import { spawn } from "node:child_process";
+import { fileURLToPath } from "node:url";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+const projectRoot = path.resolve(__dirname, "..");
+const lambdaRoot = path.join(projectRoot, "build", "lambda");
+
+const copyDirectory = async (source, target) => {
+ await fs.mkdir(target, { recursive: true });
+ const entries = await fs.readdir(source, { withFileTypes: true });
+
+ for (const entry of entries) {
+ const sourcePath = path.join(source, entry.name);
+ const targetPath = path.join(target, entry.name);
+
+ if (entry.isDirectory()) {
+ await copyDirectory(sourcePath, targetPath);
+ continue;
+ }
+
+ await fs.copyFile(sourcePath, targetPath);
+ }
+};
+
+const runCommand = (command, args, cwd) =>
+ new Promise((resolve, reject) => {
+ const child = spawn(command, args, {
+ cwd,
+ stdio: "inherit",
+ shell: process.platform === "win32",
+ });
+
+ child.on("error", reject);
+ child.on("exit", (code) => {
+ if (code === 0) {
+ resolve();
+ return;
+ }
+
+ reject(new Error(`${command} ${args.join(" ")} failed with exit code ${code ?? "unknown"}`));
+ });
+ });
+
+await fs.rm(lambdaRoot, { recursive: true, force: true });
+await fs.mkdir(lambdaRoot, { recursive: true });
+
+await fs.copyFile(path.join(projectRoot, "package.json"), path.join(lambdaRoot, "package.json"));
+await fs.copyFile(
+ path.join(projectRoot, "package-lock.json"),
+ path.join(lambdaRoot, "package-lock.json"),
+);
+await copyDirectory(path.join(projectRoot, "dist"), path.join(lambdaRoot, "dist"));
+
+await runCommand("npm", ["ci", "--omit=dev"], lambdaRoot);
diff --git a/apps/api/src/jobs/scheduled-cost-sync.lambda.ts b/apps/api/src/jobs/scheduled-cost-sync.lambda.ts
new file mode 100644
index 0000000..6068ec7
--- /dev/null
+++ b/apps/api/src/jobs/scheduled-cost-sync.lambda.ts
@@ -0,0 +1,13 @@
+import { runScheduledCostSync } from "./scheduled-cost-sync.js";
+
+export const handler = async (): Promise<{
+ statusCode: number;
+ body: string;
+}> => {
+ const result = await runScheduledCostSync();
+
+ return {
+ statusCode: 200,
+ body: JSON.stringify(result),
+ };
+};
diff --git a/apps/api/src/jobs/scheduled-cost-sync.ts b/apps/api/src/jobs/scheduled-cost-sync.ts
new file mode 100644
index 0000000..9252277
--- /dev/null
+++ b/apps/api/src/jobs/scheduled-cost-sync.ts
@@ -0,0 +1,35 @@
+import { connectToDatabase } from "../config/db.js";
+import { logger } from "../lib/logger.js";
+import { costService } from "../services/cost.service.js";
+import type { ScheduledCostSyncSummary } from "../types/aws-account.types.js";
+
+export const scheduledCostSyncDependencies = {
+ connectToDatabase,
+ syncAllVerifiedAccounts: async (): Promise =>
+ costService.syncAllVerifiedAccounts(),
+};
+
+export const runScheduledCostSync = async (): Promise => {
+ const startedAt = Date.now();
+ logger.info("Scheduled cost sync Lambda started");
+
+ await scheduledCostSyncDependencies.connectToDatabase();
+
+ try {
+ const result = await scheduledCostSyncDependencies.syncAllVerifiedAccounts();
+ logger.info("Scheduled cost sync Lambda completed", {
+ durationMs: Date.now() - startedAt,
+ scannedAccounts: result.scannedAccounts,
+ syncedAccounts: result.syncedAccounts,
+ skippedAccounts: result.skippedAccounts,
+ failedAccounts: result.failedAccounts,
+ });
+ return result;
+ } catch (error) {
+ logger.error("Scheduled cost sync Lambda failed", {
+ durationMs: Date.now() - startedAt,
+ errorMessage: error instanceof Error ? error.message : "Unknown sync error",
+ });
+ throw error;
+ }
+};
diff --git a/apps/api/src/jobs/scheduler.ts b/apps/api/src/jobs/scheduler.ts
index 7052a8a..783dc9a 100644
--- a/apps/api/src/jobs/scheduler.ts
+++ b/apps/api/src/jobs/scheduler.ts
@@ -3,7 +3,6 @@ import cron from "node-cron";
import { logger } from "../lib/logger.js";
import { jobLockRepository } from "../repositories/job-lock.repository.js";
import { alertService } from "../services/alert.service.js";
-import { costService } from "../services/cost.service.js";
const runScheduledJob = async (
jobName: string,
@@ -24,17 +23,6 @@ const runScheduledJob = async (
};
export const startJobScheduler = (): void => {
- cron.schedule("0 2 * * *", () => {
- void runScheduledJob("sync_all_verified_accounts", () =>
- costService.syncAllVerifiedAccounts(),
- ).catch((error: unknown) => {
- logger.error("Scheduled job failed", {
- jobName: "sync_all_verified_accounts",
- errorMessage: error instanceof Error ? error.message : "Unknown job error",
- });
- });
- });
-
cron.schedule("0 * * * *", () => {
void runScheduledJob("evaluate_active_alerts", () =>
alertService.evaluateActiveAlerts(),
diff --git a/apps/api/src/jobs/worker.ts b/apps/api/src/jobs/worker.ts
index c98f79d..6808cad 100644
--- a/apps/api/src/jobs/worker.ts
+++ b/apps/api/src/jobs/worker.ts
@@ -5,7 +5,7 @@ import { startJobScheduler } from "./scheduler.js";
const startWorker = async (): Promise => {
await connectToDatabase();
startJobScheduler();
- logger.info("Background jobs started");
+ logger.info("Background alert jobs started");
};
startWorker().catch((error: unknown) => {
diff --git a/apps/api/src/services/cost.service.ts b/apps/api/src/services/cost.service.ts
index 1b1c6a3..d376e19 100644
--- a/apps/api/src/services/cost.service.ts
+++ b/apps/api/src/services/cost.service.ts
@@ -7,6 +7,7 @@ import type {
CostQueryInput,
CostSummary,
CostSyncResult,
+ ScheduledCostSyncSummary,
ServiceCostBreakdownItem,
SyncHistoryItem,
SyncHistoryQueryInput,
@@ -137,8 +138,14 @@ export const costService = {
return costRepository.getSyncHistory(workspaceId, input);
},
- async syncAllVerifiedAccounts(): Promise {
+ async syncAllVerifiedAccounts(): Promise {
const accounts = await awsAccountRepository.findActiveForSync();
+ const summary: ScheduledCostSyncSummary = {
+ scannedAccounts: accounts.length,
+ syncedAccounts: 0,
+ skippedAccounts: 0,
+ failedAccounts: 0,
+ };
for (const account of accounts) {
const lock = await jobLockRepository.withAdvisoryLock(
@@ -178,12 +185,15 @@ export const costService = {
awsAccountId: account.id,
workspaceId: account.workspaceId,
});
+ summary.skippedAccounts += 1;
continue;
}
try {
await lock.result;
+ summary.syncedAccounts += 1;
} catch (error) {
+ summary.failedAccounts += 1;
logger.error("Background AWS sync failed", {
awsAccountId: account.id,
workspaceId: account.workspaceId,
@@ -191,5 +201,7 @@ export const costService = {
});
}
}
+
+ return summary;
},
};
diff --git a/apps/api/src/test/scheduled-cost-sync.test.ts b/apps/api/src/test/scheduled-cost-sync.test.ts
new file mode 100644
index 0000000..090bd28
--- /dev/null
+++ b/apps/api/src/test/scheduled-cost-sync.test.ts
@@ -0,0 +1,202 @@
+import assert from "node:assert/strict";
+import test from "node:test";
+
+const ensureTestEnv = (): void => {
+ process.env.NODE_ENV = "test";
+ process.env.DATABASE_URL ??=
+ "postgresql://postgres:postgres@localhost:5432/underflow_test";
+ process.env.CSRF_SECRET ??= "test-csrf-secret";
+ process.env.JWT_ACCESS_SECRET ??= "test-access-secret";
+ process.env.JWT_REFRESH_SECRET ??= "test-refresh-secret";
+ process.env.CLIENT_URL ??= "http://localhost:5174";
+};
+
+test("scheduled cost sync runner connects to the database and returns sync summary", async () => {
+ ensureTestEnv();
+
+ const { runScheduledCostSync, scheduledCostSyncDependencies } = await import(
+ "../jobs/scheduled-cost-sync.js"
+ );
+
+ const originalConnect = scheduledCostSyncDependencies.connectToDatabase;
+ const originalSync = scheduledCostSyncDependencies.syncAllVerifiedAccounts;
+ let connectCalls = 0;
+
+ scheduledCostSyncDependencies.connectToDatabase = async () => {
+ connectCalls += 1;
+ };
+ scheduledCostSyncDependencies.syncAllVerifiedAccounts = async () => ({
+ scannedAccounts: 4,
+ syncedAccounts: 3,
+ skippedAccounts: 1,
+ failedAccounts: 0,
+ });
+
+ try {
+ const result = await runScheduledCostSync();
+ assert.equal(connectCalls, 1);
+ assert.deepEqual(result, {
+ scannedAccounts: 4,
+ syncedAccounts: 3,
+ skippedAccounts: 1,
+ failedAccounts: 0,
+ });
+ } finally {
+ scheduledCostSyncDependencies.connectToDatabase = originalConnect;
+ scheduledCostSyncDependencies.syncAllVerifiedAccounts = originalSync;
+ }
+});
+
+test("scheduled cost sync Lambda handler returns a 200 with the sync summary body", async () => {
+ ensureTestEnv();
+
+ const [{ handler }, { scheduledCostSyncDependencies }] = await Promise.all([
+ import("../jobs/scheduled-cost-sync.lambda.js"),
+ import("../jobs/scheduled-cost-sync.js"),
+ ]);
+
+ const originalConnect = scheduledCostSyncDependencies.connectToDatabase;
+ const originalSync = scheduledCostSyncDependencies.syncAllVerifiedAccounts;
+
+ scheduledCostSyncDependencies.connectToDatabase = async () => {};
+ scheduledCostSyncDependencies.syncAllVerifiedAccounts = async () => ({
+ scannedAccounts: 2,
+ syncedAccounts: 2,
+ skippedAccounts: 0,
+ failedAccounts: 0,
+ });
+
+ try {
+ const response = await handler();
+ assert.equal(response.statusCode, 200);
+ assert.deepEqual(JSON.parse(response.body), {
+ scannedAccounts: 2,
+ syncedAccounts: 2,
+ skippedAccounts: 0,
+ failedAccounts: 0,
+ });
+ } finally {
+ scheduledCostSyncDependencies.connectToDatabase = originalConnect;
+ scheduledCostSyncDependencies.syncAllVerifiedAccounts = originalSync;
+ }
+});
+
+test("scheduled cost sync Lambda handler surfaces failures", async () => {
+ ensureTestEnv();
+
+ const [{ handler }, { scheduledCostSyncDependencies }] = await Promise.all([
+ import("../jobs/scheduled-cost-sync.lambda.js"),
+ import("../jobs/scheduled-cost-sync.js"),
+ ]);
+
+ const originalConnect = scheduledCostSyncDependencies.connectToDatabase;
+ const originalSync = scheduledCostSyncDependencies.syncAllVerifiedAccounts;
+
+ scheduledCostSyncDependencies.connectToDatabase = async () => {};
+ scheduledCostSyncDependencies.syncAllVerifiedAccounts = async () => {
+ throw new Error("sync exploded");
+ };
+
+ try {
+ await assert.rejects(handler, /sync exploded/);
+ } finally {
+ scheduledCostSyncDependencies.connectToDatabase = originalConnect;
+ scheduledCostSyncDependencies.syncAllVerifiedAccounts = originalSync;
+ }
+});
+
+test("syncAllVerifiedAccounts returns per-account summary counts", async () => {
+ ensureTestEnv();
+
+ const [
+ { costService },
+ { awsAccountRepository },
+ { jobLockRepository },
+ { costRepository },
+ { costExplorerService },
+ ] = await Promise.all([
+ import("../services/cost.service.js"),
+ import("../repositories/aws-account.repository.js"),
+ import("../repositories/job-lock.repository.js"),
+ import("../repositories/cost.repository.js"),
+ import("../services/cost-explorer.service.js"),
+ ]);
+
+ const originalFindActive = awsAccountRepository.findActiveForSync;
+ const originalUpdateLastSyncAt = awsAccountRepository.updateLastSyncAt;
+ const originalLock = jobLockRepository.withAdvisoryLock;
+ const originalCreateSyncRun = costRepository.createSyncRun;
+ const originalCompleteSyncRun = costRepository.completeSyncRun;
+ const originalReplaceSnapshots = costRepository.replaceSnapshots;
+ const originalFetchCostData = costExplorerService.fetchCostData;
+
+ let activeAccountIndex = 0;
+
+ awsAccountRepository.findActiveForSync = async () => [
+ {
+ id: "account-1",
+ workspaceId: "workspace-1",
+ name: "Prod",
+ awsAccountId: "111111111111",
+ roleArn: "arn:aws:iam::111111111111:role/UnderflowCostExplorerRead",
+ externalId: null,
+ status: "verified",
+ lastVerifiedAt: null,
+ lastSyncAt: null,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ },
+ {
+ id: "account-2",
+ workspaceId: "workspace-1",
+ name: "Stage",
+ awsAccountId: "222222222222",
+ roleArn: "arn:aws:iam::222222222222:role/UnderflowCostExplorerRead",
+ externalId: null,
+ status: "verified",
+ lastVerifiedAt: null,
+ lastSyncAt: null,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ },
+ ];
+ awsAccountRepository.updateLastSyncAt = async () => {};
+ jobLockRepository.withAdvisoryLock = async (_key, callback) => {
+ activeAccountIndex += 1;
+
+ if (activeAccountIndex === 2) {
+ return { acquired: false };
+ }
+
+ return { acquired: true, result: await callback() };
+ };
+ costRepository.createSyncRun = async () => "sync-1";
+ costRepository.completeSyncRun = async () => {};
+ costRepository.replaceSnapshots = async ({ entries }) => entries.length;
+ costExplorerService.fetchCostData = async () => [
+ {
+ usageDate: "2026-01-01",
+ serviceName: "AmazonEC2",
+ amount: 12.4,
+ currency: "USD",
+ },
+ ];
+
+ try {
+ const result = await costService.syncAllVerifiedAccounts();
+ assert.deepEqual(result, {
+ scannedAccounts: 2,
+ syncedAccounts: 1,
+ skippedAccounts: 1,
+ failedAccounts: 0,
+ });
+ } finally {
+ awsAccountRepository.findActiveForSync = originalFindActive;
+ awsAccountRepository.updateLastSyncAt = originalUpdateLastSyncAt;
+ jobLockRepository.withAdvisoryLock = originalLock;
+ costRepository.createSyncRun = originalCreateSyncRun;
+ costRepository.completeSyncRun = originalCompleteSyncRun;
+ costRepository.replaceSnapshots = originalReplaceSnapshots;
+ costExplorerService.fetchCostData = originalFetchCostData;
+ }
+});
diff --git a/apps/api/src/types/aws-account.types.ts b/apps/api/src/types/aws-account.types.ts
index a90455f..fe0337b 100644
--- a/apps/api/src/types/aws-account.types.ts
+++ b/apps/api/src/types/aws-account.types.ts
@@ -39,6 +39,13 @@ export interface CostSyncResult {
recordsSynced: number;
}
+export interface ScheduledCostSyncSummary {
+ scannedAccounts: number;
+ syncedAccounts: number;
+ skippedAccounts: number;
+ failedAccounts: number;
+}
+
export interface CostSummary {
totalAmount: number;
currency: string;
diff --git a/apps/web/package-lock.json b/apps/web/package-lock.json
index 0b528af..456d303 100644
--- a/apps/web/package-lock.json
+++ b/apps/web/package-lock.json
@@ -63,9 +63,9 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
- "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
+ "version": "7.29.3",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz",
+ "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -234,9 +234,9 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.29.2",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
- "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
+ "version": "7.29.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz",
+ "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -953,9 +953,9 @@
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz",
- "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz",
+ "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==",
"cpu": [
"arm"
],
@@ -967,9 +967,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz",
- "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.3.tgz",
+ "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==",
"cpu": [
"arm64"
],
@@ -981,9 +981,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz",
- "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.3.tgz",
+ "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==",
"cpu": [
"arm64"
],
@@ -995,9 +995,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz",
- "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.3.tgz",
+ "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==",
"cpu": [
"x64"
],
@@ -1009,9 +1009,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz",
- "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.3.tgz",
+ "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==",
"cpu": [
"arm64"
],
@@ -1023,9 +1023,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz",
- "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.3.tgz",
+ "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==",
"cpu": [
"x64"
],
@@ -1037,9 +1037,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz",
- "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.3.tgz",
+ "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==",
"cpu": [
"arm"
],
@@ -1054,9 +1054,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz",
- "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.3.tgz",
+ "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==",
"cpu": [
"arm"
],
@@ -1071,9 +1071,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz",
- "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.3.tgz",
+ "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==",
"cpu": [
"arm64"
],
@@ -1088,9 +1088,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz",
- "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.3.tgz",
+ "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==",
"cpu": [
"arm64"
],
@@ -1105,9 +1105,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz",
- "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.3.tgz",
+ "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==",
"cpu": [
"loong64"
],
@@ -1122,9 +1122,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-musl": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz",
- "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.3.tgz",
+ "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==",
"cpu": [
"loong64"
],
@@ -1139,9 +1139,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz",
- "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.3.tgz",
+ "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==",
"cpu": [
"ppc64"
],
@@ -1156,9 +1156,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz",
- "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.3.tgz",
+ "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==",
"cpu": [
"ppc64"
],
@@ -1173,9 +1173,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz",
- "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.3.tgz",
+ "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==",
"cpu": [
"riscv64"
],
@@ -1190,9 +1190,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz",
- "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.3.tgz",
+ "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==",
"cpu": [
"riscv64"
],
@@ -1207,9 +1207,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz",
- "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.3.tgz",
+ "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==",
"cpu": [
"s390x"
],
@@ -1224,9 +1224,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz",
- "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.3.tgz",
+ "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==",
"cpu": [
"x64"
],
@@ -1241,9 +1241,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz",
- "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.3.tgz",
+ "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==",
"cpu": [
"x64"
],
@@ -1258,9 +1258,9 @@
]
},
"node_modules/@rollup/rollup-openbsd-x64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz",
- "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.3.tgz",
+ "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==",
"cpu": [
"x64"
],
@@ -1272,9 +1272,9 @@
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz",
- "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.3.tgz",
+ "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==",
"cpu": [
"arm64"
],
@@ -1286,9 +1286,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz",
- "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.3.tgz",
+ "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==",
"cpu": [
"arm64"
],
@@ -1300,9 +1300,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz",
- "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.3.tgz",
+ "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==",
"cpu": [
"ia32"
],
@@ -1314,9 +1314,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz",
- "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.3.tgz",
+ "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==",
"cpu": [
"x64"
],
@@ -1328,9 +1328,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz",
- "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.3.tgz",
+ "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==",
"cpu": [
"x64"
],
@@ -1758,9 +1758,9 @@
}
},
"node_modules/baseline-browser-mapping": {
- "version": "2.10.17",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.17.tgz",
- "integrity": "sha512-HdrkN8eVG2CXxeifv/VdJ4A4RSra1DTW8dc/hdxzhGHN8QePs6gKaWM9pHPcpCoxYZJuOZ8drHmbdpLHjCYjLA==",
+ "version": "2.10.29",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.29.tgz",
+ "integrity": "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -1815,9 +1815,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001787",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz",
- "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==",
+ "version": "1.0.30001792",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz",
+ "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==",
"dev": true,
"funding": [
{
@@ -2117,9 +2117,9 @@
}
},
"node_modules/electron-to-chromium": {
- "version": "1.5.334",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.334.tgz",
- "integrity": "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog==",
+ "version": "1.5.353",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.353.tgz",
+ "integrity": "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==",
"dev": true,
"license": "ISC"
},
@@ -2488,9 +2488,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
- "version": "3.3.11",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
- "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "version": "3.3.12",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
+ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
"dev": true,
"funding": [
{
@@ -2507,9 +2507,9 @@
}
},
"node_modules/node-releases": {
- "version": "2.0.37",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz",
- "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==",
+ "version": "2.0.38",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz",
+ "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==",
"dev": true,
"license": "MIT"
},
@@ -2580,9 +2580,9 @@
}
},
"node_modules/postcss": {
- "version": "8.5.9",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz",
- "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==",
+ "version": "8.5.14",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
+ "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
"dev": true,
"funding": [
{
@@ -2624,14 +2624,6 @@
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
}
},
- "node_modules/pretty-format/node_modules/react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true,
- "license": "MIT",
- "peer": true
- },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -2660,31 +2652,33 @@
}
},
"node_modules/react": {
- "version": "19.2.5",
- "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz",
- "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==",
+ "version": "19.2.6",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz",
+ "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
- "version": "19.2.5",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz",
- "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==",
+ "version": "19.2.6",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz",
+ "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==",
"license": "MIT",
"dependencies": {
"scheduler": "^0.27.0"
},
"peerDependencies": {
- "react": "^19.2.5"
+ "react": "^19.2.6"
}
},
"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==",
- "license": "MIT"
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
},
"node_modules/react-refresh": {
"version": "0.17.0",
@@ -2697,9 +2691,9 @@
}
},
"node_modules/react-router": {
- "version": "7.14.0",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.0.tgz",
- "integrity": "sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ==",
+ "version": "7.15.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.15.0.tgz",
+ "integrity": "sha512-HW9vYwuM8f4yx66Izy8xfrzCM+SBJluoZcCbww9A1TySax11S5Vgw6fi3ZjMONw9J4gQwngL7PzkyIpJJpJ7RQ==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
@@ -2719,12 +2713,12 @@
}
},
"node_modules/react-router-dom": {
- "version": "7.14.0",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.0.tgz",
- "integrity": "sha512-2G3ajSVSZMEtmTjIklRWlNvo8wICEpLihfD/0YMDxbWK2UyP5EGfnoIn9AIQGnF3G/FX0MRbHXdFcD+rL1ZreQ==",
+ "version": "7.15.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.15.0.tgz",
+ "integrity": "sha512-VcrVg64Fo8nwBvDscajG8gRTLIuTC6N50nb22l2HOOV4PTOHgoGp8mUjy9wLiHYoYTSYI36tUnXZgasSRFZorQ==",
"license": "MIT",
"dependencies": {
- "react-router": "7.14.0"
+ "react-router": "7.15.0"
},
"engines": {
"node": ">=20.0.0"
@@ -2797,10 +2791,16 @@
"decimal.js-light": "^2.4.1"
}
},
+ "node_modules/recharts/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==",
+ "license": "MIT"
+ },
"node_modules/rollup": {
- "version": "4.60.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz",
- "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==",
+ "version": "4.60.3",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz",
+ "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2814,31 +2814,31 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.60.1",
- "@rollup/rollup-android-arm64": "4.60.1",
- "@rollup/rollup-darwin-arm64": "4.60.1",
- "@rollup/rollup-darwin-x64": "4.60.1",
- "@rollup/rollup-freebsd-arm64": "4.60.1",
- "@rollup/rollup-freebsd-x64": "4.60.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.60.1",
- "@rollup/rollup-linux-arm-musleabihf": "4.60.1",
- "@rollup/rollup-linux-arm64-gnu": "4.60.1",
- "@rollup/rollup-linux-arm64-musl": "4.60.1",
- "@rollup/rollup-linux-loong64-gnu": "4.60.1",
- "@rollup/rollup-linux-loong64-musl": "4.60.1",
- "@rollup/rollup-linux-ppc64-gnu": "4.60.1",
- "@rollup/rollup-linux-ppc64-musl": "4.60.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.60.1",
- "@rollup/rollup-linux-riscv64-musl": "4.60.1",
- "@rollup/rollup-linux-s390x-gnu": "4.60.1",
- "@rollup/rollup-linux-x64-gnu": "4.60.1",
- "@rollup/rollup-linux-x64-musl": "4.60.1",
- "@rollup/rollup-openbsd-x64": "4.60.1",
- "@rollup/rollup-openharmony-arm64": "4.60.1",
- "@rollup/rollup-win32-arm64-msvc": "4.60.1",
- "@rollup/rollup-win32-ia32-msvc": "4.60.1",
- "@rollup/rollup-win32-x64-gnu": "4.60.1",
- "@rollup/rollup-win32-x64-msvc": "4.60.1",
+ "@rollup/rollup-android-arm-eabi": "4.60.3",
+ "@rollup/rollup-android-arm64": "4.60.3",
+ "@rollup/rollup-darwin-arm64": "4.60.3",
+ "@rollup/rollup-darwin-x64": "4.60.3",
+ "@rollup/rollup-freebsd-arm64": "4.60.3",
+ "@rollup/rollup-freebsd-x64": "4.60.3",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.60.3",
+ "@rollup/rollup-linux-arm-musleabihf": "4.60.3",
+ "@rollup/rollup-linux-arm64-gnu": "4.60.3",
+ "@rollup/rollup-linux-arm64-musl": "4.60.3",
+ "@rollup/rollup-linux-loong64-gnu": "4.60.3",
+ "@rollup/rollup-linux-loong64-musl": "4.60.3",
+ "@rollup/rollup-linux-ppc64-gnu": "4.60.3",
+ "@rollup/rollup-linux-ppc64-musl": "4.60.3",
+ "@rollup/rollup-linux-riscv64-gnu": "4.60.3",
+ "@rollup/rollup-linux-riscv64-musl": "4.60.3",
+ "@rollup/rollup-linux-s390x-gnu": "4.60.3",
+ "@rollup/rollup-linux-x64-gnu": "4.60.3",
+ "@rollup/rollup-linux-x64-musl": "4.60.3",
+ "@rollup/rollup-openbsd-x64": "4.60.3",
+ "@rollup/rollup-openharmony-arm64": "4.60.3",
+ "@rollup/rollup-win32-arm64-msvc": "4.60.3",
+ "@rollup/rollup-win32-ia32-msvc": "4.60.3",
+ "@rollup/rollup-win32-x64-gnu": "4.60.3",
+ "@rollup/rollup-win32-x64-msvc": "4.60.3",
"fsevents": "~2.3.2"
}
},
diff --git a/docs/architecture.md b/docs/architecture.md
index 6710856..7721336 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -1,6 +1,6 @@
# Architecture Overview
-Underflow is split into three main areas: an API, a frontend application, and infrastructure code that supports external integrations such as SES and Route 53.
+Underflow is split into three main areas: an API, a frontend application, and infrastructure code that supports AWS integrations, email delivery, and production deployment.
## System Shape
@@ -16,15 +16,15 @@ The API is responsible for:
- cost reporting endpoints
- alert creation and alert evaluation primitives
- notifications, billing, and supporting integrations
-- background worker entrypoints and DB-backed job safety
+- background execution entrypoints and DB-backed job safety
-The backend has evolved from raw schema bootstrapping to a migration-based setup and now includes:
+The backend includes:
- structured logging
- rate limiting
- standardized error envelopes
- JWT/session-version validation
-- DB-backed job locking for recurring sync/alert work
+- DB-backed advisory locking for recurring sync and alert work
### `apps/web`
@@ -37,23 +37,19 @@ The frontend is a React application responsible for:
- alerts, notifications, and settings pages
- responsive authenticated app shell
-It now includes:
-
-- route-based code splitting
-- route-level loading and retryable error handling
-- frontend tests for major authenticated flows
+It also includes route-based code splitting, route-level loading and retryable error handling, and frontend test coverage for the major authenticated flows.
### `infra/terraform`
-Infrastructure code currently focuses on shared AWS resources, especially the email/domain setup:
-
-- Route 53 hosted zones
-- SES identities
-- DKIM records
-- custom `MAIL FROM`
-- DMARC support
+Infrastructure code provisions the production AWS footprint and supporting integrations:
-The current pattern is designed around a delegated subdomain such as `underflow.example.com`, keeping the project isolated from the rest of a parent domain.
+- Route 53 hosted zones and DNS records
+- SES identities and mail records
+- ECS services for API and worker runtimes
+- Lambda for scheduled cost sync
+- RDS PostgreSQL
+- S3, CloudFront, and ALB resources
+- CI/CD bootstrap resources such as Terraform state and GitHub OIDC
## Main Runtime Flows
@@ -71,35 +67,36 @@ The current pattern is designed around a delegated subdomain such as `underflow.
### Cost monitoring
-- Cost data is synced through API/worker logic
+- Cost data is synced through the backend using assumed AWS credentials
+- A scheduled Lambda can run cost sync across all verified AWS accounts on a fixed interval
- Reporting endpoints expose summary, by-service, timeseries, and sync history views
- The frontend presents this through workspace-scoped dashboards and detail pages
### Alerts and notifications
- Alert rules are attached to a workspace, optionally scoped to a specific AWS account
-- Background alert evaluation logic creates events and notification deliveries
-- Notifications are surfaced in the frontend feed
+- The ECS worker evaluates active alerts on a schedule
+- Notification delivery and status are persisted and surfaced in the frontend feed
## Email / SES Integration Boundary
-Email is intentionally treated as a real-integration boundary rather than purely mocked infrastructure.
+Email is treated as a real integration boundary rather than a mocked afterthought.
- Terraform provisions SES identity-related DNS
- Route 53 hosts the delegated project subdomain
-- The parent domain remains at its existing registrar/DNS provider unless you choose otherwise
-- Manual steps still exist for:
- - subdomain delegation in the parent DNS zone
- - SES production-access approval
+- The parent domain can remain at its existing registrar or DNS provider
+- SES still requires normal production-access and domain-verification steps in AWS
## Current Architectural Tradeoffs
-- The repository is intentionally optimized for learning and demonstration breadth, not minimalism
-- Infrastructure is split between app-local starter assets and new root-level shared Terraform
-- Some providers are fully integrated in local/test mode but still require real-account validation before they can be called “finished”
+- The repository is intentionally broad and demonstrates system thinking over minimalism
+- Background processing is intentionally split by responsibility:
+ - Lambda handles scheduled cost sync
+ - ECS worker handles alert evaluation
+- Some cloud integrations are fully wired but still benefit from live-account validation before they should be considered fully hardened
## What A Reviewer Should Notice
- The project goes beyond UI work and touches auth, DB migrations, cloud integrations, background jobs, testing, and infrastructure
-- Production-style concerns are visible even where the repository is still under active development
-- The implementation is broad enough to demonstrate system thinking, not just isolated feature work
+- Production concerns are visible in deployment, session handling, logging, and cloud access patterns
+- The implementation is broad enough to demonstrate end-to-end engineering ownership, not just isolated feature work
diff --git a/docs/production-deployment.md b/docs/production-deployment.md
index efedef6..fbd7a34 100644
--- a/docs/production-deployment.md
+++ b/docs/production-deployment.md
@@ -17,6 +17,8 @@ This document is the first-deployment guide for running Underflow on AWS with:
- one API service
- one worker service
- one migration task definition used during deploy
+- EventBridge + Lambda
+ - one scheduled cost sync Lambda triggered every 6 hours
- Application Load Balancer
- terminates TLS for `api.underflow.[yourdomain].com`
- forwards `/api/v1/*` traffic to the API ECS service
@@ -67,17 +69,26 @@ cd infra\terraform\envs\production
terraform init -backend-config=backend.hcl
```
+Before running `terraform plan` or `terraform apply` locally, build the scheduled sync Lambda artifact:
+
+```powershell
+cd apps\api
+npm install
+npm run build:lambda
+```
+
### 4. Run the first production apply
The deploy workflow is designed to own the ongoing rollout, but it is still useful to understand the shape:
-1. bootstrap the ECR repository
-2. build and push the API image
-3. run full Terraform apply with the image URI
-4. run the migration task
-5. wait for ECS services to stabilize
-6. sync the frontend build to S3
-7. invalidate CloudFront
+1. build the scheduled sync Lambda artifact
+2. bootstrap the ECR repository
+3. build and push the API image
+4. run full Terraform apply with the image URI and Lambda artifact
+5. run the migration task
+6. wait for ECS services to stabilize
+7. sync the frontend build to S3
+8. invalidate CloudFront
## GitHub Environment Variables
@@ -97,6 +108,7 @@ Add these as `production` environment variables in GitHub:
- `AUTH_COOKIE_DOMAIN`
- `AUTH_COOKIE_SAME_SITE`
- `COST_SYNC_LOOKBACK_DAYS`
+- `SCHEDULED_SYNC_INTERVAL_HOURS`
- `AWS_SES_REGION`
- `EMAIL_PROVIDER`
- `BILLING_ENABLED`
@@ -136,6 +148,7 @@ Add these as `production` environment secrets in GitHub:
### `deploy-production.yml`
+- builds the scheduled sync Lambda artifact from the API codebase
- initializes production Terraform
- ensures the ECR repo exists
- builds and pushes the current API image
@@ -167,6 +180,14 @@ The ECS task runtime role is responsible for:
For the split-domain setup, prefer leaving `AUTH_COOKIE_DOMAIN` empty so auth cookies remain host-only on `api.underflow.[yourdomain].com`.
+### Scheduled cost sync Lambda
+
+- runs every 6 hours through EventBridge
+- uses the same sync service path as manual account syncs
+- writes visible execution history through existing `cost_sync_runs` rows
+- emits invocation-level logs to CloudWatch
+- syncs all verified AWS accounts while relying on advisory locks to avoid duplicate per-account work
+
### Web
Production web builds should use:
diff --git a/docs/production-operations.md b/docs/production-operations.md
index 822fefe..12c5f24 100644
--- a/docs/production-operations.md
+++ b/docs/production-operations.md
@@ -8,8 +8,11 @@ This document is the minimum viable runbook for operating Underflow with a first
- stateless HTTP API
- should run with a runtime execution role, not long-lived production access keys
- `apps/api` worker process
- - runs scheduled sync and alert evaluation jobs
+ - runs alert evaluation jobs
- should use the same AWS/runtime identity and database as the API
+- scheduled cost sync Lambda
+ - runs periodic verified-account sync every 6 hours
+ - writes invocation logs to CloudWatch and sync history through existing DB tables
- `apps/web`
- static frontend served separately from the API
- PostgreSQL
@@ -48,14 +51,16 @@ Production defaults and expectations:
2. Deploy the API.
3. Run API migrations once against the target database.
4. Deploy or restart the worker process.
-5. Deploy the frontend.
-6. Verify health and a basic authenticated page load.
+5. Deploy the scheduled cost sync Lambda and schedule.
+6. Deploy the frontend.
+7. Verify health and a basic authenticated page load.
### Roll back
1. Roll back the API and worker to the last known good image/build.
-2. Roll back the frontend if the issue is client-visible.
-3. If a migration caused the issue, stop and restore from backup rather than improvising destructive SQL.
+2. Roll back the scheduled cost sync Lambda to the last known good artifact if needed.
+3. Roll back the frontend if the issue is client-visible.
+4. If a migration caused the issue, stop and restore from backup rather than improvising destructive SQL.
## Health And Readiness
@@ -63,13 +68,14 @@ Production defaults and expectations:
- treat API readiness as:
- database reachable
- migrations already applied
- - worker running separately for scheduled sync and alert evaluation
+ - worker running separately for alert evaluation
+ - scheduled sync Lambda invocations succeeding on schedule
## Observability Baseline
-Underflow already emits structured JSON logs to stdout/stderr. In production:
+Underflow already emits structured JSON logs to stdout/stderr, and the scheduled sync Lambda emits structured logs to CloudWatch. In production:
-- aggregate API and worker logs centrally
+- aggregate API, worker, and Lambda logs centrally
- build dashboards/alerts around:
- AWS verification failures
- AWS sync failures
diff --git a/infra/terraform/envs/production/main.tf b/infra/terraform/envs/production/main.tf
index ea85377..489426a 100644
--- a/infra/terraform/envs/production/main.tf
+++ b/infra/terraform/envs/production/main.tf
@@ -39,6 +39,8 @@ module "platform" {
api_memory = var.api_memory
worker_cpu = var.worker_cpu
worker_memory = var.worker_memory
+ scheduled_sync_interval_hours = var.scheduled_sync_interval_hours
+ scheduled_sync_lambda_source_dir = abspath("${path.root}/../../../../apps/api/build/lambda")
db_instance_class = var.db_instance_class
db_name = var.db_name
db_username = var.db_username
diff --git a/infra/terraform/envs/production/outputs.tf b/infra/terraform/envs/production/outputs.tf
index 4437a40..a7f1802 100644
--- a/infra/terraform/envs/production/outputs.tf
+++ b/infra/terraform/envs/production/outputs.tf
@@ -28,6 +28,11 @@ output "worker_service_name" {
value = module.platform.worker_service_name
}
+output "scheduled_cost_sync_lambda_name" {
+ description = "Scheduled cost sync Lambda function name."
+ value = module.platform.scheduled_cost_sync_lambda_name
+}
+
output "api_task_definition_arn" {
description = "API task definition ARN."
value = module.platform.api_task_definition_arn
diff --git a/infra/terraform/envs/production/response.json b/infra/terraform/envs/production/response.json
new file mode 100644
index 0000000..08841e4
--- /dev/null
+++ b/infra/terraform/envs/production/response.json
@@ -0,0 +1 @@
+{"errorType":"Error","errorMessage":"Missing required environment variable: CSRF_SECRET","trace":["Error: Missing required environment variable: CSRF_SECRET"," at getRequiredEnv (file:///var/task/dist/config/env.js:7:15)"," at file:///var/task/dist/config/env.js:59:18"," at ModuleJob.run (node:internal/modules/esm/module_job:343:25)"," at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)"," at async _tryAwaitImport (file:///var/runtime/index.mjs:1098:16)"," at async _tryRequire (file:///var/runtime/index.mjs:1156:37)"," at async _loadUserApp (file:///var/runtime/index.mjs:1186:16)"," at async UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1235:21)"," at async start (file:///var/runtime/index.mjs:1454:23)"," at async file:///var/runtime/index.mjs:1464:1"]}
\ No newline at end of file
diff --git a/infra/terraform/envs/production/terraform.tfvars.example b/infra/terraform/envs/production/terraform.tfvars.example
index 55a2679..5b13a94 100644
--- a/infra/terraform/envs/production/terraform.tfvars.example
+++ b/infra/terraform/envs/production/terraform.tfvars.example
@@ -5,6 +5,7 @@ route53_zone_id = "Z1234567890ABC"
web_bucket_name = "underflow-example-com-web"
api_image_uri = "123456789012.dkr.ecr.us-west-2.amazonaws.com/underflow-prod-app:latest"
worker_image_uri = ""
+scheduled_sync_interval_hours = 6
db_name = "underflow"
db_username = "underflow"
diff --git a/infra/terraform/envs/production/variables.tf b/infra/terraform/envs/production/variables.tf
index 6980126..39be13a 100644
--- a/infra/terraform/envs/production/variables.tf
+++ b/infra/terraform/envs/production/variables.tf
@@ -72,6 +72,12 @@ variable "worker_memory" {
default = 1024
}
+variable "scheduled_sync_interval_hours" {
+ description = "Interval in hours for the scheduled AWS cost sync Lambda."
+ type = number
+ default = 6
+}
+
variable "db_instance_class" {
description = "RDS instance class."
type = string
diff --git a/infra/terraform/modules/platform_stack/iam.tf b/infra/terraform/modules/platform_stack/iam.tf
index eaf2235..5bbeecc 100644
--- a/infra/terraform/modules/platform_stack/iam.tf
+++ b/infra/terraform/modules/platform_stack/iam.tf
@@ -9,6 +9,17 @@ data "aws_iam_policy_document" "task_execution_assume_role" {
}
}
+data "aws_iam_policy_document" "lambda_execution_assume_role" {
+ statement {
+ actions = ["sts:AssumeRole"]
+
+ principals {
+ type = "Service"
+ identifiers = ["lambda.amazonaws.com"]
+ }
+ }
+}
+
resource "aws_iam_role" "task_execution" {
name = "${var.name_prefix}-task-execution"
assume_role_policy = data.aws_iam_policy_document.task_execution_assume_role.json
@@ -66,3 +77,48 @@ resource "aws_iam_role_policy" "task_runtime" {
role = aws_iam_role.task_runtime.id
policy = data.aws_iam_policy_document.task_runtime.json
}
+
+resource "aws_iam_role" "scheduled_sync_lambda" {
+ name = "${var.name_prefix}-scheduled-sync-lambda"
+ assume_role_policy = data.aws_iam_policy_document.lambda_execution_assume_role.json
+ tags = local.tags
+}
+
+resource "aws_iam_role_policy_attachment" "scheduled_sync_lambda_basic" {
+ role = aws_iam_role.scheduled_sync_lambda.name
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
+}
+
+resource "aws_iam_role_policy_attachment" "scheduled_sync_lambda_vpc" {
+ role = aws_iam_role.scheduled_sync_lambda.name
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
+}
+
+data "aws_iam_policy_document" "scheduled_sync_lambda" {
+ statement {
+ sid = "AllowAssumeCustomerCostRoles"
+ effect = "Allow"
+ actions = [
+ "sts:AssumeRole",
+ ]
+ resources = [
+ "arn:aws:iam::*:role/UnderflowCostExplorerRead",
+ ]
+ }
+
+ statement {
+ sid = "AllowSesEmailSend"
+ effect = "Allow"
+ actions = [
+ "ses:SendEmail",
+ "ses:SendRawEmail",
+ ]
+ resources = ["*"]
+ }
+}
+
+resource "aws_iam_role_policy" "scheduled_sync_lambda" {
+ name = "${var.name_prefix}-scheduled-sync-lambda"
+ role = aws_iam_role.scheduled_sync_lambda.id
+ policy = data.aws_iam_policy_document.scheduled_sync_lambda.json
+}
diff --git a/infra/terraform/modules/platform_stack/lambda.tf b/infra/terraform/modules/platform_stack/lambda.tf
new file mode 100644
index 0000000..d17bdf2
--- /dev/null
+++ b/infra/terraform/modules/platform_stack/lambda.tf
@@ -0,0 +1,60 @@
+data "archive_file" "scheduled_cost_sync" {
+ type = "zip"
+ source_dir = var.scheduled_sync_lambda_source_dir
+ output_path = "${path.root}/.terraform/${var.name_prefix}-scheduled-cost-sync.zip"
+}
+
+resource "aws_cloudwatch_log_group" "scheduled_cost_sync_lambda" {
+ name = "/aws/lambda/${var.name_prefix}-scheduled-cost-sync"
+ retention_in_days = 30
+ tags = local.tags
+}
+
+resource "aws_lambda_function" "scheduled_cost_sync" {
+ function_name = "${var.name_prefix}-scheduled-cost-sync"
+ role = aws_iam_role.scheduled_sync_lambda.arn
+ runtime = "nodejs22.x"
+ handler = "dist/jobs/scheduled-cost-sync.lambda.handler"
+ filename = data.archive_file.scheduled_cost_sync.output_path
+ source_code_hash = data.archive_file.scheduled_cost_sync.output_base64sha256
+ timeout = 900
+ memory_size = 512
+
+ vpc_config {
+ subnet_ids = values(aws_subnet.private)[*].id
+ security_group_ids = [aws_security_group.ecs.id]
+ }
+
+ environment {
+ variables = {
+ for item in local.lambda_environment : item.name => item.value
+ }
+ }
+
+ depends_on = [
+ aws_cloudwatch_log_group.scheduled_cost_sync_lambda,
+ ]
+
+ tags = local.tags
+}
+
+resource "aws_cloudwatch_event_rule" "scheduled_cost_sync" {
+ name = "${var.name_prefix}-scheduled-cost-sync"
+ description = "Runs verified AWS account cost sync on a fixed schedule."
+ schedule_expression = local.scheduled_sync_schedule_expression
+ tags = local.tags
+}
+
+resource "aws_cloudwatch_event_target" "scheduled_cost_sync" {
+ rule = aws_cloudwatch_event_rule.scheduled_cost_sync.name
+ target_id = "scheduled-cost-sync-lambda"
+ arn = aws_lambda_function.scheduled_cost_sync.arn
+}
+
+resource "aws_lambda_permission" "scheduled_cost_sync_eventbridge" {
+ statement_id = "AllowExecutionFromEventBridgeScheduledSync"
+ action = "lambda:InvokeFunction"
+ function_name = aws_lambda_function.scheduled_cost_sync.function_name
+ principal = "events.amazonaws.com"
+ source_arn = aws_cloudwatch_event_rule.scheduled_cost_sync.arn
+}
diff --git a/infra/terraform/modules/platform_stack/locals.tf b/infra/terraform/modules/platform_stack/locals.tf
index 17c870f..5adbbe8 100644
--- a/infra/terraform/modules/platform_stack/locals.tf
+++ b/infra/terraform/modules/platform_stack/locals.tf
@@ -5,8 +5,9 @@ data "aws_availability_zones" "available" {
}
locals {
- worker_image = var.worker_image_uri != "" ? var.worker_image_uri : var.api_image_uri
- api_domain = "api.${var.app_domain_name}"
+ worker_image = var.worker_image_uri != "" ? var.worker_image_uri : var.api_image_uri
+ api_domain = "api.${var.app_domain_name}"
+ scheduled_sync_schedule_expression = "rate(${var.scheduled_sync_interval_hours} hours)"
tags = merge(
{
@@ -68,4 +69,18 @@ locals {
{ name = "RATE_LIMIT_MUTATION_WINDOW_MS", value = tostring(var.rate_limit_mutation_window_ms) },
{ name = "RATE_LIMIT_MUTATION_MAX_REQUESTS", value = tostring(var.rate_limit_mutation_max_requests) },
]
+
+ lambda_environment_names = toset([
+ "NODE_ENV",
+ "DATABASE_URL",
+ "DATABASE_SSL_ENABLED",
+ "DATABASE_SSL_REJECT_UNAUTHORIZED",
+ "AWS_SES_REGION",
+ "COST_SYNC_LOOKBACK_DAYS",
+ "LOG_LEVEL",
+ ])
+
+ lambda_environment = [
+ for item in local.app_environment : item if contains(local.lambda_environment_names, item.name)
+ ]
}
diff --git a/infra/terraform/modules/platform_stack/outputs.tf b/infra/terraform/modules/platform_stack/outputs.tf
index 062ac0e..f1b090f 100644
--- a/infra/terraform/modules/platform_stack/outputs.tf
+++ b/infra/terraform/modules/platform_stack/outputs.tf
@@ -28,6 +28,11 @@ output "worker_service_name" {
value = aws_ecs_service.worker.name
}
+output "scheduled_cost_sync_lambda_name" {
+ description = "Scheduled cost sync Lambda function name."
+ value = aws_lambda_function.scheduled_cost_sync.function_name
+}
+
output "api_task_definition_arn" {
description = "API task definition ARN."
value = aws_ecs_task_definition.api.arn
diff --git a/infra/terraform/modules/platform_stack/variables.tf b/infra/terraform/modules/platform_stack/variables.tf
index 89c7b9c..1419334 100644
--- a/infra/terraform/modules/platform_stack/variables.tf
+++ b/infra/terraform/modules/platform_stack/variables.tf
@@ -71,6 +71,17 @@ variable "worker_memory" {
default = 1024
}
+variable "scheduled_sync_interval_hours" {
+ description = "Interval in hours for the scheduled AWS cost sync Lambda."
+ type = number
+ default = 6
+}
+
+variable "scheduled_sync_lambda_source_dir" {
+ description = "Absolute path to the prepared scheduled cost sync Lambda source directory."
+ type = string
+}
+
variable "db_instance_class" {
description = "RDS instance class."
type = string
diff --git a/infra/terraform/modules/platform_stack/versions.tf b/infra/terraform/modules/platform_stack/versions.tf
index c4fbc14..eda4532 100644
--- a/infra/terraform/modules/platform_stack/versions.tf
+++ b/infra/terraform/modules/platform_stack/versions.tf
@@ -1,5 +1,9 @@
terraform {
required_providers {
+ archive = {
+ source = "hashicorp/archive"
+ }
+
aws = {
source = "hashicorp/aws"
configuration_aliases = [