From 7afcc065fbe6047f2951af3536cabbabd5b5b724 Mon Sep 17 00:00:00 2001 From: Aidan Kelly Date: Tue, 24 Feb 2026 19:38:07 -0500 Subject: [PATCH 1/4] Twilio SMS working before number verification --- server/package-lock.json | 1230 +++++++++++++---- server/package.json | 4 +- server/scripts/create-user.ts | 4 +- .../src/data/repository/message-blast-repo.ts | 34 +- server/src/service/twilio-service.ts | 170 +++ server/src/utils/aws-sns.ts | 145 ++ 6 files changed, 1282 insertions(+), 305 deletions(-) create mode 100644 server/src/service/twilio-service.ts create mode 100644 server/src/utils/aws-sns.ts diff --git a/server/package-lock.json b/server/package-lock.json index 89488e5b..e774c58e 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -12,6 +12,7 @@ "@aws-sdk/client-bedrock-runtime": "^3.938.0", "@aws-sdk/client-s3": "^3.937.0", "@aws-sdk/client-secrets-manager": "^3.936.0", + "@aws-sdk/client-sns": "^3.996.0", "@aws-sdk/s3-request-presigner": "^3.937.0", "@trpc/client": "^11.6.0", "@trpc/server": "^11.6.0", @@ -27,6 +28,7 @@ "swagger-ui-express": "^5.0.1", "trpc-to-openapi": "^3.1.0", "tsx": "^4.20.6", + "twilio": "^5.12.2", "web-push": "^3.6.7", "zod": "^4.1.13" }, @@ -34,7 +36,7 @@ "@biomejs/biome": "2.2.5", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", - "@types/node": "^24.7.0", + "@types/node": "^24.10.13", "@types/pg": "^8.15.5", "@types/swagger-ui-express": "^4.1.8", "@types/web-push": "^3.6.4", @@ -420,45 +422,111 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/client-sns": { + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sns/-/client-sns-3.996.0.tgz", + "integrity": "sha512-yhNov6mSPc3QAACYAd5IggqzyqwWNiSaiCXBF678jnHv53v7FUwfAfKd4hEIxunq7lOf69c3+nbuzDjzlyeOiA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.12", + "@aws-sdk/credential-provider-node": "^3.972.11", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.12", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.996.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.11", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.23.2", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.16", + "@smithy/middleware-retry": "^4.4.33", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.10", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.5", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.32", + "@smithy/util-defaults-mode-node": "^4.2.35", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sns/node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.0.tgz", + "integrity": "sha512-EhSBGWSGQ6Jcbt6jRyX1/0EV7rf+6RGbIIskN0MTtHk0k8uj5FAa1FZhLf+1ETfnDTy/BT39t5IUOQiZL5X1jQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/client-sso": { - "version": "3.988.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.988.0.tgz", - "integrity": "sha512-ThqQ7aF1k0Zz4yJRwegHw+T1rM3a7ZPvvEUSEdvn5Z8zTeWgJAbtqW/6ejPsMLmFOlHgNcwDQN/e69OvtEOoIQ==", + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.996.0.tgz", + "integrity": "sha512-QzlZozTam0modnGanLjXBHbHC53mMxH/4XmoA9f6ZjPYaGlCcHPYLcslO6w2w68v+F3qN0kxVldUAcL/edtBBA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.8", + "@aws-sdk/core": "^3.973.12", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", - "@aws-sdk/middleware-user-agent": "^3.972.8", + "@aws-sdk/middleware-user-agent": "^3.972.12", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", - "@aws-sdk/util-endpoints": "3.988.0", + "@aws-sdk/util-endpoints": "3.996.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", - "@aws-sdk/util-user-agent-node": "^3.972.6", + "@aws-sdk/util-user-agent-node": "^3.972.11", "@smithy/config-resolver": "^4.4.6", - "@smithy/core": "^3.23.0", + "@smithy/core": "^3.23.2", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", - "@smithy/middleware-endpoint": "^4.4.14", - "@smithy/middleware-retry": "^4.4.31", + "@smithy/middleware-endpoint": "^4.4.16", + "@smithy/middleware-retry": "^4.4.33", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.11.3", + "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.30", - "@smithy/util-defaults-mode-node": "^4.2.33", + "@smithy/util-defaults-mode-browser": "^4.3.32", + "@smithy/util-defaults-mode-node": "^4.2.35", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", @@ -469,20 +537,36 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.0.tgz", + "integrity": "sha512-EhSBGWSGQ6Jcbt6jRyX1/0EV7rf+6RGbIIskN0MTtHk0k8uj5FAa1FZhLf+1ETfnDTy/BT39t5IUOQiZL5X1jQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/core": { - "version": "3.973.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.8.tgz", - "integrity": "sha512-WeYJ2sfvRLbbUIrjGMUXcEHGu5SJk53jz3K9F8vFP42zWyROzPJ2NB6lMu9vWl5hnMwzwabX7pJc9Euh3JyMGw==", + "version": "3.973.12", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.12.tgz", + "integrity": "sha512-hFiezao0lCEddPhSQEF6vCu+TepUN3edKxWYbswMoH87XpUvHJmFVX5+zttj4qi33saGiuOaJciswWcN6YSA9g==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.1", - "@aws-sdk/xml-builder": "^3.972.4", - "@smithy/core": "^3.23.0", + "@aws-sdk/xml-builder": "^3.972.5", + "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", - "@smithy/smithy-client": "^4.11.3", + "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", @@ -507,12 +591,12 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.6.tgz", - "integrity": "sha512-+dYEBWgTqkQQHFUllvBL8SLyXyLKWdxLMD1LmKJRvmb0NMJuaJFG/qg78C+LE67eeGbipYcE+gJ48VlLBGHlMw==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.10.tgz", + "integrity": "sha512-YTWjM78Wiqix0Jv/anbq7+COFOFIBBMLZ+JsLKGwbTZNJ2DG4JNBnLVJAWylPOHwurMws9157pqzU8ODrpBOow==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.8", + "@aws-sdk/core": "^3.973.12", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", @@ -523,18 +607,18 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.8.tgz", - "integrity": "sha512-z3QkozMV8kOFisN2pgRag/f0zPDrw96mY+ejAM0xssV/+YQ2kklbylRNI/TcTQUDnGg0yPxNjyV6F2EM2zPTwg==", + "version": "3.972.12", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.12.tgz", + "integrity": "sha512-adDRE3iFrgJJ7XhRHkb6RdFDMrA5x64WAWxygI3F6wND+3v5qQ4Uks12vsnEZgduU/+JQBgFB6L4vfwUS+rpBQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.8", + "@aws-sdk/core": "^3.973.12", "@aws-sdk/types": "^3.973.1", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/node-http-handler": "^4.4.10", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.11.3", + "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.12", "tslib": "^2.6.2" @@ -544,19 +628,19 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.972.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.6.tgz", - "integrity": "sha512-6tkIYFv3sZH1XsjQq+veOmx8XWRnyqTZ5zx/sMtdu/xFRIzrJM1Y2wAXeCJL1rhYSB7uJSZ1PgALI2WVTj78ow==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.10.tgz", + "integrity": "sha512-uAXUMfnQJxJ25qeiX4e3Z36NTm1XT7woajV8BXx2yAUDD4jF6kubqnLEcqtiPzHANxmhta2SXm5PbDwSdhThBw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.8", - "@aws-sdk/credential-provider-env": "^3.972.6", - "@aws-sdk/credential-provider-http": "^3.972.8", - "@aws-sdk/credential-provider-login": "^3.972.6", - "@aws-sdk/credential-provider-process": "^3.972.6", - "@aws-sdk/credential-provider-sso": "^3.972.6", - "@aws-sdk/credential-provider-web-identity": "^3.972.6", - "@aws-sdk/nested-clients": "3.988.0", + "@aws-sdk/core": "^3.973.12", + "@aws-sdk/credential-provider-env": "^3.972.10", + "@aws-sdk/credential-provider-http": "^3.972.12", + "@aws-sdk/credential-provider-login": "^3.972.10", + "@aws-sdk/credential-provider-process": "^3.972.10", + "@aws-sdk/credential-provider-sso": "^3.972.10", + "@aws-sdk/credential-provider-web-identity": "^3.972.10", + "@aws-sdk/nested-clients": "3.996.0", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", @@ -568,14 +652,79 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/nested-clients": { + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.0.tgz", + "integrity": "sha512-edZwYLgRI0rZlH9Hru9+JvTsR1OAxuCRGEtJohkZneIJ5JIYzvFoMR1gaASjl1aPKRhjkCv8SSAb7hes5a1GGA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.12", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.12", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.996.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.11", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.23.2", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.16", + "@smithy/middleware-retry": "^4.4.33", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.10", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.5", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.32", + "@smithy/util-defaults-mode-node": "^4.2.35", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.0.tgz", + "integrity": "sha512-EhSBGWSGQ6Jcbt6jRyX1/0EV7rf+6RGbIIskN0MTtHk0k8uj5FAa1FZhLf+1ETfnDTy/BT39t5IUOQiZL5X1jQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.6.tgz", - "integrity": "sha512-LXsoBoaTSGHdRCQXlWSA0CHHh05KWncb592h9ElklnPus++8kYn1Ic6acBR4LKFQ0RjjMVgwe5ypUpmTSUOjPA==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.10.tgz", + "integrity": "sha512-7Me+/EkY3kQC1nehBjb9ryc558N+a8R4Dg3rSV3zpiB7iQtvXh4gU3rV14h/dIbn2/VkK9sh55YdXamSjfdb/Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.8", - "@aws-sdk/nested-clients": "3.988.0", + "@aws-sdk/core": "^3.973.12", + "@aws-sdk/nested-clients": "3.996.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", @@ -587,18 +736,83 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/nested-clients": { + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.0.tgz", + "integrity": "sha512-edZwYLgRI0rZlH9Hru9+JvTsR1OAxuCRGEtJohkZneIJ5JIYzvFoMR1gaASjl1aPKRhjkCv8SSAb7hes5a1GGA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.12", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.12", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.996.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.11", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.23.2", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.16", + "@smithy/middleware-retry": "^4.4.33", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.10", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.5", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.32", + "@smithy/util-defaults-mode-node": "^4.2.35", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.0.tgz", + "integrity": "sha512-EhSBGWSGQ6Jcbt6jRyX1/0EV7rf+6RGbIIskN0MTtHk0k8uj5FAa1FZhLf+1ETfnDTy/BT39t5IUOQiZL5X1jQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.7.tgz", - "integrity": "sha512-PuJ1IkISG7ZDpBFYpGotaay6dYtmriBYuHJ/Oko4VHxh8YN5vfoWnMNYFEWuzOfyLmP7o9kDVW0BlYIpb3skvw==", + "version": "3.972.11", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.11.tgz", + "integrity": "sha512-maPmjL7nOT93a1QdSDzdF/qLbI+jit3oslKp7g+pTbASewkSYax7FwboETdKRxufPfCdrsRzMW2pIJ+QA8e+Bg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.6", - "@aws-sdk/credential-provider-http": "^3.972.8", - "@aws-sdk/credential-provider-ini": "^3.972.6", - "@aws-sdk/credential-provider-process": "^3.972.6", - "@aws-sdk/credential-provider-sso": "^3.972.6", - "@aws-sdk/credential-provider-web-identity": "^3.972.6", + "@aws-sdk/credential-provider-env": "^3.972.10", + "@aws-sdk/credential-provider-http": "^3.972.12", + "@aws-sdk/credential-provider-ini": "^3.972.10", + "@aws-sdk/credential-provider-process": "^3.972.10", + "@aws-sdk/credential-provider-sso": "^3.972.10", + "@aws-sdk/credential-provider-web-identity": "^3.972.10", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", @@ -611,12 +825,12 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.6.tgz", - "integrity": "sha512-Yf34cjIZJHVnD92jnVYy3tNjM+Q4WJtffLK2Ehn0nKpZfqd1m7SI0ra22Lym4C53ED76oZENVSS2wimoXJtChQ==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.10.tgz", + "integrity": "sha512-tk/XxFhk37rKviArOIYbJ8crXiN3Mzn7Tb147jH51JTweNgUOwmqN+s027uqc3d8UeAyUcPUH8Bmfj86SzOhBQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.8", + "@aws-sdk/core": "^3.973.12", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", @@ -628,14 +842,81 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.972.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.6.tgz", - "integrity": "sha512-2+5UVwUYdD4BBOkLpKJ11MQ8wQeyJGDVMDRH5eWOULAh9d6HJq07R69M/mNNMC9NTjr3mB1T0KGDn4qyQh5jzg==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.10.tgz", + "integrity": "sha512-tIz/O0yV1s77/FjMTWvvzU2vsztap2POlbetheOyRXq+E3PQtLOzCYopasXP+aeO1oerw3PFd9eycLbiwpgZZA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.988.0", - "@aws-sdk/core": "^3.973.8", - "@aws-sdk/token-providers": "3.988.0", + "@aws-sdk/client-sso": "3.996.0", + "@aws-sdk/core": "^3.973.12", + "@aws-sdk/token-providers": "3.996.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/nested-clients": { + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.0.tgz", + "integrity": "sha512-edZwYLgRI0rZlH9Hru9+JvTsR1OAxuCRGEtJohkZneIJ5JIYzvFoMR1gaASjl1aPKRhjkCv8SSAb7hes5a1GGA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.12", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.12", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.996.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.11", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.23.2", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.16", + "@smithy/middleware-retry": "^4.4.33", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.10", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.5", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.32", + "@smithy/util-defaults-mode-node": "^4.2.35", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.996.0.tgz", + "integrity": "sha512-jzBmlG97hYPdHjFs7G11fBgVArcwUrZX+SbGeQMph7teEWLDqIruKV+N0uzxFJF2GJJJ0UnMaKhv3PcXMltySg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.12", + "@aws-sdk/nested-clients": "3.996.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", @@ -646,14 +927,30 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.0.tgz", + "integrity": "sha512-EhSBGWSGQ6Jcbt6jRyX1/0EV7rf+6RGbIIskN0MTtHk0k8uj5FAa1FZhLf+1ETfnDTy/BT39t5IUOQiZL5X1jQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.6.tgz", - "integrity": "sha512-pdJzwKtlDxBnvZ04pWMqttijmkUIlwOsS0GcxCjzEVyUMpARysl0S0ks74+gs2Pdev3Ujz+BTAjOc1tQgAxGqA==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.10.tgz", + "integrity": "sha512-HFlIVx8mm+Au7hkO7Hq/ZkPomjTt26iRj8uWZqEE1cJWMZ2NKvieNiT1ngzWt60Bc2uD51LqQUqiwr5JDgS4iQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.8", - "@aws-sdk/nested-clients": "3.988.0", + "@aws-sdk/core": "^3.973.12", + "@aws-sdk/nested-clients": "3.996.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", @@ -664,6 +961,71 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/nested-clients": { + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.0.tgz", + "integrity": "sha512-edZwYLgRI0rZlH9Hru9+JvTsR1OAxuCRGEtJohkZneIJ5JIYzvFoMR1gaASjl1aPKRhjkCv8SSAb7hes5a1GGA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.12", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.12", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.996.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.11", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.23.2", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.16", + "@smithy/middleware-retry": "^4.4.33", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.10", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.5", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.32", + "@smithy/util-defaults-mode-node": "^4.2.35", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.0.tgz", + "integrity": "sha512-EhSBGWSGQ6Jcbt6jRyX1/0EV7rf+6RGbIIskN0MTtHk0k8uj5FAa1FZhLf+1ETfnDTy/BT39t5IUOQiZL5X1jQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/eventstream-handler-node": { "version": "3.972.5", "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.5.tgz", @@ -851,15 +1213,15 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.8.tgz", - "integrity": "sha512-3PGL+Kvh1PhB0EeJeqNqOWQgipdqFheO4OUKc6aYiFwEpM5t9AyE5hjjxZ5X6iSj8JiduWFZLPwASzF6wQRgFg==", + "version": "3.972.12", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.12.tgz", + "integrity": "sha512-iv9toQZloEJp+dIuOr+1XWGmBMLU9c2qqNtgscfnEBZnUq3qKdBJHmLTKoq3mkLlV+41GrCWn8LrOunc6OlP6g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.8", + "@aws-sdk/core": "^3.973.12", "@aws-sdk/types": "^3.973.1", - "@aws-sdk/util-endpoints": "3.988.0", - "@smithy/core": "^3.23.0", + "@aws-sdk/util-endpoints": "3.996.0", + "@smithy/core": "^3.23.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" @@ -868,6 +1230,22 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/middleware-user-agent/node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.0.tgz", + "integrity": "sha512-EhSBGWSGQ6Jcbt6jRyX1/0EV7rf+6RGbIIskN0MTtHk0k8uj5FAa1FZhLf+1ETfnDTy/BT39t5IUOQiZL5X1jQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/middleware-websocket": { "version": "3.972.6", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.6.tgz", @@ -1091,12 +1469,12 @@ } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.972.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.6.tgz", - "integrity": "sha512-966xH8TPqkqOXP7EwnEThcKKz0SNP9kVJBKd9M8bNXE4GSqVouMKKnFBwYnzbWVKuLXubzX5seokcX4a0JLJIA==", + "version": "3.972.11", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.11.tgz", + "integrity": "sha512-pQr35pSZANfUb0mJ9H87pziJQ39jW1D7xFRwh36eWfrEclbKoIqrzpOIVz49o1Jq9ZQzOtjS7rQVvt7V4w5awA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "^3.972.8", + "@aws-sdk/middleware-user-agent": "^3.972.12", "@aws-sdk/types": "^3.973.1", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", @@ -1115,13 +1493,13 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.4.tgz", - "integrity": "sha512-0zJ05ANfYqI6+rGqj8samZBFod0dPPousBjLEqg8WdxSgbMAkRgLyn81lP215Do0rFJ/17LIXwr7q0yK24mP6Q==", + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.5.tgz", + "integrity": "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.12.0", - "fast-xml-parser": "5.3.4", + "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" }, "engines": { @@ -2200,12 +2578,12 @@ "license": "Apache-2.0" }, "node_modules/@smithy/abort-controller": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.8.tgz", - "integrity": "sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.9.tgz", + "integrity": "sha512-6YGSygFmck1vMjzSxbjEPKMm1xWUr2+w+F8kWVc8rqKQYd1C5zZftvxGii4ti4Mh5ulIXZtAUoXS88Hhu6fkjQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2238,37 +2616,37 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.6.tgz", - "integrity": "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==", + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.7.tgz", + "integrity": "sha512-RISbtc12JKdFRYadt2kW12Cp6XCSU00uFaBZPZqInNVSrRdJFPY/S6nd6/sV7+ySTgGPiKrERtnimEFI6sSweQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-endpoints": "^3.2.8", - "@smithy/util-middleware": "^4.2.8", + "@smithy/node-config-provider": "^4.3.9", + "@smithy/types": "^4.12.1", + "@smithy/util-config-provider": "^4.2.1", + "@smithy/util-endpoints": "^3.2.9", + "@smithy/util-middleware": "^4.2.9", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@smithy/core": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.0.tgz", - "integrity": "sha512-Yq4UPVoQICM9zHnByLmG8632t2M0+yap4T7ANVw482J0W7HW0pOuxwVmeOwzJqX2Q89fkXz0Vybz55Wj2Xzrsg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^4.2.9", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-stream": "^4.5.12", - "@smithy/util-utf8": "^4.2.0", - "@smithy/uuid": "^1.1.0", + "node_modules/@smithy/core": { + "version": "3.23.4", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.4.tgz", + "integrity": "sha512-IH7G3hWxUhd2Z6HtvjZ1EiyDBCRYRr2sngOB9KUWf96XQ8JP2O5ascUH6TouW5YCIMFaVnKADEscM/vUfI3TvA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.2.10", + "@smithy/protocol-http": "^5.3.9", + "@smithy/types": "^4.12.1", + "@smithy/util-base64": "^4.3.1", + "@smithy/util-body-length-browser": "^4.2.1", + "@smithy/util-middleware": "^4.2.9", + "@smithy/util-stream": "^4.5.14", + "@smithy/util-utf8": "^4.2.1", + "@smithy/uuid": "^1.1.1", "tslib": "^2.6.2" }, "engines": { @@ -2276,15 +2654,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz", - "integrity": "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.9.tgz", + "integrity": "sha512-Jf723a38EGAzWHxJHzb9DtBq7lrvdJlkCAPWQdN/oiznovx5yWXCFCVspzDe8JU6b+k9hJXYB5duFZpb+3mB6Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", + "@smithy/node-config-provider": "^4.3.9", + "@smithy/property-provider": "^4.2.9", + "@smithy/types": "^4.12.1", + "@smithy/url-parser": "^4.2.9", "tslib": "^2.6.2" }, "engines": { @@ -2362,15 +2740,15 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz", - "integrity": "sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.10.tgz", + "integrity": "sha512-qF4EcrEtEf2P6f2kGGuSVe1lan26cn7PsWJBC3vZJ6D16Fm5FSN06udOMVoW6hjzQM3W7VDFwtyUG2szQY50dA==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.8", - "@smithy/querystring-builder": "^4.2.8", - "@smithy/types": "^4.12.0", - "@smithy/util-base64": "^4.3.0", + "@smithy/protocol-http": "^5.3.9", + "@smithy/querystring-builder": "^4.2.9", + "@smithy/types": "^4.12.1", + "@smithy/util-base64": "^4.3.1", "tslib": "^2.6.2" }, "engines": { @@ -2435,9 +2813,9 @@ } }, "node_modules/@smithy/is-array-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", - "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.1.tgz", + "integrity": "sha512-Yfu664Qbf1B4IYIsYgKoABt010daZjkaCRvdU/sPnZG6TtHOB0md0RjNdLGzxe5UIdn9js4ftPICzmkRa9RJ4Q==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -2475,18 +2853,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.4.14", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.14.tgz", - "integrity": "sha512-FUFNE5KVeaY6U/GL0nzAAHkaCHzXLZcY1EhtQnsAqhD8Du13oPKtMB9/0WK4/LK6a/T5OZ24wPoSShff5iI6Ag==", + "version": "4.4.18", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.18.tgz", + "integrity": "sha512-4OS3TP3IWZysT8KlSG/UwfKdelJmuQ2CqVNfrkjm2Rsm146/DuSTfXiD1ulgWpp9L6lJmPYfWTp7/m4b4dQSdQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.23.0", - "@smithy/middleware-serde": "^4.2.9", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "@smithy/util-middleware": "^4.2.8", + "@smithy/core": "^3.23.4", + "@smithy/middleware-serde": "^4.2.10", + "@smithy/node-config-provider": "^4.3.9", + "@smithy/shared-ini-file-loader": "^4.4.4", + "@smithy/types": "^4.12.1", + "@smithy/url-parser": "^4.2.9", + "@smithy/util-middleware": "^4.2.9", "tslib": "^2.6.2" }, "engines": { @@ -2494,19 +2872,19 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.31", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.31.tgz", - "integrity": "sha512-RXBzLpMkIrxBPe4C8OmEOHvS8aH9RUuCOH++Acb5jZDEblxDjyg6un72X9IcbrGTJoiUwmI7hLypNfuDACypbg==", + "version": "4.4.35", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.35.tgz", + "integrity": "sha512-sz+Th9ofKypOtaboPTcyZtIfCs2LNb84bzxEhPffCElyMorVYDBdeGzxYqSLC6gWaZUqpPSbj5F6TIxYUlSCfQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/service-error-classification": "^4.2.8", - "@smithy/smithy-client": "^4.11.3", - "@smithy/types": "^4.12.0", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-retry": "^4.2.8", - "@smithy/uuid": "^1.1.0", + "@smithy/node-config-provider": "^4.3.9", + "@smithy/protocol-http": "^5.3.9", + "@smithy/service-error-classification": "^4.2.9", + "@smithy/smithy-client": "^4.11.7", + "@smithy/types": "^4.12.1", + "@smithy/util-middleware": "^4.2.9", + "@smithy/util-retry": "^4.2.9", + "@smithy/uuid": "^1.1.1", "tslib": "^2.6.2" }, "engines": { @@ -2514,13 +2892,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz", - "integrity": "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.10.tgz", + "integrity": "sha512-BQsdoi7ma4siJAzD0S6MedNPhiMcTdTLUqEUjrHeT1TJppBKWnwqySg34Oh/uGRhJeBd1sAH2t5tghBvcyD6tw==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", + "@smithy/protocol-http": "^5.3.9", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2528,12 +2906,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz", - "integrity": "sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.9.tgz", + "integrity": "sha512-pid7ksBr7nm0X/3paIlGo9Fh3UK1pQ5yH0007tBmdkVvv+AsBZAOzC2dmLhlzDWKkSB+ZCiiyDArjAW3klkbMg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2541,14 +2919,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz", - "integrity": "sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.9.tgz", + "integrity": "sha512-EjdDTVGnnyJ9y8jXIfkF45UUZs21/Pp8xaMTZySLoC0xI3EhY7jq4co3LQnhh/bB6VVamd9ELpYJWLDw2ANhZA==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", + "@smithy/property-provider": "^4.2.9", + "@smithy/shared-ini-file-loader": "^4.4.4", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2556,15 +2934,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.10.tgz", - "integrity": "sha512-u4YeUwOWRZaHbWaebvrs3UhwQwj+2VNmcVCwXcYTvPIuVyM7Ex1ftAj+fdbG/P4AkBwLq/+SKn+ydOI4ZJE9PA==", + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.11.tgz", + "integrity": "sha512-kQNJFwzYA9y+Fj3h9t1ToXYOJBobwUVEc6/WX45urJXyErgG0WOsres8Se8BAiFCMe8P06OkzRgakv7bQ5S+6Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/querystring-builder": "^4.2.8", - "@smithy/types": "^4.12.0", + "@smithy/abort-controller": "^4.2.9", + "@smithy/protocol-http": "^5.3.9", + "@smithy/querystring-builder": "^4.2.9", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2572,12 +2950,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.8.tgz", - "integrity": "sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.9.tgz", + "integrity": "sha512-ibHwLxq4KlbfueoNxMNrZkG+O7V/5XKrewhDGYn0p9DYKCsdsofuWHKdX3QW4zHlAUfLStqdCUSDi/q/9WSjwA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2585,12 +2963,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.8.tgz", - "integrity": "sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==", + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.9.tgz", + "integrity": "sha512-PRy4yZqsKI3Eab8TLc16Dj2NzC4dnw/8E95+++Jc+wwlkjBpAq3tNLqkLHMmSvDfxKQ+X5PmmCYt+rM/GcMKPA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2598,13 +2976,13 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz", - "integrity": "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.9.tgz", + "integrity": "sha512-/AIDaq0+ehv+QfeyAjCUFShwHIt+FA1IodsV/2AZE5h4PUZcQYv5sjmy9V67UWfsBoTjOPKUFYSRfGoNW9T2UQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", - "@smithy/util-uri-escape": "^4.2.0", + "@smithy/types": "^4.12.1", + "@smithy/util-uri-escape": "^4.2.1", "tslib": "^2.6.2" }, "engines": { @@ -2612,12 +2990,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz", - "integrity": "sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.9.tgz", + "integrity": "sha512-kZ9AHhrYTea3UoklXudEnyA4duy9KAWERC28+ft8y8HIhR3yGsjv1PFTgzMpB+5L4tQKXNTwFbVJMeRK20vpHQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2625,24 +3003,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz", - "integrity": "sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.9.tgz", + "integrity": "sha512-DYYd4xrm9Ozik+ZT4f5ZqSXdzscVHF/tFCzqieIFcLrjRDxWSgRtvtXOohJGoniLfPcBcy5ltR3tp2Lw4/d9ag==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0" + "@smithy/types": "^4.12.1" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz", - "integrity": "sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.4.tgz", + "integrity": "sha512-tA5Cm11BHQCk/67y6VPIWydLh/pMY90jqOEWIr/2VAzTOoDwGpwp0C/AuHBc3/xWSOA5m5PXLN+lIOrsnTm/PQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2669,17 +3047,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.11.3", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.11.3.tgz", - "integrity": "sha512-Q7kY5sDau8OoE6Y9zJoRGgje8P4/UY0WzH8R2ok0PDh+iJ+ZnEKowhjEqYafVcubkbYxQVaqwm3iufktzhprGg==", + "version": "4.11.7", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.11.7.tgz", + "integrity": "sha512-gQP2J3qB/Wmc26gdmB8gA6zq2o2spG5sEU3o7TaTATBJEk29sYGWdEFoGEy91BczSpifTo0DQhVYjZXBEVcrpA==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.23.0", - "@smithy/middleware-endpoint": "^4.4.14", - "@smithy/middleware-stack": "^4.2.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-stream": "^4.5.12", + "@smithy/core": "^3.23.4", + "@smithy/middleware-endpoint": "^4.4.18", + "@smithy/middleware-stack": "^4.2.9", + "@smithy/protocol-http": "^5.3.9", + "@smithy/types": "^4.12.1", + "@smithy/util-stream": "^4.5.14", "tslib": "^2.6.2" }, "engines": { @@ -2687,9 +3065,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.12.0.tgz", - "integrity": "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.12.1.tgz", + "integrity": "sha512-ow30Ze/DD02KH2p0eMyIF2+qJzGyNb0kFrnTRtPpuOkQ4hrgvLdaU4YC6r/K8aOrCML4FH0Cmm0aI4503L1Hwg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -2699,13 +3077,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.8.tgz", - "integrity": "sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.9.tgz", + "integrity": "sha512-gYs8FrnwKoIvL+GyPz6VvweCkrXqHeD+KnOAxB+NFy6mLr4l75lFrn3dZ413DG0K2TvFtN7L43x7r8hyyohYdg==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.2.8", - "@smithy/types": "^4.12.0", + "@smithy/querystring-parser": "^4.2.9", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2713,13 +3091,13 @@ } }, "node_modules/@smithy/util-base64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", - "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.1.tgz", + "integrity": "sha512-BKGuawX4Doq/bI/uEmg+Zyc36rJKWuin3py89PquXBIBqmbnJwBBsmKhdHfNEp0+A4TDgLmT/3MSKZ1SxHcR6w==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/util-buffer-from": "^4.2.1", + "@smithy/util-utf8": "^4.2.1", "tslib": "^2.6.2" }, "engines": { @@ -2727,9 +3105,9 @@ } }, "node_modules/@smithy/util-body-length-browser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", - "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.1.tgz", + "integrity": "sha512-SiJeLiozrAoCrgDBUgsVbmqHmMgg/2bA15AzcbcW+zan7SuyAVHN4xTSbq0GlebAIwlcaX32xacnrG488/J/6g==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -2751,12 +3129,12 @@ } }, "node_modules/@smithy/util-buffer-from": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", - "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.1.tgz", + "integrity": "sha512-/swhmt1qTiVkaejlmMPPDgZhEaWb/HWMGRBheaxwuVkusp/z+ErJyQxO6kaXumOciZSWlmq6Z5mNylCd33X7Ig==", "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.2.0", + "@smithy/is-array-buffer": "^4.2.1", "tslib": "^2.6.2" }, "engines": { @@ -2764,9 +3142,9 @@ } }, "node_modules/@smithy/util-config-provider": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", - "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.1.tgz", + "integrity": "sha512-462id/00U8JWFw6qBuTSWfN5TxOHvDu4WliI97qOIOnuC/g+NDAknTU8eoGXEPlLkRVgWEr03jJBLV4o2FL8+A==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -2776,14 +3154,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.30", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.30.tgz", - "integrity": "sha512-cMni0uVU27zxOiU8TuC8pQLC1pYeZ/xEMxvchSK/ILwleRd1ugobOcIRr5vXtcRqKd4aBLWlpeBoDPJJ91LQng==", + "version": "4.3.34", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.34.tgz", + "integrity": "sha512-m75CH7xaVG8ErlnfXsIBLrgVrApejrvUpohr41CMdeWNcEu/Ouvj9fbNA7oW9Qpr0Awf+BmDRrYx72hEKgY+FQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.8", - "@smithy/smithy-client": "^4.11.3", - "@smithy/types": "^4.12.0", + "@smithy/property-provider": "^4.2.9", + "@smithy/smithy-client": "^4.11.7", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2791,17 +3169,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.33", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.33.tgz", - "integrity": "sha512-LEb2aq5F4oZUSzWBG7S53d4UytZSkOEJPXcBq/xbG2/TmK9EW5naUZ8lKu1BEyWMzdHIzEVN16M3k8oxDq+DJA==", + "version": "4.2.37", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.37.tgz", + "integrity": "sha512-1LcAt0PV1dletxiGwcw2IJ8vLNhfkir02NTi1i/CFCY2ObtM5wDDjn/8V2dbPrbyoh6OTFH+uayI1rSVRBMT3A==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.4.6", - "@smithy/credential-provider-imds": "^4.2.8", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/smithy-client": "^4.11.3", - "@smithy/types": "^4.12.0", + "@smithy/config-resolver": "^4.4.7", + "@smithy/credential-provider-imds": "^4.2.9", + "@smithy/node-config-provider": "^4.3.9", + "@smithy/property-provider": "^4.2.9", + "@smithy/smithy-client": "^4.11.7", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2809,13 +3187,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz", - "integrity": "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==", + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.9.tgz", + "integrity": "sha512-9FTqTzKxCFelCKdtHb22BTbrLgw7tTI+D6r/Ci/njI0tzqWLQctS0uEDTzraCR5K6IJItfFp1QmESlBytSpRhQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.8", - "@smithy/types": "^4.12.0", + "@smithy/node-config-provider": "^4.3.9", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2823,9 +3201,9 @@ } }, "node_modules/@smithy/util-hex-encoding": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", - "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.1.tgz", + "integrity": "sha512-c1hHtkgAWmE35/50gmdKajgGAKV3ePJ7t6UtEmpfCWJmQE9BQAQPz0URUVI89eSkcDqCtzqllxzG28IQoZPvwA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -2835,12 +3213,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.8.tgz", - "integrity": "sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.9.tgz", + "integrity": "sha512-pfnZneJ1S9X3TRmg2l3pG11Pvx2BW9O3NFhUN30llrK/yUKu8WbqMTx4/CzED+qKBYw0//ntUT00hvmaG+nLgA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2848,13 +3226,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.8.tgz", - "integrity": "sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.9.tgz", + "integrity": "sha512-79hfhL/oxP40SCXJGfjfE9pjbUVfHhXZFpCWXTHqXSluzaVy7jwWs9Ui7lLbfDBSp+7i+BIwgeVIRerbIRWN6g==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.2.8", - "@smithy/types": "^4.12.0", + "@smithy/service-error-classification": "^4.2.9", + "@smithy/types": "^4.12.1", "tslib": "^2.6.2" }, "engines": { @@ -2862,18 +3240,18 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.12", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.12.tgz", - "integrity": "sha512-D8tgkrmhAX/UNeCZbqbEO3uqyghUnEmmoO9YEvRuwxjlkKKUE7FOgCJnqpTlQPe9MApdWPky58mNQQHbnCzoNg==", + "version": "4.5.14", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.14.tgz", + "integrity": "sha512-IOBEiJTOltSx6MAfwkx/GSVM8/UCJxdtw13haP5OEL543lb1DN6TAypsxv+qcj4l/rKcpapbS6zK9MQGBOhoaA==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/node-http-handler": "^4.4.10", - "@smithy/types": "^4.12.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/fetch-http-handler": "^5.3.10", + "@smithy/node-http-handler": "^4.4.11", + "@smithy/types": "^4.12.1", + "@smithy/util-base64": "^4.3.1", + "@smithy/util-buffer-from": "^4.2.1", + "@smithy/util-hex-encoding": "^4.2.1", + "@smithy/util-utf8": "^4.2.1", "tslib": "^2.6.2" }, "engines": { @@ -2881,9 +3259,9 @@ } }, "node_modules/@smithy/util-uri-escape": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", - "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.1.tgz", + "integrity": "sha512-YmiUDn2eo2IOiWYYvGQkgX5ZkBSiTQu4FlDo5jNPpAxng2t6Sjb6WutnZV9l6VR4eJul1ABmCrnWBC9hKHQa6Q==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -2893,12 +3271,12 @@ } }, "node_modules/@smithy/util-utf8": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", - "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.1.tgz", + "integrity": "sha512-DSIwNaWtmzrNQHv8g7DBGR9mulSit65KSj5ymGEIAknmIN8IpbZefEep10LaMG/P/xquwbmJ1h9ectz8z6mV6g==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-buffer-from": "^4.2.1", "tslib": "^2.6.2" }, "engines": { @@ -2920,9 +3298,9 @@ } }, "node_modules/@smithy/uuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", - "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.1.tgz", + "integrity": "sha512-dSfDCeihDmZlV2oyr0yWPTUfh07suS+R5OB+FZGiv/hHyK3hrFBW5rR1UYjfa57vBsrP9lciFkRPzebaV1Qujw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3057,13 +3435,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.0.tgz", - "integrity": "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==", + "version": "24.10.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.13.tgz", + "integrity": "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==", "devOptional": true, "license": "MIT", "dependencies": { - "undici-types": "~7.14.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/pg": { @@ -3305,6 +3683,12 @@ "node": ">=12" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -3314,6 +3698,17 @@ "node": ">=8.0.0" } }, + "node_modules/axios": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/better-auth": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/better-auth/-/better-auth-1.4.5.tgz", @@ -3650,6 +4045,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/content-disposition": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", @@ -3727,6 +4134,12 @@ "node": "*" } }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -3760,6 +4173,15 @@ "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", "license": "MIT" }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -4001,6 +4423,21 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.25.11", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", @@ -4146,9 +4583,9 @@ "license": "MIT" }, "node_modules/fast-xml-parser": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz", - "integrity": "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.6.tgz", + "integrity": "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==", "funding": [ { "type": "github", @@ -4157,7 +4594,7 @@ ], "license": "MIT", "dependencies": { - "strnum": "^2.1.0" + "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" @@ -4198,6 +4635,63 @@ "node": ">= 0.8" } }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -4334,6 +4828,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -4481,6 +4990,28 @@ "dev": true, "license": "MIT" }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, "node_modules/jwa": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", @@ -4511,6 +5042,48 @@ "node": ">=20.0.0" } }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/loupe": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", @@ -5046,6 +5619,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/pump": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", @@ -5240,6 +5819,13 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/scmp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", + "integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==", + "deprecated": "Just use Node.js's crypto.timingSafeEqual()", + "license": "BSD-3-Clause" + }, "node_modules/secure-json-parse": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz", @@ -5257,6 +5843,18 @@ ], "license": "BSD-3-Clause" }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", @@ -5692,6 +6290,49 @@ "fsevents": "~2.3.3" } }, + "node_modules/twilio": { + "version": "5.12.2", + "resolved": "https://registry.npmjs.org/twilio/-/twilio-5.12.2.tgz", + "integrity": "sha512-yjTH04Ig0Z3PAxIXhwrto0IJC4Gv7lBDQQ9f4/P9zJhnxVdd+3tENqBMJOtdmmRags3X0jl2IGKEQefCEpJE9g==", + "license": "MIT", + "dependencies": { + "axios": "^1.12.0", + "dayjs": "^1.11.9", + "https-proxy-agent": "^5.0.0", + "jsonwebtoken": "^9.0.2", + "qs": "^6.14.1", + "scmp": "^2.1.0", + "xmlbuilder": "^13.0.2" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/twilio/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/twilio/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -5733,9 +6374,9 @@ "license": "MIT" }, "node_modules/undici-types": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", - "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "devOptional": true, "license": "MIT" }, @@ -5990,6 +6631,15 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/xmlbuilder": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", + "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==", + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/server/package.json b/server/package.json index 0e6a5cca..f7ee431d 100644 --- a/server/package.json +++ b/server/package.json @@ -20,7 +20,7 @@ "@biomejs/biome": "2.2.5", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", - "@types/node": "^24.7.0", + "@types/node": "^24.10.13", "@types/pg": "^8.15.5", "@types/swagger-ui-express": "^4.1.8", "@types/web-push": "^3.6.4", @@ -36,6 +36,7 @@ "@aws-sdk/client-bedrock-runtime": "^3.938.0", "@aws-sdk/client-s3": "^3.937.0", "@aws-sdk/client-secrets-manager": "^3.936.0", + "@aws-sdk/client-sns": "^3.996.0", "@aws-sdk/s3-request-presigner": "^3.937.0", "@trpc/client": "^11.6.0", "@trpc/server": "^11.6.0", @@ -51,6 +52,7 @@ "swagger-ui-express": "^5.0.1", "trpc-to-openapi": "^3.1.0", "tsx": "^4.20.6", + "twilio": "^5.12.2", "web-push": "^3.6.7", "zod": "^4.1.13" } diff --git a/server/scripts/create-user.ts b/server/scripts/create-user.ts index c1b35a7d..43db195d 100644 --- a/server/scripts/create-user.ts +++ b/server/scripts/create-user.ts @@ -18,9 +18,9 @@ async function createUser() { await connectPostgres(); // Edit this object to create a new user const userData = { - email: "basic2@basic.basic", + email: "basic@basic.basic", password: "password", - name: "U2", + name: "I2", phoneNumber: "555-123-1234", // optional rank: "Captain", // optional department: "Engineering", // optional diff --git a/server/src/data/repository/message-blast-repo.ts b/server/src/data/repository/message-blast-repo.ts index f9067061..7e452db9 100644 --- a/server/src/data/repository/message-blast-repo.ts +++ b/server/src/data/repository/message-blast-repo.ts @@ -1,5 +1,5 @@ import { and, eq, gt, sql } from "drizzle-orm"; -import { messageBlasts } from "../../data/db/schema.js"; +import { messageBlasts, users } from "../../data/db/schema.js"; import { db } from "../../data/db/sql.js"; import { ConflictError, NotFoundError } from "../../types/errors.js"; import type { @@ -12,6 +12,7 @@ import type { UpdateMessageBlastOutput, } from "../../types/message-blast-types.js"; import { parseTargetAudience } from "../../types/message-blast-types.js"; +import { TwilioSMSService } from "../../service/twilio-service.js"; /** * Repository to handle database queries/communication related to message blasts. @@ -77,6 +78,20 @@ export class MessageBlastRepository { throw new ConflictError("Failed to create broadcast"); } + const smsService = new TwilioSMSService(); + + const [sender] = await db + .select({ + name: users.name, // or whatever your name column is called + }) + .from(users) + .where(eq(users.id, created.senderId)); + + await smsService.broadcast( + ["+17742054619", "+14086129083"], + `GuardConnect broadcast from ${sender}:\n**${created.title}**\n${created.content}`, + ); + return this.parseMessageBlastRow(created); } @@ -116,9 +131,7 @@ export class MessageBlastRepository { * @param senderId Sender user ID * @returns Array of message blast objects */ - async getMessageBlastsBySender( - senderId: string, - ): Promise { + async getMessageBlastsBySender(senderId: string): Promise { const rows = await db .select({ blastId: messageBlasts.blastId, @@ -163,8 +176,7 @@ export class MessageBlastRepository { if (title !== undefined) updateData.title = title; if (content !== undefined) updateData.content = content; - if (targetAudience !== undefined) - updateData.targetAudience = targetAudience; + if (targetAudience !== undefined) updateData.targetAudience = targetAudience; if (validUntil !== undefined) updateData.validUntil = validUntil; if (status !== undefined) updateData.status = status; @@ -311,9 +323,7 @@ export class MessageBlastRepository { const branchPath = sql`${messageBlasts.targetAudience}->${query.branch}`; - const rankCondition = query.rank - ? sql`${branchPath}->'ranks' ? ${query.rank}` - : null; + const rankCondition = query.rank ? sql`${branchPath}->'ranks' ? ${query.rank}` : null; const departmentCondition = query.department ? sql`${branchPath}->'departments' ? ${query.department}` : null; @@ -322,9 +332,9 @@ export class MessageBlastRepository { return sql`(${messageBlasts.targetAudience} IS NULL OR ${branchPath} IS NOT NULL)`; } - const applicableConditions = [rankCondition, departmentCondition].filter( - Boolean, - ) as ReturnType[]; + const applicableConditions = [rankCondition, departmentCondition].filter(Boolean) as ReturnType< + typeof sql + >[]; return sql`(${messageBlasts.targetAudience} IS NULL OR ${sql.join( applicableConditions, diff --git a/server/src/service/twilio-service.ts b/server/src/service/twilio-service.ts new file mode 100644 index 00000000..91e70ccf --- /dev/null +++ b/server/src/service/twilio-service.ts @@ -0,0 +1,170 @@ +import twilio from "twilio"; + +interface SMSResult { + phoneNumber: string; + success: boolean; + messageId?: string; + error?: string; +} + +interface BroadcastResult { + total: number; + successful: number; + failed: number; + results: SMSResult[]; +} + +export class TwilioSMSService { + private client: ReturnType; + private fromNumber: string; + private readonly DEFAULT_BATCH_SIZE = 10; + private readonly DEFAULT_BATCH_DELAY_MS = 1000; + + constructor() { + const accountSid = process.env.TWILIO_ACCOUNT_SID; + const authToken = process.env.TWILIO_AUTH_TOKEN; + const fromNumber = process.env.TWILIO_PHONE_NUMBER; + + if (!accountSid || !authToken || !fromNumber) { + throw new Error( + "Missing Twilio credentials. Please set TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_PHONE_NUMBER in your environment variables.", + ); + } + + this.client = twilio(accountSid, authToken); + this.fromNumber = fromNumber; + } + + /** + * Send SMS to a single recipient + */ + async sendSMS(phoneNumber: string, message: string): Promise { + console.log(`📱 Attempting to send SMS to ${phoneNumber}`); + console.log(`📝 Message: ${message.substring(0, 50)}...`); + + try { + const result = await this.client.messages.create({ + body: message, + from: this.fromNumber, + to: phoneNumber, // Must be in E.164 format: +1234567890 + }); + + console.log(`✅ SMS sent successfully! MessageSID: ${result.sid}`); + console.log(` Status: ${result.status}`); + + return { + phoneNumber, + success: true, + messageId: result.sid, + }; + } catch (error) { + console.error(`❌ Failed to send SMS to ${phoneNumber}:`, error); + return { + phoneNumber, + success: false, + error: error instanceof Error ? error.message : "Unknown error", + }; + } + } + + /** + * Broadcast SMS to multiple recipients with rate limiting + */ + async broadcast( + phoneNumbers: string[], + message: string, + options?: { + batchSize?: number; + batchDelayMs?: number; + }, + ): Promise { + console.log(`📢 Starting broadcast to ${phoneNumbers.length} recipients`); + + const batchSize = options?.batchSize || this.DEFAULT_BATCH_SIZE; + const batchDelayMs = options?.batchDelayMs || this.DEFAULT_BATCH_DELAY_MS; + + const results: SMSResult[] = []; + + // Process in batches to avoid rate limits + for (let i = 0; i < phoneNumbers.length; i += batchSize) { + const batch = phoneNumbers.slice(i, i + batchSize); + console.log( + `📦 Processing batch ${Math.floor(i / batchSize) + 1} of ${Math.ceil(phoneNumbers.length / batchSize)}`, + ); + + // Send batch in parallel + const batchPromises = batch.map((phoneNumber) => this.sendSMS(phoneNumber, message)); + + const batchResults = await Promise.allSettled(batchPromises); + + // Collect results + batchResults.forEach((result) => { + if (result.status === "fulfilled") { + results.push(result.value); + } else { + results.push({ + phoneNumber: "unknown", + success: false, + error: result.reason, + }); + } + }); + + // Delay between batches (except for last batch) + if (i + batchSize < phoneNumbers.length) { + console.log(`⏳ Waiting ${batchDelayMs}ms before next batch...`); + await this.delay(batchDelayMs); + } + } + + // Calculate summary + const successful = results.filter((r) => r.success).length; + const failed = results.filter((r) => !r.success).length; + + console.log( + `📊 Broadcast complete: ${successful} successful, ${failed} failed out of ${phoneNumbers.length} total`, + ); + + return { + total: phoneNumbers.length, + successful, + failed, + results, + }; + } + + /** + * Validate phone number format (E.164) + */ + isValidPhoneNumber(phoneNumber: string): boolean { + // E.164 format: +[country code][number] + const e164Regex = /^\+[1-9]\d{1,14}$/; + return e164Regex.test(phoneNumber); + } + + /** + * Sanitize and validate phone numbers + */ + sanitizePhoneNumbers(phoneNumbers: string[]): string[] { + return phoneNumbers + .map((phone) => phone.trim()) + .filter((phone) => this.isValidPhoneNumber(phone)); + } + + /** + * Get message status (for tracking delivery) + */ + async getMessageStatus(messageSid: string): Promise { + try { + const message = await this.client.messages(messageSid).fetch(); + return message.status; + } catch (error) { + console.error(`Failed to fetch message status:`, error); + throw error; + } + } + + private delay(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); + } +} diff --git a/server/src/utils/aws-sns.ts b/server/src/utils/aws-sns.ts new file mode 100644 index 00000000..c168ce06 --- /dev/null +++ b/server/src/utils/aws-sns.ts @@ -0,0 +1,145 @@ +import { SNSClient, PublishCommand, type PublishCommandInput } from "@aws-sdk/client-sns"; + +interface SMSResult { + phoneNumber: string; + success: boolean; + messageId?: string; + error?: string; +} + +interface BroadcastResult { + total: number; + successful: number; + failed: number; + results: SMSResult[]; +} + +export class SMSBroadcastService { + private snsClient: SNSClient; + private readonly DEFAULT_BATCH_SIZE = 10; + private readonly DEFAULT_BATCH_DELAY_MS = 1000; + + constructor(region: string = "us-east-1") { + this.snsClient = new SNSClient({ region }); + } + + /** + * Send SMS to a single recipient + */ + async sendSMS( + phoneNumber: string, + message: string, + isTransactional: boolean = true, + ): Promise { + try { + const params: PublishCommandInput = { + Message: message, + PhoneNumber: phoneNumber, // Must be in E.164 format: +1234567890 + MessageAttributes: { + "AWS.SNS.SMS.SMSType": { + DataType: "String", + StringValue: isTransactional ? "Transactional" : "Promotional", + }, + }, + }; + + const command = new PublishCommand(params); + const response = await this.snsClient.send(command); + + return { + phoneNumber, + success: true, + messageId: response.MessageId, + }; + } catch (error) { + console.error(`Failed to send SMS to ${phoneNumber}:`, error); + return { + phoneNumber, + success: false, + error: error instanceof Error ? error.message : "Unknown error", + }; + } + } + + /** + * Broadcast SMS to multiple recipients with rate limiting + */ + async broadcast( + phoneNumbers: string[], + message: string, + options?: { + batchSize?: number; + batchDelayMs?: number; + isTransactional?: boolean; + }, + ): Promise { + const batchSize = options?.batchSize || this.DEFAULT_BATCH_SIZE; + const batchDelayMs = options?.batchDelayMs || this.DEFAULT_BATCH_DELAY_MS; + const isTransactional = options?.isTransactional ?? true; + + const results: SMSResult[] = []; + + // Process in batches to avoid rate limits + for (let i = 0; i < phoneNumbers.length; i += batchSize) { + const batch = phoneNumbers.slice(i, i + batchSize); + + // Send batch in parallel + const batchPromises = batch.map((phoneNumber) => + this.sendSMS(phoneNumber, message, isTransactional), + ); + + const batchResults = await Promise.allSettled(batchPromises); + + // Collect results + batchResults.forEach((result) => { + if (result.status === "fulfilled") { + results.push(result.value); + } else { + results.push({ + phoneNumber: "unknown", + success: false, + error: result.reason, + }); + } + }); + + // Delay between batches (except for last batch) + if (i + batchSize < phoneNumbers.length) { + await this.delay(batchDelayMs); + } + } + + // Calculate summary + const successful = results.filter((r) => r.success).length; + const failed = results.filter((r) => !r.success).length; + + return { + total: phoneNumbers.length, + successful, + failed, + results, + }; + } + + /** + * Validate phone number format (E.164) + */ + isValidPhoneNumber(phoneNumber: string): boolean { + // E.164 format: +[country code][number] + const e164Regex = /^\+[1-9]\d{1,14}$/; + return e164Regex.test(phoneNumber); + } + + /** + * Sanitize and validate phone numbers + */ + sanitizePhoneNumbers(phoneNumbers: string[]): string[] { + return phoneNumbers + .map((phone) => phone.trim()) + .filter((phone) => this.isValidPhoneNumber(phone)); + } + + private delay(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); + } +} From 2c8c52784c334c890be447c6ec8a8d43f8a6f724 Mon Sep 17 00:00:00 2001 From: Aidan Kelly Date: Fri, 27 Feb 2026 17:09:49 -0500 Subject: [PATCH 2/4] terms and privacy readmes --- docs/Privacy-Policy.md | 50 ++++++++++++++++++++++++++++++++++++ docs/Terms-and-Conditions.md | 38 +++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 docs/Privacy-Policy.md create mode 100644 docs/Terms-and-Conditions.md diff --git a/docs/Privacy-Policy.md b/docs/Privacy-Policy.md new file mode 100644 index 00000000..58aa18a2 --- /dev/null +++ b/docs/Privacy-Policy.md @@ -0,0 +1,50 @@ +# Privacy Policy + +**Massachusetts National Guard Communications App** +_Last Updated: February 27, 2026_ + +--- + +## 1. Overview + +The Massachusetts National Guard is committed to protecting the privacy of its personnel. This policy describes how information is collected, used, and safeguarded within this application. + +## 2. Information Collected + +This application may collect the following data: + +- **Account Information:** Name, rank, unit, and military email address. +- **Usage Data:** Login timestamps, message metadata, and device identifiers. +- **Communications Content:** Messages and files transmitted through the app for operational purposes. +- **Phone Number:** Used solely to deliver broadcast notifications sent by authorized senior personnel. + +## 3. How Information Is Used + +Collected information is used exclusively to: + +- Facilitate official Guard communications. +- Maintain application security and audit trails. +- Send official broadcast notifications to personnel on behalf of authorized senior-ranking members. +- Ensure compliance with applicable military and state regulations. + +Data is **never sold or shared with third parties** for commercial purposes. + +## 4. Data Storage and Security + +All data is stored on secured, access-controlled systems. Reasonable technical and administrative safeguards are in place to protect against unauthorized access, disclosure, or loss. Users should report any suspected security incidents to their IT administrator immediately. + +## 5. Data Retention + +Data is retained in accordance with Massachusetts National Guard records management policies and applicable state and federal regulations. Inactive accounts may be purged after a defined retention period. + +## 6. Your Rights + +Authorized users may request access to their account data or report concerns about data accuracy by contacting their unit administrator. + +## 7. Changes to This Policy + +This Privacy Policy may be revised periodically. Users will be notified of material changes through the application or official channels. + +## 8. Contact + +For privacy-related questions, contact your unit's communications officer or IT administrator. diff --git a/docs/Terms-and-Conditions.md b/docs/Terms-and-Conditions.md new file mode 100644 index 00000000..60c98513 --- /dev/null +++ b/docs/Terms-and-Conditions.md @@ -0,0 +1,38 @@ +# Terms and Conditions + +**Massachusetts National Guard Communications App** +_Last Updated: February 27, 2026_ + +--- + +## 1. Acceptance of Terms + +By accessing or using this application, you agree to be bound by these Terms and Conditions. Use is restricted to authorized Massachusetts National Guard personnel only. Unauthorized access is strictly prohibited. + +## 2. Authorized Use + +This application is provided solely for official Massachusetts National Guard communications and operational purposes. Users must: + +- Use the app only for lawful, mission-related activities. +- Comply with all applicable federal, state, and military regulations, including OPSEC guidelines. +- Not share access credentials or allow unauthorized individuals to use their account. + +## 3. User Responsibilities + +Users are responsible for all activity conducted under their account. Any misuse, unauthorized disclosure of information, or violation of military communications policy may result in revocation of access and disciplinary action in accordance with applicable regulations. + +## 4. Intellectual Property + +All content, branding, and software associated with this application are the property of the Massachusetts National Guard or its authorized vendors. Unauthorized reproduction or distribution is prohibited. + +## 5. Disclaimer of Warranties + +This application is provided "as is." The Massachusetts National Guard makes no guarantees regarding uptime, availability, or fitness for a particular purpose. It should not be used as the sole means of communication during critical operations. + +## 6. Modifications + +These Terms may be updated at any time. Continued use of the application following any changes constitutes acceptance of the revised Terms. + +## 7. Contact + +For questions or to report misuse, contact your unit's communications officer or IT administrator. From 09b43077b720b7f9e3a0494e93455e2ad873b9b2 Mon Sep 17 00:00:00 2001 From: Aidan Kelly Date: Fri, 27 Feb 2026 17:15:37 -0500 Subject: [PATCH 3/4] Format and lint --- .../src/data/repository/message-blast-repo.ts | 19 ++- server/src/service/twilio-service.ts | 4 +- server/src/utils/aws-sns.ts | 145 ------------------ 3 files changed, 15 insertions(+), 153 deletions(-) delete mode 100644 server/src/utils/aws-sns.ts diff --git a/server/src/data/repository/message-blast-repo.ts b/server/src/data/repository/message-blast-repo.ts index 7e452db9..9b1a2e3b 100644 --- a/server/src/data/repository/message-blast-repo.ts +++ b/server/src/data/repository/message-blast-repo.ts @@ -1,6 +1,7 @@ import { and, eq, gt, sql } from "drizzle-orm"; import { messageBlasts, users } from "../../data/db/schema.js"; import { db } from "../../data/db/sql.js"; +import { TwilioSMSService } from "../../service/twilio-service.js"; import { ConflictError, NotFoundError } from "../../types/errors.js"; import type { ActiveMessageBlastsForUserQuery, @@ -12,7 +13,6 @@ import type { UpdateMessageBlastOutput, } from "../../types/message-blast-types.js"; import { parseTargetAudience } from "../../types/message-blast-types.js"; -import { TwilioSMSService } from "../../service/twilio-service.js"; /** * Repository to handle database queries/communication related to message blasts. @@ -131,7 +131,9 @@ export class MessageBlastRepository { * @param senderId Sender user ID * @returns Array of message blast objects */ - async getMessageBlastsBySender(senderId: string): Promise { + async getMessageBlastsBySender( + senderId: string, + ): Promise { const rows = await db .select({ blastId: messageBlasts.blastId, @@ -176,7 +178,8 @@ export class MessageBlastRepository { if (title !== undefined) updateData.title = title; if (content !== undefined) updateData.content = content; - if (targetAudience !== undefined) updateData.targetAudience = targetAudience; + if (targetAudience !== undefined) + updateData.targetAudience = targetAudience; if (validUntil !== undefined) updateData.validUntil = validUntil; if (status !== undefined) updateData.status = status; @@ -323,7 +326,9 @@ export class MessageBlastRepository { const branchPath = sql`${messageBlasts.targetAudience}->${query.branch}`; - const rankCondition = query.rank ? sql`${branchPath}->'ranks' ? ${query.rank}` : null; + const rankCondition = query.rank + ? sql`${branchPath}->'ranks' ? ${query.rank}` + : null; const departmentCondition = query.department ? sql`${branchPath}->'departments' ? ${query.department}` : null; @@ -332,9 +337,9 @@ export class MessageBlastRepository { return sql`(${messageBlasts.targetAudience} IS NULL OR ${branchPath} IS NOT NULL)`; } - const applicableConditions = [rankCondition, departmentCondition].filter(Boolean) as ReturnType< - typeof sql - >[]; + const applicableConditions = [rankCondition, departmentCondition].filter( + Boolean, + ) as ReturnType[]; return sql`(${messageBlasts.targetAudience} IS NULL OR ${sql.join( applicableConditions, diff --git a/server/src/service/twilio-service.ts b/server/src/service/twilio-service.ts index 91e70ccf..b3179f06 100644 --- a/server/src/service/twilio-service.ts +++ b/server/src/service/twilio-service.ts @@ -93,7 +93,9 @@ export class TwilioSMSService { ); // Send batch in parallel - const batchPromises = batch.map((phoneNumber) => this.sendSMS(phoneNumber, message)); + const batchPromises = batch.map((phoneNumber) => + this.sendSMS(phoneNumber, message), + ); const batchResults = await Promise.allSettled(batchPromises); diff --git a/server/src/utils/aws-sns.ts b/server/src/utils/aws-sns.ts deleted file mode 100644 index c168ce06..00000000 --- a/server/src/utils/aws-sns.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { SNSClient, PublishCommand, type PublishCommandInput } from "@aws-sdk/client-sns"; - -interface SMSResult { - phoneNumber: string; - success: boolean; - messageId?: string; - error?: string; -} - -interface BroadcastResult { - total: number; - successful: number; - failed: number; - results: SMSResult[]; -} - -export class SMSBroadcastService { - private snsClient: SNSClient; - private readonly DEFAULT_BATCH_SIZE = 10; - private readonly DEFAULT_BATCH_DELAY_MS = 1000; - - constructor(region: string = "us-east-1") { - this.snsClient = new SNSClient({ region }); - } - - /** - * Send SMS to a single recipient - */ - async sendSMS( - phoneNumber: string, - message: string, - isTransactional: boolean = true, - ): Promise { - try { - const params: PublishCommandInput = { - Message: message, - PhoneNumber: phoneNumber, // Must be in E.164 format: +1234567890 - MessageAttributes: { - "AWS.SNS.SMS.SMSType": { - DataType: "String", - StringValue: isTransactional ? "Transactional" : "Promotional", - }, - }, - }; - - const command = new PublishCommand(params); - const response = await this.snsClient.send(command); - - return { - phoneNumber, - success: true, - messageId: response.MessageId, - }; - } catch (error) { - console.error(`Failed to send SMS to ${phoneNumber}:`, error); - return { - phoneNumber, - success: false, - error: error instanceof Error ? error.message : "Unknown error", - }; - } - } - - /** - * Broadcast SMS to multiple recipients with rate limiting - */ - async broadcast( - phoneNumbers: string[], - message: string, - options?: { - batchSize?: number; - batchDelayMs?: number; - isTransactional?: boolean; - }, - ): Promise { - const batchSize = options?.batchSize || this.DEFAULT_BATCH_SIZE; - const batchDelayMs = options?.batchDelayMs || this.DEFAULT_BATCH_DELAY_MS; - const isTransactional = options?.isTransactional ?? true; - - const results: SMSResult[] = []; - - // Process in batches to avoid rate limits - for (let i = 0; i < phoneNumbers.length; i += batchSize) { - const batch = phoneNumbers.slice(i, i + batchSize); - - // Send batch in parallel - const batchPromises = batch.map((phoneNumber) => - this.sendSMS(phoneNumber, message, isTransactional), - ); - - const batchResults = await Promise.allSettled(batchPromises); - - // Collect results - batchResults.forEach((result) => { - if (result.status === "fulfilled") { - results.push(result.value); - } else { - results.push({ - phoneNumber: "unknown", - success: false, - error: result.reason, - }); - } - }); - - // Delay between batches (except for last batch) - if (i + batchSize < phoneNumbers.length) { - await this.delay(batchDelayMs); - } - } - - // Calculate summary - const successful = results.filter((r) => r.success).length; - const failed = results.filter((r) => !r.success).length; - - return { - total: phoneNumbers.length, - successful, - failed, - results, - }; - } - - /** - * Validate phone number format (E.164) - */ - isValidPhoneNumber(phoneNumber: string): boolean { - // E.164 format: +[country code][number] - const e164Regex = /^\+[1-9]\d{1,14}$/; - return e164Regex.test(phoneNumber); - } - - /** - * Sanitize and validate phone numbers - */ - sanitizePhoneNumbers(phoneNumbers: string[]): string[] { - return phoneNumbers - .map((phone) => phone.trim()) - .filter((phone) => this.isValidPhoneNumber(phone)); - } - - private delay(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)); - } -} From c204a5d8bae5cb883bb10acf670b2556be9e1190 Mon Sep 17 00:00:00 2001 From: Aidan Kelly Date: Fri, 27 Feb 2026 17:19:41 -0500 Subject: [PATCH 4/4] Privacy and Terms docs --- docs/Privacy-Policy.md | 50 ++++++++++++++++++++++++++++++++++++ docs/Terms-and-Conditions.md | 38 +++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 docs/Privacy-Policy.md create mode 100644 docs/Terms-and-Conditions.md diff --git a/docs/Privacy-Policy.md b/docs/Privacy-Policy.md new file mode 100644 index 00000000..58aa18a2 --- /dev/null +++ b/docs/Privacy-Policy.md @@ -0,0 +1,50 @@ +# Privacy Policy + +**Massachusetts National Guard Communications App** +_Last Updated: February 27, 2026_ + +--- + +## 1. Overview + +The Massachusetts National Guard is committed to protecting the privacy of its personnel. This policy describes how information is collected, used, and safeguarded within this application. + +## 2. Information Collected + +This application may collect the following data: + +- **Account Information:** Name, rank, unit, and military email address. +- **Usage Data:** Login timestamps, message metadata, and device identifiers. +- **Communications Content:** Messages and files transmitted through the app for operational purposes. +- **Phone Number:** Used solely to deliver broadcast notifications sent by authorized senior personnel. + +## 3. How Information Is Used + +Collected information is used exclusively to: + +- Facilitate official Guard communications. +- Maintain application security and audit trails. +- Send official broadcast notifications to personnel on behalf of authorized senior-ranking members. +- Ensure compliance with applicable military and state regulations. + +Data is **never sold or shared with third parties** for commercial purposes. + +## 4. Data Storage and Security + +All data is stored on secured, access-controlled systems. Reasonable technical and administrative safeguards are in place to protect against unauthorized access, disclosure, or loss. Users should report any suspected security incidents to their IT administrator immediately. + +## 5. Data Retention + +Data is retained in accordance with Massachusetts National Guard records management policies and applicable state and federal regulations. Inactive accounts may be purged after a defined retention period. + +## 6. Your Rights + +Authorized users may request access to their account data or report concerns about data accuracy by contacting their unit administrator. + +## 7. Changes to This Policy + +This Privacy Policy may be revised periodically. Users will be notified of material changes through the application or official channels. + +## 8. Contact + +For privacy-related questions, contact your unit's communications officer or IT administrator. diff --git a/docs/Terms-and-Conditions.md b/docs/Terms-and-Conditions.md new file mode 100644 index 00000000..60c98513 --- /dev/null +++ b/docs/Terms-and-Conditions.md @@ -0,0 +1,38 @@ +# Terms and Conditions + +**Massachusetts National Guard Communications App** +_Last Updated: February 27, 2026_ + +--- + +## 1. Acceptance of Terms + +By accessing or using this application, you agree to be bound by these Terms and Conditions. Use is restricted to authorized Massachusetts National Guard personnel only. Unauthorized access is strictly prohibited. + +## 2. Authorized Use + +This application is provided solely for official Massachusetts National Guard communications and operational purposes. Users must: + +- Use the app only for lawful, mission-related activities. +- Comply with all applicable federal, state, and military regulations, including OPSEC guidelines. +- Not share access credentials or allow unauthorized individuals to use their account. + +## 3. User Responsibilities + +Users are responsible for all activity conducted under their account. Any misuse, unauthorized disclosure of information, or violation of military communications policy may result in revocation of access and disciplinary action in accordance with applicable regulations. + +## 4. Intellectual Property + +All content, branding, and software associated with this application are the property of the Massachusetts National Guard or its authorized vendors. Unauthorized reproduction or distribution is prohibited. + +## 5. Disclaimer of Warranties + +This application is provided "as is." The Massachusetts National Guard makes no guarantees regarding uptime, availability, or fitness for a particular purpose. It should not be used as the sole means of communication during critical operations. + +## 6. Modifications + +These Terms may be updated at any time. Continued use of the application following any changes constitutes acceptance of the revised Terms. + +## 7. Contact + +For questions or to report misuse, contact your unit's communications officer or IT administrator.