diff --git a/.env.example b/.env.example index 68156c4..baae955 100644 --- a/.env.example +++ b/.env.example @@ -28,3 +28,18 @@ EMAIL_TEMPLATES_DIR=src/email/templates # Form Schemas Directory FORM_SCHEMAS_DIR=schemas + +# EZPay Configuration +EZPAY_API_KEY=HWqgTn5EXIHLAzVjXtGpB2mIjgQgj0Ql +EZPAY_BASE_URL=https://test.ezpay.gov.bb +EZPAY_WEBHOOK_SECRET=your_webhook_secret + +# Department-specific EZPay API keys +EZPAY_EDUCATION_API_KEY=edu_specific_api_key_here +EZPAY_HEALTH_API_KEY=health_specific_api_key_here +EZPAY_SOCIAL_SERVICES_API_KEY=social_services_api_key_here +EZPAY_TRANSPORT_API_KEY=transport_api_key_here + +# Form-specific secrets (example) +PRIMARY_SCHOOL_TEXTBOOK_GRANT_PAYMENT_CODE=EDU001 +PRIMARY_SCHOOL_TEXTBOOK_GRANT_ADMIN_EMAIL=education@gov.bb diff --git a/package-lock.json b/package-lock.json index 8ecfcb5..890e7e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,11 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { + "@aws-sdk/client-s3": "^3.954.0", "@aws-sdk/client-ses": "^3.933.0", "@aws-sdk/client-sesv2": "^3.933.0", + "@aws-sdk/lib-storage": "^3.954.0", + "@aws-sdk/s3-request-presigner": "^3.954.0", "@nestjs/common": "^9.0.0", "@nestjs/config": "^4.0.2", "@nestjs/core": "^9.0.0", @@ -33,6 +36,7 @@ "@nestjs/testing": "^9.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.8", + "@types/multer": "^2.0.0", "@types/node": "^16.0.0", "@types/supertest": "^2.0.11", "@types/uuid": "^11.0.0", @@ -163,6 +167,92 @@ "tslib": "^2.1.0" } }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/crc32c/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", @@ -298,6 +388,543 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.954.0.tgz", + "integrity": "sha512-DoeySsljzjuWRzqoETLszHGKKOOWlzuGZh3oAF7TkYRsrwbuYYmttrWomb9koogaF0S5YSPwCMCUbKbpF0lbTA==", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/credential-provider-node": "3.954.0", + "@aws-sdk/middleware-bucket-endpoint": "3.953.0", + "@aws-sdk/middleware-expect-continue": "3.953.0", + "@aws-sdk/middleware-flexible-checksums": "3.954.0", + "@aws-sdk/middleware-host-header": "3.953.0", + "@aws-sdk/middleware-location-constraint": "3.953.0", + "@aws-sdk/middleware-logger": "3.953.0", + "@aws-sdk/middleware-recursion-detection": "3.953.0", + "@aws-sdk/middleware-sdk-s3": "3.954.0", + "@aws-sdk/middleware-ssec": "3.953.0", + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/region-config-resolver": "3.953.0", + "@aws-sdk/signature-v4-multi-region": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@aws-sdk/util-user-agent-browser": "3.953.0", + "@aws-sdk/util-user-agent-node": "3.954.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/core": "^3.19.0", + "@smithy/eventstream-serde-browser": "^4.2.6", + "@smithy/eventstream-serde-config-resolver": "^4.3.6", + "@smithy/eventstream-serde-node": "^4.2.6", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/hash-blob-browser": "^4.2.7", + "@smithy/hash-node": "^4.2.6", + "@smithy/hash-stream-node": "^4.2.6", + "@smithy/invalid-dependency": "^4.2.6", + "@smithy/md5-js": "^4.2.6", + "@smithy/middleware-content-length": "^4.2.6", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/middleware-retry": "^4.4.16", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/middleware-stack": "^4.2.6", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@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.15", + "@smithy/util-defaults-mode-node": "^4.2.18", + "@smithy/util-endpoints": "^3.2.6", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-retry": "^4.2.6", + "@smithy/util-stream": "^4.5.7", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.954.0.tgz", + "integrity": "sha512-FVyMAvlFhLK68DHWB1lSkCRTm25xl38bIZDd+jKt5+yDolCrG5+n9aIN8AA8jNO1HNGhZuMjSIQm9r5rGmJH8g==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/middleware-host-header": "3.953.0", + "@aws-sdk/middleware-logger": "3.953.0", + "@aws-sdk/middleware-recursion-detection": "3.953.0", + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/region-config-resolver": "3.953.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@aws-sdk/util-user-agent-browser": "3.953.0", + "@aws-sdk/util-user-agent-node": "3.954.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/core": "^3.19.0", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/hash-node": "^4.2.6", + "@smithy/invalid-dependency": "^4.2.6", + "@smithy/middleware-content-length": "^4.2.6", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/middleware-retry": "^4.4.16", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/middleware-stack": "^4.2.6", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@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.15", + "@smithy/util-defaults-mode-node": "^4.2.18", + "@smithy/util-endpoints": "^3.2.6", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-retry": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/core": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.954.0.tgz", + "integrity": "sha512-5oYO5RP+mvCNXNj8XnF9jZo0EP0LTseYOJVNQYcii1D9DJqzHL3HJWurYh7cXxz7G7eDyvVYA01O9Xpt34TdoA==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@aws-sdk/xml-builder": "3.953.0", + "@smithy/core": "^3.19.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/property-provider": "^4.2.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/signature-v4": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.954.0.tgz", + "integrity": "sha512-2HNkqBjfsvyoRuPAiFh86JBFMFyaCNhL4VyH6XqwTGKZffjG7hdBmzXPy7AT7G3oFh1k/1Zc27v0qxaKoK7mBA==", + "dependencies": { + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.954.0.tgz", + "integrity": "sha512-CrWD5300+NE1OYRnSVDxoG7G0b5cLIZb7yp+rNQ5Jq/kqnTmyJXpVAsivq+bQIDaGzPXhadzpAMIoo7K/aHaag==", + "dependencies": { + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/property-provider": "^4.2.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/util-stream": "^4.5.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.954.0.tgz", + "integrity": "sha512-WAFD8pVwRSoBsuXcoD+s/hrdsP9Z0PNUedSgkOGExuJVAabpM2cIIMzYNsdHio9XFZUSqHkv8mF5mQXuIZvuzg==", + "dependencies": { + "@aws-sdk/core": "3.954.0", + "@aws-sdk/credential-provider-env": "3.954.0", + "@aws-sdk/credential-provider-http": "3.954.0", + "@aws-sdk/credential-provider-login": "3.954.0", + "@aws-sdk/credential-provider-process": "3.954.0", + "@aws-sdk/credential-provider-sso": "3.954.0", + "@aws-sdk/credential-provider-web-identity": "3.954.0", + "@aws-sdk/nested-clients": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/credential-provider-imds": "^4.2.6", + "@smithy/property-provider": "^4.2.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.954.0.tgz", + "integrity": "sha512-UPBjw7Lnly5i+/rES8Z5U+nPaumzEUYOE/wrHkxyH6JjwFWn8w7R07fE5Z5cgYlIq1U1lQ7sxYwB3wHPpQ65Aw==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.954.0", + "@aws-sdk/credential-provider-http": "3.954.0", + "@aws-sdk/credential-provider-ini": "3.954.0", + "@aws-sdk/credential-provider-process": "3.954.0", + "@aws-sdk/credential-provider-sso": "3.954.0", + "@aws-sdk/credential-provider-web-identity": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/credential-provider-imds": "^4.2.6", + "@smithy/property-provider": "^4.2.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.954.0.tgz", + "integrity": "sha512-Y1/0O2LgbKM8iIgcVj/GNEQW6p90LVTCOzF2CI1pouoKqxmZ/1F7F66WHoa6XUOfKaCRj/R6nuMR3om9ThaM5A==", + "dependencies": { + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.954.0.tgz", + "integrity": "sha512-UXxGfkp/plFRdyidMLvNul5zoLKmHhVQOCrD2OgR/lg9jNqNmJ7abF+Qu8abo902iDkhU21Qj4M398cx6l8Kng==", + "dependencies": { + "@aws-sdk/client-sso": "3.954.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/token-providers": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.954.0.tgz", + "integrity": "sha512-XEyf1T08q1tG4zkTS4Dnf1cAQyrJUo/xlvi6XNpqGhY3bOmKUYE2h/K6eITIdytDL9VuCpWYQ6YRcIVtL29E0w==", + "dependencies": { + "@aws-sdk/core": "3.954.0", + "@aws-sdk/nested-clients": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.953.0.tgz", + "integrity": "sha512-jTGhfkONav+r4E6HLOrl5SzBqDmPByUYCkyB/c/3TVb8jX3wAZx8/q9bphKpCh+G5ARi3IdbSisgkZrJYqQ19Q==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.953.0.tgz", + "integrity": "sha512-PlWdVYgcuptkIC0ZKqVUhWNtSHXJSx7U9V8J7dJjRmsXC40X7zpEycvrkzDMJjeTDGcCceYbyYAg/4X1lkcIMw==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.953.0.tgz", + "integrity": "sha512-cmIJx0gWeesUKK4YwgE+VQL3mpACr3/J24fbwnc1Z5tntC86b+HQFzU5vsBDw6lLwyD46dBgWdsXFh1jL+ZaFw==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.954.0.tgz", + "integrity": "sha512-274CNmnRjknmfFb2o0Azxic54fnujaA8AYSeRUOho3lN48TVzx85eAFWj2kLgvUJO88pE3jBDPWboKQiQdXeUQ==", + "dependencies": { + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-arn-parser": "3.953.0", + "@smithy/core": "^3.19.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/signature-v4": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-stream": "^4.5.7", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.954.0.tgz", + "integrity": "sha512-5PX8JDe3dB2+MqXeGIhmgFnm2rbVsSxhz+Xyuu1oxLtbOn+a9UDA+sNBufEBjt3UxWy5qwEEY1fxdbXXayjlGg==", + "dependencies": { + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@smithy/core": "^3.19.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/nested-clients": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.954.0.tgz", + "integrity": "sha512-JLUhf35fTQIDPLk6G5KPggL9tV//Hjhy6+N2zZeis76LuBRNhKDq8z1CFyKhjf00vXi/tDYdn9D7y9emI+5Y/g==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/middleware-host-header": "3.953.0", + "@aws-sdk/middleware-logger": "3.953.0", + "@aws-sdk/middleware-recursion-detection": "3.953.0", + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/region-config-resolver": "3.953.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@aws-sdk/util-user-agent-browser": "3.953.0", + "@aws-sdk/util-user-agent-node": "3.954.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/core": "^3.19.0", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/hash-node": "^4.2.6", + "@smithy/invalid-dependency": "^4.2.6", + "@smithy/middleware-content-length": "^4.2.6", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/middleware-retry": "^4.4.16", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/middleware-stack": "^4.2.6", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@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.15", + "@smithy/util-defaults-mode-node": "^4.2.18", + "@smithy/util-endpoints": "^3.2.6", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-retry": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.953.0.tgz", + "integrity": "sha512-5MJgnsc+HLO+le0EK1cy92yrC7kyhGZSpaq8PcQvKs9qtXCXT5Tb6tMdkr5Y07JxYsYOV1omWBynvL6PWh08tQ==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.954.0.tgz", + "integrity": "sha512-GJJbUaSlGrMSRWui3Oz8ByygpQlzDGm195yTKirgGyu4tfYrFr/QWrWT42EUktY/L4Irev1pdHTuLS+AGHO1gw==", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/signature-v4": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.954.0.tgz", + "integrity": "sha512-rDyN3oQQKMOJgyQ9/LNbh4fAGAj8ePMGOAQzSP/kyzizmViI6STpBW1o/VRqiTgMNi1bvA9ZasDtfrJqcVt0iA==", + "dependencies": { + "@aws-sdk/core": "3.954.0", + "@aws-sdk/nested-clients": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/types": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.953.0.tgz", + "integrity": "sha512-M9Iwg9kTyqTErI0vOTVVpcnTHWzS3VplQppy8MuL02EE+mJ0BIwpWfsaAPQW+/XnVpdNpWZTsHcNE29f1+hR8g==", + "dependencies": { + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-arn-parser": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.953.0.tgz", + "integrity": "sha512-9hqdKkn4OvYzzaLryq2xnwcrPc8ziY34i9szUdgBfSqEC6pBxbY9/lLXmrgzfwMSL2Z7/v2go4Od0p5eukKLMQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.953.0.tgz", + "integrity": "sha512-rjaS6jrFksopXvNg6YeN+D1lYwhcByORNlFuYesFvaQNtPOufbE5tJL4GJ3TMXyaY0uFR28N5BHHITPyWWfH/g==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@smithy/util-endpoints": "^3.2.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.953.0.tgz", + "integrity": "sha512-UF5NeqYesWuFao+u7LJvpV1SJCaLml5BtFZKUdTnNNMeN6jvV+dW/eQoFGpXF94RCqguX0XESmRuRRPQp+/rzQ==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/types": "^4.10.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.954.0.tgz", + "integrity": "sha512-fB5S5VOu7OFkeNzcblQlez4AjO5hgDFaa7phYt7716YWisY3RjAaQPlxgv+G3GltHHDJIfzEC5aRxdf62B9zMg==", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/xml-builder": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.953.0.tgz", + "integrity": "sha512-Zmrj21jQ2OeOJGr9spPiN00aQvXa/WUqRXcTVENhrMt+OFoSOfDFpYhUj9NQ09QmQ8KMWFoWuWW6iKurNqLvAA==", + "dependencies": { + "@smithy/types": "^4.10.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@aws-sdk/client-ses": { "version": "3.933.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.933.0.tgz", @@ -504,60 +1131,302 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-env/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "node_modules/@aws-sdk/credential-provider-env/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.932.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.932.0.tgz", + "integrity": "sha512-b6N9Nnlg8JInQwzBkUq5spNaXssM3h3zLxGzpPrnw0nHSIWPJPTbZzA5Ca285fcDUFuKP+qf3qkuqlAjGOdWhg==", + "dependencies": { + "@aws-sdk/core": "3.932.0", + "@aws-sdk/types": "3.930.0", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.5", + "@smithy/types": "^4.9.0", + "@smithy/util-stream": "^4.5.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.933.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.933.0.tgz", + "integrity": "sha512-HygGyKuMG5AaGXsmM0d81miWDon55xwalRHB3UmDg3QBhtunbNIoIaWUbNTKuBZXcIN6emeeEZw/YgSMqLc0YA==", + "dependencies": { + "@aws-sdk/core": "3.932.0", + "@aws-sdk/credential-provider-env": "3.932.0", + "@aws-sdk/credential-provider-http": "3.932.0", + "@aws-sdk/credential-provider-process": "3.932.0", + "@aws-sdk/credential-provider-sso": "3.933.0", + "@aws-sdk/credential-provider-web-identity": "3.933.0", + "@aws-sdk/nested-clients": "3.933.0", + "@aws-sdk/types": "3.930.0", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.954.0.tgz", + "integrity": "sha512-EYqaBWwdVbVK7prmsmgTWLPptoWREplPkFMFscOpVmseDvf/0IjYNbNLLtfuhy/6L7ZBGI9wat2k4u0MRivvxA==", + "dependencies": { + "@aws-sdk/core": "3.954.0", + "@aws-sdk/nested-clients": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/core": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.954.0.tgz", + "integrity": "sha512-5oYO5RP+mvCNXNj8XnF9jZo0EP0LTseYOJVNQYcii1D9DJqzHL3HJWurYh7cXxz7G7eDyvVYA01O9Xpt34TdoA==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@aws-sdk/xml-builder": "3.953.0", + "@smithy/core": "^3.19.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/property-provider": "^4.2.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/signature-v4": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.953.0.tgz", + "integrity": "sha512-jTGhfkONav+r4E6HLOrl5SzBqDmPByUYCkyB/c/3TVb8jX3wAZx8/q9bphKpCh+G5ARi3IdbSisgkZrJYqQ19Q==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/middleware-logger": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.953.0.tgz", + "integrity": "sha512-PlWdVYgcuptkIC0ZKqVUhWNtSHXJSx7U9V8J7dJjRmsXC40X7zpEycvrkzDMJjeTDGcCceYbyYAg/4X1lkcIMw==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.953.0.tgz", + "integrity": "sha512-cmIJx0gWeesUKK4YwgE+VQL3mpACr3/J24fbwnc1Z5tntC86b+HQFzU5vsBDw6lLwyD46dBgWdsXFh1jL+ZaFw==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.954.0.tgz", + "integrity": "sha512-5PX8JDe3dB2+MqXeGIhmgFnm2rbVsSxhz+Xyuu1oxLtbOn+a9UDA+sNBufEBjt3UxWy5qwEEY1fxdbXXayjlGg==", + "dependencies": { + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@smithy/core": "^3.19.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/nested-clients": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.954.0.tgz", + "integrity": "sha512-JLUhf35fTQIDPLk6G5KPggL9tV//Hjhy6+N2zZeis76LuBRNhKDq8z1CFyKhjf00vXi/tDYdn9D7y9emI+5Y/g==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/middleware-host-header": "3.953.0", + "@aws-sdk/middleware-logger": "3.953.0", + "@aws-sdk/middleware-recursion-detection": "3.953.0", + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/region-config-resolver": "3.953.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@aws-sdk/util-user-agent-browser": "3.953.0", + "@aws-sdk/util-user-agent-node": "3.954.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/core": "^3.19.0", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/hash-node": "^4.2.6", + "@smithy/invalid-dependency": "^4.2.6", + "@smithy/middleware-content-length": "^4.2.6", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/middleware-retry": "^4.4.16", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/middleware-stack": "^4.2.6", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@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.15", + "@smithy/util-defaults-mode-node": "^4.2.18", + "@smithy/util-endpoints": "^3.2.6", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-retry": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.953.0.tgz", + "integrity": "sha512-5MJgnsc+HLO+le0EK1cy92yrC7kyhGZSpaq8PcQvKs9qtXCXT5Tb6tMdkr5Y07JxYsYOV1omWBynvL6PWh08tQ==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/types": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.953.0.tgz", + "integrity": "sha512-M9Iwg9kTyqTErI0vOTVVpcnTHWzS3VplQppy8MuL02EE+mJ0BIwpWfsaAPQW+/XnVpdNpWZTsHcNE29f1+hR8g==", + "dependencies": { + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/util-endpoints": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.953.0.tgz", + "integrity": "sha512-rjaS6jrFksopXvNg6YeN+D1lYwhcByORNlFuYesFvaQNtPOufbE5tJL4GJ3TMXyaY0uFR28N5BHHITPyWWfH/g==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@smithy/util-endpoints": "^3.2.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.953.0.tgz", + "integrity": "sha512-UF5NeqYesWuFao+u7LJvpV1SJCaLml5BtFZKUdTnNNMeN6jvV+dW/eQoFGpXF94RCqguX0XESmRuRRPQp+/rzQ==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/types": "^4.10.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.932.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.932.0.tgz", - "integrity": "sha512-b6N9Nnlg8JInQwzBkUq5spNaXssM3h3zLxGzpPrnw0nHSIWPJPTbZzA5Ca285fcDUFuKP+qf3qkuqlAjGOdWhg==", + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.954.0.tgz", + "integrity": "sha512-fB5S5VOu7OFkeNzcblQlez4AjO5hgDFaa7phYt7716YWisY3RjAaQPlxgv+G3GltHHDJIfzEC5aRxdf62B9zMg==", "dependencies": { - "@aws-sdk/core": "3.932.0", - "@aws-sdk/types": "3.930.0", - "@smithy/fetch-http-handler": "^5.3.6", - "@smithy/node-http-handler": "^4.4.5", - "@smithy/property-provider": "^4.2.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/smithy-client": "^4.9.5", - "@smithy/types": "^4.9.0", - "@smithy/util-stream": "^4.5.6", + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@aws-sdk/credential-provider-http/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.933.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.933.0.tgz", - "integrity": "sha512-HygGyKuMG5AaGXsmM0d81miWDon55xwalRHB3UmDg3QBhtunbNIoIaWUbNTKuBZXcIN6emeeEZw/YgSMqLc0YA==", + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/xml-builder": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.953.0.tgz", + "integrity": "sha512-Zmrj21jQ2OeOJGr9spPiN00aQvXa/WUqRXcTVENhrMt+OFoSOfDFpYhUj9NQ09QmQ8KMWFoWuWW6iKurNqLvAA==", "dependencies": { - "@aws-sdk/core": "3.932.0", - "@aws-sdk/credential-provider-env": "3.932.0", - "@aws-sdk/credential-provider-http": "3.932.0", - "@aws-sdk/credential-provider-process": "3.932.0", - "@aws-sdk/credential-provider-sso": "3.933.0", - "@aws-sdk/credential-provider-web-identity": "3.933.0", - "@aws-sdk/nested-clients": "3.933.0", - "@aws-sdk/types": "3.930.0", - "@smithy/credential-provider-imds": "^4.2.5", - "@smithy/property-provider": "^4.2.5", - "@smithy/shared-ini-file-loader": "^4.4.0", - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.10.0", + "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/tslib": { + "node_modules/@aws-sdk/credential-provider-login/node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" @@ -655,6 +1524,192 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@aws-sdk/lib-storage": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.954.0.tgz", + "integrity": "sha512-NdqDWorjG5dvnp/sfDsrru5S52O8gCKo92wcQe8A7M+6JUaQ6Qtnmzh7104SYCc/OdZwrjg7OL1pViGfw+lZxQ==", + "dependencies": { + "@smithy/abort-controller": "^4.2.6", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/smithy-client": "^4.10.1", + "buffer": "5.6.0", + "events": "3.3.0", + "stream-browserify": "3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-s3": "^3.954.0" + } + }, + "node_modules/@aws-sdk/lib-storage/node_modules/buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "node_modules/@aws-sdk/lib-storage/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.953.0.tgz", + "integrity": "sha512-YHVRIOowtGIl/L2WuS83FgRlm31tU0aL1yryWaFtF+AFjA5BIeiFkxIZqaRGxJpJvFEBdohsyq6Ipv5mgWfezg==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-arn-parser": "3.953.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "@smithy/util-config-provider": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@aws-sdk/types": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.953.0.tgz", + "integrity": "sha512-M9Iwg9kTyqTErI0vOTVVpcnTHWzS3VplQppy8MuL02EE+mJ0BIwpWfsaAPQW+/XnVpdNpWZTsHcNE29f1+hR8g==", + "dependencies": { + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@aws-sdk/util-arn-parser": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.953.0.tgz", + "integrity": "sha512-9hqdKkn4OvYzzaLryq2xnwcrPc8ziY34i9szUdgBfSqEC6pBxbY9/lLXmrgzfwMSL2Z7/v2go4Od0p5eukKLMQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.953.0.tgz", + "integrity": "sha512-BQTVXrypQ0rbb7au/Hk4IS5GaJZlwk6O44Rjk6Kxb0IvGQhSurNTuesFiJx1sLbf+w+T31saPtODcfQQERqhCQ==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue/node_modules/@aws-sdk/types": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.953.0.tgz", + "integrity": "sha512-M9Iwg9kTyqTErI0vOTVVpcnTHWzS3VplQppy8MuL02EE+mJ0BIwpWfsaAPQW+/XnVpdNpWZTsHcNE29f1+hR8g==", + "dependencies": { + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.954.0.tgz", + "integrity": "sha512-hHOPDJyxucNodkgapLhA0VdwDBwVYN9DX20aA6j+3nwutAlZ5skaV7Bw0W3YC7Fh/ieDKKhcSZulONd4lVTwMg==", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-stream": "^4.5.7", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/core": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.954.0.tgz", + "integrity": "sha512-5oYO5RP+mvCNXNj8XnF9jZo0EP0LTseYOJVNQYcii1D9DJqzHL3HJWurYh7cXxz7G7eDyvVYA01O9Xpt34TdoA==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@aws-sdk/xml-builder": "3.953.0", + "@smithy/core": "^3.19.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/property-provider": "^4.2.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/signature-v4": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/types": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.953.0.tgz", + "integrity": "sha512-M9Iwg9kTyqTErI0vOTVVpcnTHWzS3VplQppy8MuL02EE+mJ0BIwpWfsaAPQW+/XnVpdNpWZTsHcNE29f1+hR8g==", + "dependencies": { + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/xml-builder": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.953.0.tgz", + "integrity": "sha512-Zmrj21jQ2OeOJGr9spPiN00aQvXa/WUqRXcTVENhrMt+OFoSOfDFpYhUj9NQ09QmQ8KMWFoWuWW6iKurNqLvAA==", + "dependencies": { + "@smithy/types": "^4.10.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@aws-sdk/middleware-host-header": { "version": "3.930.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.930.0.tgz", @@ -674,6 +1729,36 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.953.0.tgz", + "integrity": "sha512-h0urrbteIQEybyIISaJfQLZ/+/lJPRzPWAQT4epvzfgv/4MKZI7K83dK7SfTwAooVKFBHiCMok2Cf0iHDt07Kw==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint/node_modules/@aws-sdk/types": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.953.0.tgz", + "integrity": "sha512-M9Iwg9kTyqTErI0vOTVVpcnTHWzS3VplQppy8MuL02EE+mJ0BIwpWfsaAPQW+/XnVpdNpWZTsHcNE29f1+hR8g==", + "dependencies": { + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@aws-sdk/middleware-logger": { "version": "3.930.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.930.0.tgz", @@ -741,6 +1826,36 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.953.0.tgz", + "integrity": "sha512-OrhG1kcQ9zZh3NS3RovR028N0+UndQ957zF1k5HPLeFLwFwQN1uPOufzzPzAyXIIKtR69ARFsQI4mstZS4DMvw==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec/node_modules/@aws-sdk/types": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.953.0.tgz", + "integrity": "sha512-M9Iwg9kTyqTErI0vOTVVpcnTHWzS3VplQppy8MuL02EE+mJ0BIwpWfsaAPQW+/XnVpdNpWZTsHcNE29f1+hR8g==", + "dependencies": { + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@aws-sdk/middleware-user-agent": { "version": "3.932.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.932.0.tgz", @@ -836,6 +1951,128 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.954.0.tgz", + "integrity": "sha512-ypFOt8JK/x6p2hD9Jd5ctStw8h32KdgSBV+eXAMvcMP4DFt7wp6f7StwbjAblehZnPwNolFJE4Hz2F08nh4VdA==", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-format-url": "3.953.0", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/core": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.954.0.tgz", + "integrity": "sha512-5oYO5RP+mvCNXNj8XnF9jZo0EP0LTseYOJVNQYcii1D9DJqzHL3HJWurYh7cXxz7G7eDyvVYA01O9Xpt34TdoA==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@aws-sdk/xml-builder": "3.953.0", + "@smithy/core": "^3.19.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/property-provider": "^4.2.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/signature-v4": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.954.0.tgz", + "integrity": "sha512-274CNmnRjknmfFb2o0Azxic54fnujaA8AYSeRUOho3lN48TVzx85eAFWj2kLgvUJO88pE3jBDPWboKQiQdXeUQ==", + "dependencies": { + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-arn-parser": "3.953.0", + "@smithy/core": "^3.19.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/signature-v4": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-stream": "^4.5.7", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.954.0.tgz", + "integrity": "sha512-GJJbUaSlGrMSRWui3Oz8ByygpQlzDGm195yTKirgGyu4tfYrFr/QWrWT42EUktY/L4Irev1pdHTuLS+AGHO1gw==", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/signature-v4": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/types": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.953.0.tgz", + "integrity": "sha512-M9Iwg9kTyqTErI0vOTVVpcnTHWzS3VplQppy8MuL02EE+mJ0BIwpWfsaAPQW+/XnVpdNpWZTsHcNE29f1+hR8g==", + "dependencies": { + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/util-arn-parser": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.953.0.tgz", + "integrity": "sha512-9hqdKkn4OvYzzaLryq2xnwcrPc8ziY34i9szUdgBfSqEC6pBxbY9/lLXmrgzfwMSL2Z7/v2go4Od0p5eukKLMQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/xml-builder": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.953.0.tgz", + "integrity": "sha512-Zmrj21jQ2OeOJGr9spPiN00aQvXa/WUqRXcTVENhrMt+OFoSOfDFpYhUj9NQ09QmQ8KMWFoWuWW6iKurNqLvAA==", + "dependencies": { + "@smithy/types": "^4.10.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@aws-sdk/signature-v4-multi-region": { "version": "3.932.0", "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.932.0.tgz", @@ -927,7 +2164,38 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/util-endpoints/node_modules/tslib": { + "node_modules/@aws-sdk/util-endpoints/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.953.0.tgz", + "integrity": "sha512-fs70vtTiBhp/T9ss52OuW2LGJqPoNBbd1+wxqh82CMdzkOvCzI3qa/cK8tR0jCFeIjGeiV74lAskImRxu/V4lg==", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@smithy/querystring-builder": "^4.2.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url/node_modules/@aws-sdk/types": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.953.0.tgz", + "integrity": "sha512-M9Iwg9kTyqTErI0vOTVVpcnTHWzS3VplQppy8MuL02EE+mJ0BIwpWfsaAPQW+/XnVpdNpWZTsHcNE29f1+hR8g==", + "dependencies": { + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url/node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" @@ -1011,9 +2279,9 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@aws/lambda-invoke-store": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.0.tgz", - "integrity": "sha512-D1jAmAZQYMoPiacfgNf7AWhg3DFN3Wq/vQv3WINt9znwjzHp2x+WzdJFxxj7xZL7V1U79As6G8f7PorMYWBKsQ==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.2.tgz", + "integrity": "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==", "engines": { "node": ">=18.0.0" } @@ -2618,11 +3886,11 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.5.tgz", - "integrity": "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.6.tgz", + "integrity": "sha512-P7JD4J+wxHMpGxqIg6SHno2tPkZbBUBLbPpR5/T1DEUvw/mEaINBMaPFZNM7lA+ToSCZ36j6nMHa+5kej+fhGg==", "dependencies": { - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -2634,16 +3902,49 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz", + "integrity": "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz", + "integrity": "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==", + "dependencies": { + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@smithy/chunked-blob-reader/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@smithy/config-resolver": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.3.tgz", - "integrity": "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.4.tgz", + "integrity": "sha512-s3U5ChS21DwU54kMmZ0UJumoS5cg0+rGVZvN6f5Lp6EbAVi0ZyP+qDSHdewfmXKUgNK1j3z45JyzulkDukrjAA==", "dependencies": { - "@smithy/node-config-provider": "^4.3.5", - "@smithy/types": "^4.9.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/types": "^4.10.0", "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-endpoints": "^3.2.5", - "@smithy/util-middleware": "^4.2.5", + "@smithy/util-endpoints": "^3.2.6", + "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" }, "engines": { @@ -2656,17 +3957,17 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/core": { - "version": "3.18.4", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.18.4.tgz", - "integrity": "sha512-o5tMqPZILBvvROfC8vC+dSVnWJl9a0u9ax1i1+Bq8515eYjUJqqk5XjjEsDLoeL5dSqGSh6WGdVx1eJ1E/Nwhw==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.19.0.tgz", + "integrity": "sha512-Y9oHXpBcXQgYHOcAEmxjkDilUbSTkgKjoHYed3WaYUH8jngq8lPWDBSpjHblJ9uOgBdy5mh3pzebrScDdYr29w==", "dependencies": { - "@smithy/middleware-serde": "^4.2.6", - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-middleware": "^4.2.5", - "@smithy/util-stream": "^4.5.6", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-stream": "^4.5.7", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" @@ -2681,14 +3982,14 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz", - "integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.6.tgz", + "integrity": "sha512-xBmawExyTzOjbhzkZwg+vVm/khg28kG+rj2sbGlULjFd1jI70sv/cbpaR0Ev4Yfd6CpDUDRMe64cTqR//wAOyA==", "dependencies": { - "@smithy/node-config-provider": "^4.3.5", - "@smithy/property-provider": "^4.2.5", - "@smithy/types": "^4.9.0", - "@smithy/url-parser": "^4.2.5", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/property-provider": "^4.2.6", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", "tslib": "^2.6.2" }, "engines": { @@ -2700,14 +4001,104 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.6.tgz", + "integrity": "sha512-OZfsI+YRG26XZik/jKMMg37acnBSbUiK/8nETW3uM3mLj+0tMmFXdHQw1e5WEd/IHN8BGOh3te91SNDe2o4RHg==", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.10.0", + "@smithy/util-hex-encoding": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.6.tgz", + "integrity": "sha512-6OiaAaEbLB6dEkRbQyNzFSJv5HDvly3Mc6q/qcPd2uS/g3szR8wAIkh7UndAFKfMypNSTuZ6eCBmgCLR5LacTg==", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.6.tgz", + "integrity": "sha512-xP5YXbOVRVN8A4pDnSUkEUsL9fYFU6VNhxo8tgr13YnMbf3Pn4xVr+hSyLVjS1Frfi1Uk03ET5Bwml4+0CeYEw==", + "dependencies": { + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.6.tgz", + "integrity": "sha512-jhH7nJuaOpnTFcuZpWK9dqb6Ge2yGi1okTo0W6wkJrfwAm2vwmO74tF1v07JmrSyHBcKLQATEexclJw9K1Vj7w==", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.6.tgz", + "integrity": "sha512-olIfZ230B64TvPD6b0tPvrEp2eB0FkyL3KvDlqF4RVmIc/kn3orzXnV6DTQdOOW5UU+M5zKY3/BU47X420/oPw==", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz", - "integrity": "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==", + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.7.tgz", + "integrity": "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ==", "dependencies": { - "@smithy/protocol-http": "^5.3.5", - "@smithy/querystring-builder": "^4.2.5", - "@smithy/types": "^4.9.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/querystring-builder": "^4.2.6", + "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" }, @@ -2720,12 +4111,31 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.7.tgz", + "integrity": "sha512-CIbCTGGX5CI7tfewBPSYD9ycp2Vb2GW5xnXD1n7GcO9mu37EN7A6DvCHM9MX7pOeS1adMn5D+1yRwI3eABVbcA==", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.2.0", + "@smithy/chunked-blob-reader-native": "^4.2.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@smithy/hash-node": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.5.tgz", - "integrity": "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.6.tgz", + "integrity": "sha512-k3Dy9VNR37wfMh2/1RHkFf/e0rMyN0pjY0FdyY6ItJRjENYyVPRMwad6ZR1S9HFm6tTuIOd9pqKBmtJ4VHxvxg==", "dependencies": { - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.10.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -2739,12 +4149,30 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.6.tgz", + "integrity": "sha512-+3T8LkH39YIhYHsv/Ec8lF+92nykZpU+XMBvAyXF/uLcTp86pxa5oSJk1vzaRY9N++qgDLYjzJ6OVbtAgDGwfw==", + "dependencies": { + "@smithy/types": "^4.10.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@smithy/invalid-dependency": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.5.tgz", - "integrity": "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.6.tgz", + "integrity": "sha512-E4t/V/q2T46RY21fpfznd1iSLTvCXKNKo4zJ1QuEFN4SE9gKfu2vb6bgq35LpufkQ+SETWIC7ZAf2GGvTlBaMQ==", "dependencies": { - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -2772,13 +4200,31 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@smithy/md5-js": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.6.tgz", + "integrity": "sha512-ZXeh8UmH31JdcNsrQ1o9v1IVuva9JFwxIc6zTMxWX7wcmWvVR7Ai9aUEw5LraNKqdkAsb06clpM2sRH4Iy55Sg==", + "dependencies": { + "@smithy/types": "^4.10.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@smithy/middleware-content-length": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.5.tgz", - "integrity": "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.6.tgz", + "integrity": "sha512-0cjqjyfj+Gls30ntq45SsBtqF3dfJQCeqQPyGz58Pk8OgrAr5YiB7ZvDzjCA94p4r6DCI4qLm7FKobqBjf515w==", "dependencies": { - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -2791,17 +4237,17 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.3.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.11.tgz", - "integrity": "sha512-eJXq9VJzEer1W7EQh3HY2PDJdEcEUnv6sKuNt4eVjyeNWcQFS4KmnY+CKkYOIR6tSqarn6bjjCqg1UB+8UJiPQ==", - "dependencies": { - "@smithy/core": "^3.18.4", - "@smithy/middleware-serde": "^4.2.6", - "@smithy/node-config-provider": "^4.3.5", - "@smithy/shared-ini-file-loader": "^4.4.0", - "@smithy/types": "^4.9.0", - "@smithy/url-parser": "^4.2.5", - "@smithy/util-middleware": "^4.2.5", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.0.tgz", + "integrity": "sha512-M6qWfUNny6NFNy8amrCGIb9TfOMUkHVtg9bHtEFGRgfH7A7AtPpn/fcrToGPjVDK1ECuMVvqGQOXcZxmu9K+7A==", + "dependencies": { + "@smithy/core": "^3.19.0", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" }, "engines": { @@ -2814,17 +4260,17 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.11.tgz", - "integrity": "sha512-EL5OQHvFOKneJVRgzRW4lU7yidSwp/vRJOe542bHgExN3KNThr1rlg0iE4k4SnA+ohC+qlUxoK+smKeAYPzfAQ==", - "dependencies": { - "@smithy/node-config-provider": "^4.3.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/service-error-classification": "^4.2.5", - "@smithy/smithy-client": "^4.9.7", - "@smithy/types": "^4.9.0", - "@smithy/util-middleware": "^4.2.5", - "@smithy/util-retry": "^4.2.5", + "version": "4.4.16", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.16.tgz", + "integrity": "sha512-XPpNhNRzm3vhYm7YCsyw3AtmWggJbg1wNGAoqb7NBYr5XA5isMRv14jgbYyUV6IvbTBFZQdf2QpeW43LrRdStQ==", + "dependencies": { + "@smithy/node-config-provider": "^4.3.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/service-error-classification": "^4.2.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-retry": "^4.2.6", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" }, @@ -2838,12 +4284,12 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.6.tgz", - "integrity": "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==", + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.7.tgz", + "integrity": "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA==", "dependencies": { - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -2856,11 +4302,11 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/middleware-stack": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.5.tgz", - "integrity": "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.6.tgz", + "integrity": "sha512-JSbALU3G+JS4kyBZPqnJ3hxIYwOVRV7r9GNQMS6j5VsQDo5+Es5nddLfr9TQlxZLNHPvKSh+XSB0OuWGfSWFcA==", "dependencies": { - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -2873,13 +4319,13 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/node-config-provider": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.5.tgz", - "integrity": "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.6.tgz", + "integrity": "sha512-fYEyL59Qe82Ha1p97YQTMEQPJYmBS+ux76foqluaTVWoG9Px5J53w6NvXZNE3wP7lIicLDF7Vj1Em18XTX7fsA==", "dependencies": { - "@smithy/property-provider": "^4.2.5", - "@smithy/shared-ini-file-loader": "^4.4.0", - "@smithy/types": "^4.9.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -2892,14 +4338,14 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/node-http-handler": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz", - "integrity": "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==", - "dependencies": { - "@smithy/abort-controller": "^4.2.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/querystring-builder": "^4.2.5", - "@smithy/types": "^4.9.0", + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.6.tgz", + "integrity": "sha512-Gsb9jf4ido5BhPfani4ggyrKDd3ZK+vTFWmUaZeFg5G3E5nhFmqiTzAIbHqmPs1sARuJawDiGMGR/nY+Gw6+aQ==", + "dependencies": { + "@smithy/abort-controller": "^4.2.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/querystring-builder": "^4.2.6", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -2912,11 +4358,11 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/property-provider": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.5.tgz", - "integrity": "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.6.tgz", + "integrity": "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA==", "dependencies": { - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -2929,11 +4375,11 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/protocol-http": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.5.tgz", - "integrity": "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.6.tgz", + "integrity": "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ==", "dependencies": { - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -2946,11 +4392,11 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/querystring-builder": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz", - "integrity": "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.6.tgz", + "integrity": "sha512-MeM9fTAiD3HvoInK/aA8mgJaKQDvm8N0dKy6EiFaCfgpovQr4CaOkJC28XqlSRABM+sHdSQXbC8NZ0DShBMHqg==", "dependencies": { - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.10.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" }, @@ -2964,11 +4410,11 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/querystring-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz", - "integrity": "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.6.tgz", + "integrity": "sha512-YmWxl32SQRw/kIRccSOxzS/Ib8/b5/f9ex0r5PR40jRJg8X1wgM3KrR2In+8zvOGVhRSXgvyQpw9yOSlmfmSnA==", "dependencies": { - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -2981,22 +4427,22 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/service-error-classification": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.5.tgz", - "integrity": "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.6.tgz", + "integrity": "sha512-Q73XBrzJlGTut2nf5RglSntHKgAG0+KiTJdO5QQblLfr4TdliGwIAha1iZIjwisc3rA5ulzqwwsYC6xrclxVQg==", "dependencies": { - "@smithy/types": "^4.9.0" + "@smithy/types": "^4.10.0" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.0.tgz", - "integrity": "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.1.tgz", + "integrity": "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA==", "dependencies": { - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -3009,15 +4455,15 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/signature-v4": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.5.tgz", - "integrity": "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.6.tgz", + "integrity": "sha512-P1TXDHuQMadTMTOBv4oElZMURU4uyEhxhHfn+qOc2iofW9Rd4sZtBGx58Lzk112rIGVEYZT8eUMK4NftpewpRA==", "dependencies": { "@smithy/is-array-buffer": "^4.2.0", - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-middleware": "^4.2.5", + "@smithy/util-middleware": "^4.2.6", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -3032,16 +4478,16 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/smithy-client": { - "version": "4.9.7", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.9.7.tgz", - "integrity": "sha512-pskaE4kg0P9xNQWihfqlTMyxyFR3CH6Sr6keHYghgyqqDXzjl2QJg5lAzuVe/LzZiOzcbcVtxKYi1/fZPt/3DA==", - "dependencies": { - "@smithy/core": "^3.18.4", - "@smithy/middleware-endpoint": "^4.3.11", - "@smithy/middleware-stack": "^4.2.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", - "@smithy/util-stream": "^4.5.6", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.10.1.tgz", + "integrity": "sha512-1ovWdxzYprhq+mWqiGZlt3kF69LJthuQcfY9BIyHx9MywTFKzFapluku1QXoaBB43GCsLDxNqS+1v30ure69AA==", + "dependencies": { + "@smithy/core": "^3.19.0", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/middleware-stack": "^4.2.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "@smithy/util-stream": "^4.5.7", "tslib": "^2.6.2" }, "engines": { @@ -3054,9 +4500,9 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/types": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.9.0.tgz", - "integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.10.0.tgz", + "integrity": "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ==", "dependencies": { "tslib": "^2.6.2" }, @@ -3070,12 +4516,12 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/url-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.5.tgz", - "integrity": "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.6.tgz", + "integrity": "sha512-tVoyzJ2vXp4R3/aeV4EQjBDmCuWxRa8eo3KybL7Xv4wEM16nObYh7H1sNfcuLWHAAAzb0RVyxUz1S3sGj4X+Tg==", "dependencies": { - "@smithy/querystring-parser": "^4.2.5", - "@smithy/types": "^4.9.0", + "@smithy/querystring-parser": "^4.2.6", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -3171,13 +4617,13 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.10", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.10.tgz", - "integrity": "sha512-3iA3JVO1VLrP21FsZZpMCeF93aqP3uIOMvymAT3qHIJz2YlgDeRvNUspFwCNqd/j3qqILQJGtsVQnJZICh/9YA==", + "version": "4.3.15", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.15.tgz", + "integrity": "sha512-LiZQVAg/oO8kueX4c+oMls5njaD2cRLXRfcjlTYjhIqmwHnCwkQO5B3dMQH0c5PACILxGAQf6Mxsq7CjlDc76A==", "dependencies": { - "@smithy/property-provider": "^4.2.5", - "@smithy/smithy-client": "^4.9.7", - "@smithy/types": "^4.9.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -3190,16 +4636,16 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.13", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.13.tgz", - "integrity": "sha512-PTc6IpnpSGASuzZAgyUtaVfOFpU0jBD2mcGwrgDuHf7PlFgt5TIPxCYBDbFQs06jxgeV3kd/d/sok1pzV0nJRg==", - "dependencies": { - "@smithy/config-resolver": "^4.4.3", - "@smithy/credential-provider-imds": "^4.2.5", - "@smithy/node-config-provider": "^4.3.5", - "@smithy/property-provider": "^4.2.5", - "@smithy/smithy-client": "^4.9.7", - "@smithy/types": "^4.9.0", + "version": "4.2.18", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.18.tgz", + "integrity": "sha512-Kw2J+KzYm9C9Z9nY6+W0tEnoZOofstVCMTshli9jhQbQCy64rueGfKzPfuFBnVUqZD9JobxTh2DzHmPkp/Va/Q==", + "dependencies": { + "@smithy/config-resolver": "^4.4.4", + "@smithy/credential-provider-imds": "^4.2.6", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/property-provider": "^4.2.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -3212,12 +4658,12 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/util-endpoints": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.5.tgz", - "integrity": "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.6.tgz", + "integrity": "sha512-v60VNM2+mPvgHCBXEfMCYrQ0RepP6u6xvbAkMenfe4Mi872CqNkJzgcnQL837e8NdeDxBgrWQRTluKq5Lqdhfg==", "dependencies": { - "@smithy/node-config-provider": "^4.3.5", - "@smithy/types": "^4.9.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -3246,11 +4692,11 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/util-middleware": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.5.tgz", - "integrity": "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.6.tgz", + "integrity": "sha512-qrvXUkxBSAFomM3/OEMuDVwjh4wtqK8D2uDZPShzIqOylPst6gor2Cdp6+XrH4dyksAWq/bE2aSDYBTTnj0Rxg==", "dependencies": { - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -3263,12 +4709,12 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/util-retry": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.5.tgz", - "integrity": "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.6.tgz", + "integrity": "sha512-x7CeDQLPQ9cb6xN7fRJEjlP9NyGW/YeXWc4j/RUhg4I+H60F0PEeRc2c/z3rm9zmsdiMFzpV/rT+4UHW6KM1SA==", "dependencies": { - "@smithy/service-error-classification": "^4.2.5", - "@smithy/types": "^4.9.0", + "@smithy/service-error-classification": "^4.2.6", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -3281,13 +4727,13 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/util-stream": { - "version": "4.5.6", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.6.tgz", - "integrity": "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==", + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.7.tgz", + "integrity": "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A==", "dependencies": { - "@smithy/fetch-http-handler": "^5.3.6", - "@smithy/node-http-handler": "^4.4.5", - "@smithy/types": "^4.9.0", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", @@ -3337,12 +4783,12 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@smithy/util-waiter": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.5.tgz", - "integrity": "sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.6.tgz", + "integrity": "sha512-xU9HwUSik9UUCJmm530yvBy0AwlQFICveKmqvaaTukKkXEAhyiBdHtSrhPrH3rH+uz0ykyaE3LdgsX86C6mDCQ==", "dependencies": { - "@smithy/abort-controller": "^4.2.5", - "@smithy/types": "^4.9.0", + "@smithy/abort-controller": "^4.2.6", + "@smithy/types": "^4.10.0", "tslib": "^2.6.2" }, "engines": { @@ -3582,6 +5028,15 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, + "node_modules/@types/multer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", + "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "16.18.126", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.126.tgz", @@ -5692,7 +7147,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, "engines": { "node": ">=0.8.x" } @@ -9275,6 +10729,28 @@ "node": ">= 0.8" } }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/stream-browserify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", diff --git a/package.json b/package.json index 783e82b..96a81e6 100644 --- a/package.json +++ b/package.json @@ -26,11 +26,14 @@ "db:migrate": "npm run typeorm migration:run -- -d src/database/datasource.ts", "db:revert": "npm run typeorm migration:revert -- -d src/database/datasource.ts", "db:drop": "npm run typeorm schema:drop -- -d src/database/datasource.ts", - "db:migrate:prod": "node ./node_modules/typeorm/cli migration:run -- -d ./dist/database/datasource.js" + "db:migrate:prod": "node ./node_modules/typeorm/cli migration:run -d ./dist/database/datasource.js" }, "dependencies": { + "@aws-sdk/client-s3": "^3.954.0", "@aws-sdk/client-ses": "^3.933.0", "@aws-sdk/client-sesv2": "^3.933.0", + "@aws-sdk/lib-storage": "^3.954.0", + "@aws-sdk/s3-request-presigner": "^3.954.0", "@nestjs/common": "^9.0.0", "@nestjs/config": "^4.0.2", "@nestjs/core": "^9.0.0", @@ -53,6 +56,7 @@ "@nestjs/testing": "^9.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.8", + "@types/multer": "^2.0.0", "@types/node": "^16.0.0", "@types/supertest": "^2.0.11", "@types/uuid": "^11.0.0", diff --git a/schemas/get-birth-certificate.json b/schemas/get-birth-certificate.json index 9ab9abe..b27c524 100644 --- a/schemas/get-birth-certificate.json +++ b/schemas/get-birth-certificate.json @@ -1,265 +1,518 @@ { - "id": "get-birth-certificate", - "name": "Get Birth Certificate", - "description": "Apply for a copy of your birth certificate or someone else's birth certificate", - "fields": [ - { - "name": "applicant", - "type": "object", - "required": true, - "fields": [ - { - "name": "title", - "type": "string", - "label": "Title", - "required": true, - "validations": { - "regex": "^(mr|ms|mrs)$", - "message": "Must select a valid title" - } - }, - { - "name": "firstName", - "type": "string", - "label": "First name", - "required": true, - "validations": { - "min": 1, - "max": 100, - "message": "First name is required" - } - }, - { - "name": "middleName", - "type": "string", - "label": "Middle name", - "required": false, - "validations": { - "max": 100 - } - }, - { - "name": "lastName", - "type": "string", - "label": "Last name", - "required": true, - "validations": { - "min": 1, - "max": 100, - "message": "Last name is required" - } - }, - { - "name": "addressLine1", - "type": "string", - "label": "Address Line 1", - "required": true, - "validations": { - "min": 5, - "max": 200, - "message": "Address must be at least 5 characters" - } - }, - { - "name": "addressLine2", - "type": "string", - "label": "Address Line 2", - "required": false, - "validations": { - "max": 200 - } - }, - { - "name": "parish", - "type": "string", - "label": "Parish", - "required": true, - "validations": { - "regex": "^(christ-church|st-andrew|st-george|st-james|st-john|st-joseph|st-lucy|st-michael|st-peter|st-philip|st-thomas)$", - "message": "Must select a valid parish" - } - }, - { - "name": "postalCode", - "type": "string", - "label": "Postal Code", - "required": false, - "validations": { - "regex": "^BB\\d{5}$", - "message": "Enter a valid postal code (e.g., BB17004)" - } - }, - { - "name": "idNumber", - "type": "string", - "label": "National Identification (ID) Number", - "required": false, - "validations": { - "min": 2, - "message": "ID Number must be at least 2 characters" - } - }, - { - "name": "passportNumber", - "type": "string", - "label": "Passport Number", - "required": false, - "validations": { - "message": "Passport number must be at least 6 characters" - } - } - ] - }, - { - "name": "applyingForYourself", - "type": "string", - "label": "Are you applying for your own birth certificate?", - "required": true, - "validations": { - "regex": "^(yes|no)$", - "message": "Must select an option" - } - }, - { - "name": "birthDetails", - "type": "object", - "required": true, - "fields": [ - { - "name": "dateOfBirth", - "type": "date", - "label": "Date of birth", - "required": true, - "validations": { - "regex": "^\\d{4}-\\d{2}-\\d{2}$", - "message": "Date of birth is required and must be in YYYY-MM-DD format" - } - }, - { - "name": "placeOfBirth", - "type": "string", - "label": "Place of birth", - "required": true, - "validations": { - "min": 2, - "max": 200, - "message": "Place of birth must be at least 2 characters" - } - }, - { - "name": "placeOfBaptism", - "type": "string", - "label": "Place of baptism", - "required": true, - "validations": { - "min": 2, - "max": 200, - "message": "Place of baptism must be at least 2 characters" - } - } - ] - }, - { - "name": "parents", - "type": "object", - "required": true, - "fields": [ - { - "name": "father", - "type": "object", - "required": true, - "fields": [ - { - "name": "firstName", - "type": "string", - "label": "Father's first name", - "required": true, - "validations": { - "min": 1, - "max": 100, - "message": "First name is required" - } - }, - { - "name": "lastName", - "type": "string", - "label": "Father's last name", - "required": true, - "validations": { - "min": 1, - "max": 100, - "message": "Last name is required" - } - } - ] - }, - { - "name": "mother", - "type": "object", - "required": true, - "fields": [ - { - "name": "firstName", - "type": "string", - "label": "Mother's first name", - "required": true, - "validations": { - "min": 1, - "max": 100, - "message": "First name is required" - } - }, - { - "name": "lastName", - "type": "string", - "label": "Mother's last name", - "required": true, - "validations": { - "min": 1, - "max": 100, - "message": "Last name is required" - } - } - ] - } - ] - }, - { - "name": "order", - "type": "object", - "required": true, - "fields": [ - { - "name": "numberOfCopies", - "type": "number", - "label": "Number of copies", - "required": true, - "validations": { - "message": "You must order at least 1 copy and maximum 10 copies" - } - } - ] - } - ], - "processors": [ - { - "type": "payment", - "config": { - "provider": "ezpay", - "paymentCode": "{{db:get-birth-certificate:payment_code}}", - "amount": 25.0, - "description": "Processing Fee", - "required": true, - "timing": "after_validation" - } - }, - { - "type": "email", - "config": { - "to": "{{db:get-birth-certificate:admin_email}}", - "subject": "New Birth Certificate Application - {{formData.applicant.firstName}} {{formData.applicant.lastName}}", - "template": "birth-certificate" - } - } - ] + "id": "get-birth-certificate", + "name": "Get Birth Certificate", + "description": "Apply for a copy of your birth certificate or someone else's birth certificate", + "fields": [ + { + "name": "applicant", + "type": "object", + "required": true, + "fields": [ + { + "name": "title", + "type": "string", + "label": "Title", + "required": true, + "validations": { + "regex": "^(mr|ms|mrs)$", + "message": "Must select a valid title" + } + }, + { + "name": "firstName", + "type": "string", + "label": "First name", + "required": true, + "validations": { + "min": 1, + "max": 100, + "message": "First name is required" + } + }, + { + "name": "middleName", + "type": "string", + "label": "Middle name", + "required": false, + "validations": { + "max": 100 + } + }, + { + "name": "lastName", + "type": "string", + "label": "Last name", + "required": true, + "validations": { + "min": 1, + "max": 100, + "message": "Last name is required" + } + }, + { + "name": "addressLine1", + "type": "string", + "label": "Address Line 1", + "required": true, + "validations": { + "min": 5, + "max": 200, + "message": "Address must be at least 5 characters" + } + }, + { + "name": "addressLine2", + "type": "string", + "label": "Address Line 2", + "required": false, + "validations": { + "max": 200 + } + }, + { + "name": "parish", + "type": "string", + "label": "Parish", + "required": true, + "validations": { + "regex": "^(christ-church|st-andrew|st-george|st-james|st-john|st-joseph|st-lucy|st-michael|st-peter|st-philip|st-thomas)$", + "message": "Must select a valid parish" + } + }, + { + "name": "postalCode", + "type": "string", + "label": "Postal Code", + "required": false, + "validations": { + "regex": "^BB\\d{5}$", + "message": "Enter a valid postal code (e.g., BB17004)" + } + }, + { + "name": "idNumber", + "type": "string", + "label": "National Identification (ID) Number", + "required": false, + "validations": { + "min": 2, + "message": "ID Number must be at least 2 characters" + } + }, + { + "name": "passportNumber", + "type": "string", + "label": "Passport Number", + "required": false, + "validations": { + "message": "Passport number must be at least 6 characters" + } + } + ] + }, + { + "name": "applyingForYourself", + "type": "string", + "label": "Are you applying for your own birth certificate?", + "required": true, + "validations": { + "regex": "^(yes|no)$", + "message": "Must select an option" + } + }, + { + "name": "relationshipToPerson", + "type": "string", + "label": "What is your relationship to the person?", + "required": false, + "validations": { + "message": "" + } + }, + { + "name": "relationshipOtherDescription", + "type": "string", + "label": "Please describe your relationship", + "required": false, + "validations": { + "message": "" + } + }, + { + "name": "reasonForOrderingCertificate", + "type": "string", + "label": "Tell us why you're ordering a birth certificate", + "required": false, + "validations": { + "message": "" + } + }, + { + "name": "personDeceased", + "type": "string", + "label": "Is the person deceased?", + "required": false, + "validations": { + "message": "" + } + }, + { + "name": "dateOfDeath", + "type": "string", + "label": "Date of death", + "required": false, + "validations": { + "message": "" + } + }, + { + "name": "person", + "type": "object", + "required": false, + "fields": [ + { + "name": "firstName", + "type": "string", + "label": "First name", + "required": false, + "validations": { + "min": 1, + "max": 100, + "message": "First name is required" + } + }, + { + "name": "middleName", + "type": "string", + "label": "Middle name", + "required": false, + "validations": { + "max": 100 + } + }, + { + "name": "lastName", + "type": "string", + "label": "Last name", + "required": false, + "validations": { + "min": 1, + "max": 100, + "message": "Last name is required" + } + }, + { + "name": "addressLine1", + "type": "string", + "label": "Address Line 1", + "required": false, + "validations": { + "min": 5, + "max": 200, + "message": "Address must be at least 5 characters" + } + }, + { + "name": "addressLine2", + "type": "string", + "label": "Address Line 2", + "required": false, + "validations": { + "max": 200 + } + }, + { + "name": "parish", + "type": "string", + "label": "Parish", + "required": false, + "validations": { + "regex": "^(christ-church|st-andrew|st-george|st-james|st-john|st-joseph|st-lucy|st-michael|st-peter|st-philip|st-thomas)$", + "message": "Must select a valid parish" + } + }, + { + "name": "postalCode", + "type": "string", + "label": "Postal Code", + "required": false, + "validations": { + "regex": "^BB\\d{5}$", + "message": "Enter a valid postal code (e.g., BB17004)" + } + }, + { + "name": "idNumber", + "type": "string", + "label": "National Identification (ID) Number", + "required": false, + "validations": { + "min": 2, + "message": "ID Number must be at least 2 characters" + } + }, + { + "name": "passportNumber", + "type": "string", + "label": "Passport Number", + "required": false, + "validations": { + "message": "Passport number must be at least 6 characters" + } + } + ] + }, + { + "name": "birthDetails", + "type": "object", + "required": false, + "fields": [ + { + "name": "dateOfBirth", + "type": "date", + "label": "Date of birth", + "required": false, + "validations": { + "regex": "^\\d{4}-\\d{2}-\\d{2}$", + "message": "Date of birth is required and must be in YYYY-MM-DD format" + } + }, + { + "name": "placeOfBirth", + "type": "string", + "label": "Place of birth", + "required": false, + "validations": { + "min": 2, + "max": 200, + "message": "Place of birth must be at least 2 characters" + } + }, + { + "name": "placeOfBaptism", + "type": "string", + "label": "Place of baptism", + "required": false, + "validations": { + "min": 2, + "max": 200, + "message": "Place of baptism must be at least 2 characters" + } + } + ] + }, + { + "name": "parents", + "type": "object", + "required": false, + "fields": [ + { + "name": "father", + "type": "object", + "required": false, + "fields": [ + { + "name": "firstName", + "type": "string", + "label": "Father's first name", + "required": false, + "validations": { + "min": 1, + "max": 100, + "message": "First name is required" + } + }, + { + "name": "middleName", + "type": "string", + "label": "Father's middle name", + "required": false, + "validations": { + "max": 100, + "message": "Middle name is required" + } + }, + { + "name": "lastName", + "type": "string", + "label": "Father's last name", + "required": false, + "validations": { + "min": 1, + "max": 100, + "message": "Last name is required" + } + } + ] + }, + { + "name": "mother", + "type": "object", + "required": false, + "fields": [ + { + "name": "firstName", + "type": "string", + "label": "Mother's first name", + "required": false, + "validations": { + "min": 1, + "max": 100, + "message": "First name is required" + } + }, + { + "name": "middleName", + "type": "string", + "label": "Mother's middle name", + "required": false, + "validations": { + "max": 100, + "message": "Middle name is required" + } + }, + { + "name": "lastName", + "type": "string", + "label": "Mother's last name", + "required": false, + "validations": { + "min": 1, + "max": 100, + "message": "Last name is required" + } + } + ] + } + ] + }, + { + "name": "parentsOther", + "type": "object", + "required": false, + "fields": [ + { + "name": "father", + "type": "object", + "required": false, + "fields": [ + { + "name": "firstName", + "type": "string", + "label": "Father's first name", + "required": false, + "validations": { + "min": 1, + "max": 100, + "message": "First name is required" + } + }, + { + "name": "middleName", + "type": "string", + "label": "Father's middle name", + "required": false, + "validations": { + "max": 100, + "message": "Middle name is required" + } + }, + { + "name": "lastName", + "type": "string", + "label": "Father's last name", + "required": false, + "validations": { + "min": 1, + "max": 100, + "message": "Last name is required" + } + } + ] + }, + { + "name": "mother", + "type": "object", + "required": false, + "fields": [ + { + "name": "firstName", + "type": "string", + "label": "Mother's first name", + "required": false, + "validations": { + "min": 1, + "max": 100, + "message": "First name is required" + } + }, + { + "name": "middleName", + "type": "string", + "label": "Mother's middle name", + "required": false, + "validations": { + "max": 100, + "message": "Middle name is required" + } + }, + { + "name": "lastName", + "type": "string", + "label": "Mother's last name", + "required": false, + "validations": { + "min": 1, + "max": 100, + "message": "Last name is required" + } + } + ] + } + ] + }, + { + "name": "order", + "type": "object", + "required": true, + "fields": [ + { + "name": "numberOfCopies", + "type": "number", + "label": "Number of copies", + "required": true, + "validations": { + "min": 1, + "max": 10, + "message": "You must order at least 1 copy and maximum 10 copies" + } + } + ] + } + ], + "processors": [ + { + "type": "payment", + "config": { + "provider": "ezpay", + "department": "revenue_authority", + "paymentCode": "{{db:get-birth-certificate:payment_code}}", + "amount": "{{formData.order.numberOfCopies * db:get-birth-certificate:payment_amount}}", + "description": "Birth Certificate Processing Fee (per copy)", + "required": true, + "timing": "after_validation", + "responseData": { + "include": ["order.numberOfCopies"] + } + } + }, + { + "type": "email", + "config": { + "to": "{{db:get-birth-certificate:admin_email}}", + "subject": "New Birth Certificate Application - {{formData.applicant.firstName}} {{formData.applicant.lastName}}", + "template": "birth-certificate" + } + } + ] } diff --git a/schemas/get-death-certificate.json b/schemas/get-death-certificate.json new file mode 100644 index 0000000..01b1d8a --- /dev/null +++ b/schemas/get-death-certificate.json @@ -0,0 +1,293 @@ +{ + "id": "get-death-certificate", + "name": "Get Death Certificate", + "description": "Apply for a copy of your death certificate or someone else's death certificate", + "fields": [ + { + "name": "applicant", + "type": "object", + "required": true, + "fields": [ + { + "name": "title", + "type": "string", + "label": "Title", + "required": true, + "validations": { + "regex": "^(mr|ms|mrs)$", + "message": "Must select a valid title" + } + }, + { + "name": "firstName", + "type": "string", + "label": "First name", + "required": true, + "validations": { + "min": 1, + "max": 100, + "message": "First name is required" + } + }, + { + "name": "middleName", + "type": "string", + "label": "Middle name", + "required": false, + "validations": { + "max": 100 + } + }, + { + "name": "lastName", + "type": "string", + "label": "Last name", + "required": true, + "validations": { + "min": 1, + "max": 100, + "message": "Last name is required" + } + }, + { + "name": "addressLine1", + "type": "string", + "label": "Address Line 1", + "required": true, + "validations": { + "min": 5, + "max": 200, + "message": "Address must be at least 5 characters" + } + }, + { + "name": "addressLine2", + "type": "string", + "label": "Address Line 2", + "required": false, + "validations": { + "max": 200 + } + }, + { + "name": "parish", + "type": "string", + "label": "Parish", + "required": true, + "validations": { + "regex": "^(christ-church|st-andrew|st-george|st-james|st-john|st-joseph|st-lucy|st-michael|st-peter|st-philip|st-thomas)$", + "message": "Must select a valid parish" + } + }, + { + "name": "postalCode", + "type": "string", + "label": "Postal Code", + "required": false, + "validations": { + "regex": "^BB\\d{5}$", + "message": "Enter a valid postal code (e.g., BB17004)" + } + }, + { + "name": "idNumber", + "type": "string", + "label": "National Identification (ID) Number", + "required": false, + "validations": { + "min": 2, + "message": "ID Number must be at least 2 characters" + } + }, + { + "name": "passportNumber", + "type": "string", + "label": "Passport Number", + "required": false, + "validations": { + "message": "Passport number must be at least 6 characters" + } + }, + { + "name": "email", + "type": "email", + "label": "Email Address", + "required": true + }, + { + "name": "telephoneNumber", + "type": "string", + "label": "Telephone Number", + "required": true, + "validations": { + "regex": "^\\+?[0-9]{10,15}$", + "message": "Telephone number must be 10-15 digits" + } + } + ] + }, + { + "name": "relationship", + "type": "string", + "label": "Tell us your relationship with the deceased", + "required": true, + "validations": { + "max": 100 + } + }, + { + "name": "reasonForRequest", + "type": "string", + "label": "Tell us about why you need this certificate", + "required": true, + "validations": { + "min": 1, + "max": 500 + } + }, + { + "name": "deceased", + "type": "object", + "required": true, + "fields": [ + { + "name": "firstName", + "type": "string", + "label": "First name", + "required": true, + "validations": { + "min": 1, + "max": 100, + "message": "First name is required" + } + }, + { + "name": "middleName", + "type": "string", + "label": "Middle name", + "required": false, + "validations": { + "max": 100 + } + }, + { + "name": "lastName", + "type": "string", + "label": "Last name", + "required": true, + "validations": { + "min": 1, + "max": 100, + "message": "Last name is required" + } + }, + { + "name": "knownDateOfDeath", + "type": "string", + "label": "Do you know the date of death?", + "required": true, + "validations": { + "regex": "^(yes|no)$", + "message": "Must select an option" + } + }, + { + "name": "dateOfDeath", + "type": "date", + "label": "Date of death", + "required": false, + "validations": { + "regex": "^\\d{4}-\\d{2}-\\d{2}$", + "message": "Date of death is required and must be in YYYY-MM-DD format" + } + }, + { + "name": "estimatedDateOfDeath", + "type": "string", + "label": "Estimated date of death", + "required": false, + "validations": { + "max": 100, + "message": "Estimate is required" + } + }, + { + "name": "idNumber", + "type": "string", + "label": "National Identification (ID) Number", + "required": false, + "validations": { + "min": 2, + "message": "ID Number must be at least 2 characters" + } + }, + { + "name": "placeOfDeath", + "type": "string", + "label": "Place of death", + "required": true, + "validations": { + "min": 2, + "max": 200, + "message": "Place of death must be at least 2 characters" + } + }, + { + "name": "causeOfDeath", + "type": "string", + "label": "Cause of death", + "required": true, + "validations": { + "min": 2, + "max": 200, + "message": "Cause of death must be at least 2 characters" + } + } + ] + }, + + { + "name": "order", + "type": "object", + "required": true, + "fields": [ + { + "name": "numberOfCopies", + "type": "number", + "label": "Number of copies", + "required": true, + "validations": { + "min": 1, + "max": 10, + "message": "You must order at least 1 copy and maximum 10 copies" + } + } + ] + } + ], + "processors": [ + { + "type": "payment", + "config": { + "provider": "ezpay", + "department": "revenue_authority", + "paymentCode": "{{db:get-death-certificate:payment_code}}", + "amount": "{{formData.order.numberOfCopies * db:get-death-certificate:payment_amount}}", + "description": "Death Certificate Processing Fee (per copy)", + "required": true, + "timing": "after_validation", + "responseData": { + "include": ["order.numberOfCopies"] + } + } + }, + { + "type": "email", + "config": { + "to": "{{db:get-death-certificate:admin_email}}", + "subject": "New Death Certificate Application - {{formData.applicant.firstName}} {{formData.applicant.lastName}}", + "template": "death-certificate" + } + } + ] +} diff --git a/schemas/get-marriage-certificate.json b/schemas/get-marriage-certificate.json new file mode 100644 index 0000000..ce80a9a --- /dev/null +++ b/schemas/get-marriage-certificate.json @@ -0,0 +1,326 @@ +{ + "id": "get-marriage-certificate", + "name": "Get Marriage Certificate", + "description": "Apply for a copy of your marriage certificate or someone else's marriage certificate", + "fields": [ + { + "name": "applicant", + "type": "object", + "required": true, + "fields": [ + { + "name": "title", + "type": "string", + "label": "Title", + "required": true, + "validations": { + "regex": "^(mr|ms|mrs)$", + "message": "Must select a valid title" + } + }, + { + "name": "firstName", + "type": "string", + "label": "First name", + "required": true, + "validations": { + "min": 1, + "max": 100, + "message": "First name is required" + } + }, + { + "name": "middleName", + "type": "string", + "label": "Middle name", + "required": false, + "validations": { + "max": 100 + } + }, + { + "name": "lastName", + "type": "string", + "label": "Last name", + "required": true, + "validations": { + "min": 1, + "max": 100, + "message": "Last name is required" + } + }, + { + "name": "addressLine1", + "type": "string", + "label": "Address Line 1", + "required": true, + "validations": { + "min": 5, + "max": 200, + "message": "Address must be at least 5 characters" + } + }, + { + "name": "addressLine2", + "type": "string", + "label": "Address Line 2", + "required": false, + "validations": { + "max": 200 + } + }, + { + "name": "parish", + "type": "string", + "label": "Parish", + "required": true, + "validations": { + "regex": "^(christ-church|st-andrew|st-george|st-james|st-john|st-joseph|st-lucy|st-michael|st-peter|st-philip|st-thomas)$", + "message": "Must select a valid parish" + } + }, + { + "name": "postalCode", + "type": "string", + "label": "Postal Code", + "required": false, + "validations": { + "regex": "^BB\\d{5}$", + "message": "Enter a valid postal code (e.g., BB17004)" + } + }, + { + "name": "idNumber", + "type": "string", + "label": "National Identification (ID) Number", + "required": false, + "validations": { + "min": 2, + "message": "ID Number must be at least 2 characters" + } + }, + { + "name": "passportNumber", + "type": "string", + "label": "Passport Number", + "required": false, + "validations": { + "message": "Passport number must be at least 6 characters" + } + } + ] + }, + { + "name": "applyingForYourself", + "type": "string", + "label": "Are you applying for your own marriage certificate?", + "required": true, + "validations": { + "regex": "^(yes|no)$", + "message": "Must select an option" + } + }, + { + "name": "husband", + "type": "object", + "required": true, + "fields": [ + { + "name": "firstName", + "type": "string", + "required": true, + "validations": { + "min": 1, + "max": 100 + } + }, + { + "name": "middleName", + "type": "string", + "required": false, + "validations": { + "max": 200 + } + }, + { + "name": "lastName", + "type": "string", + "required": true, + "validations": { + "min": 1, + "max": 100 + } + }, + { + "name": "idNumber", + "type": "string", + "label": "National Identification (ID) Number", + "required": false, + "validations": { + "min": 2, + "message": "ID Number must be at least 2 characters" + } + }, + { + "name": "passportNumber", + "type": "string", + "label": "Passport Number", + "required": false, + "validations": { + "message": "Passport number must be at least 6 characters" + } + } + ] + }, + { + "name": "wife", + "type": "object", + "required": true, + "fields": [ + { + "name": "firstName", + "type": "string", + "required": true, + "validations": { + "min": 1, + "max": 100 + } + }, + { + "name": "middleName", + "type": "string", + "required": false, + "validations": { + "max": 200 + } + }, + { + "name": "maidenName", + "type": "string", + "required": true, + "validations": { + "min": 1, + "max": 100 + } + }, + { + "name": "idNumber", + "type": "string", + "label": "National Identification (ID) Number", + "required": false, + "validations": { + "min": 2, + "message": "ID Number must be at least 2 characters" + } + }, + { + "name": "passportNumber", + "type": "string", + "label": "Passport Number", + "required": false, + "validations": { + "message": "Passport number must be at least 6 characters" + } + } + ] + }, + { + "name": "marriageDetails", + "type": "object", + "required": true, + "fields": [ + { + "name": "dateOfMarriage", + "type": "date", + "label": "Date of marriage", + "required": true, + "validations": { + "regex": "^\\d{4}-\\d{2}-\\d{2}$", + "message": "Date of marriage is required and must be in YYYY-MM-DD format" + } + }, + { + "name": "placeOfMarriage", + "type": "string", + "label": "Place of marriage", + "required": true, + "validations": { + "min": 2, + "max": 200, + "message": "Place of marriage must be at least 2 characters" + } + } + ] + }, + { + "name": "reason", + "type": "object", + "label": "Reason for Requesting a Marriage Certificate", + "required": false, + "fields": [ + { + "name": "relationshipToMarriedPersons", + "type": "string", + "label": "Relationship", + "required": false, + "validations": { + "max": 100 + } + }, + { + "name": "explanationForRequestingCertificate", + "type": "string", + "label": "Why are you requesting the marriage certificate?", + "required": false, + "validations": { + "min": 1, + "max": 500 + } + } + ] + }, + { + "name": "order", + "type": "object", + "required": true, + "fields": [ + { + "name": "numberOfCopies", + "type": "number", + "label": "Number of copies", + "required": true, + "validations": { + "min": 1, + "max": 10, + "message": "You must order at least 1 copy and maximum 10 copies" + } + } + ] + } + ], + "processors": [ + { + "type": "payment", + "config": { + "provider": "ezpay", + "department": "revenue_authority", + "paymentCode": "{{db:get-marriage-certificate:payment_code}}", + "amount": "{{formData.order.numberOfCopies * db:get-marriage-certificate:payment_amount}}", + "description": "Marriage Certificate Processing Fee (per copy)", + "required": true, + "timing": "after_validation", + "responseData": { + "include": ["order.numberOfCopies"] + } + } + }, + { + "type": "email", + "config": { + "to": "{{db:get-marriage-certificate:admin_email}}", + "subject": "New Marriage Certificate Application - {{formData.applicant.firstName}} {{formData.applicant.lastName}}", + "template": "marriage-certificate" + } + } + ] +} diff --git a/schemas/primary-school-textbook-grant.json b/schemas/primary-school-textbook-grant.json index 9010b71..a81cd0e 100644 --- a/schemas/primary-school-textbook-grant.json +++ b/schemas/primary-school-textbook-grant.json @@ -228,17 +228,6 @@ } ], "processors": [ - { - "type": "payment", - "config": { - "provider": "ezpay", - "paymentCode": "{{db:primary-school-textbook-grant:payment_code}}", - "amount": 25.0, - "description": "Processing Fee", - "required": true, - "timing": "after_validation" - } - }, { "type": "email", "config": { diff --git a/src/app.module.ts b/src/app.module.ts index 09812ca..4f8c86a 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -5,6 +5,7 @@ import { FormsModule } from './forms/forms.module'; import { ValidationModule } from './validation/validation.module'; import { ProcessorsModule } from './processors/processors.module'; import { EmailModule } from './email/email.module'; +import { FileModule } from './file/file.module'; @Module({ imports: [ @@ -14,6 +15,7 @@ import { EmailModule } from './email/email.module'; ValidationModule, ProcessorsModule, EmailModule, + FileModule, ], controllers: [], providers: [], diff --git a/src/common/decorators/api-file.decorator.ts b/src/common/decorators/api-file.decorator.ts new file mode 100644 index 0000000..30311f4 --- /dev/null +++ b/src/common/decorators/api-file.decorator.ts @@ -0,0 +1,6 @@ +import { applyDecorators, UseInterceptors } from '@nestjs/common'; +import { FileInterceptor } from '@nestjs/platform-express'; + +export function ApiFile(fieldName = 'file') { + return applyDecorators(UseInterceptors(FileInterceptor(fieldName))); +} diff --git a/src/common/decorators/index.ts b/src/common/decorators/index.ts new file mode 100644 index 0000000..73ad553 --- /dev/null +++ b/src/common/decorators/index.ts @@ -0,0 +1 @@ +export * from './api-file.decorator'; diff --git a/src/common/dto/attachment.dto.ts b/src/common/dto/attachment.dto.ts new file mode 100644 index 0000000..472931b --- /dev/null +++ b/src/common/dto/attachment.dto.ts @@ -0,0 +1,7 @@ +export class AttachmentDTO { + url: string; + key: string; + type: string; + name: string; + size: number; +} diff --git a/src/common/dto/index.ts b/src/common/dto/index.ts index f77a035..b6b0b6c 100644 --- a/src/common/dto/index.ts +++ b/src/common/dto/index.ts @@ -1 +1,2 @@ export * from './api-response.dto'; +export * from './attachment.dto'; diff --git a/src/common/pipes/index.ts b/src/common/pipes/index.ts index dbc04f8..30ce9f6 100644 --- a/src/common/pipes/index.ts +++ b/src/common/pipes/index.ts @@ -1 +1,2 @@ export * from './validation.pipe'; +export * from './parse-file.pipe'; diff --git a/src/common/pipes/parse-file.pipe.ts b/src/common/pipes/parse-file.pipe.ts new file mode 100644 index 0000000..79a32a3 --- /dev/null +++ b/src/common/pipes/parse-file.pipe.ts @@ -0,0 +1,19 @@ +import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common'; + +@Injectable() +export class ParseFile implements PipeTransform { + transform(file: Express.Multer.File): Express.Multer.File { + if (!file) { + throw new BadRequestException('File is required'); + } + + const maxSize = 10 * 1024 * 1024; // 10MB + if (file.size > maxSize) { + throw new BadRequestException( + `File size exceeds maximum limit of ${maxSize / 1024 / 1024}MB`, + ); + } + + return file; + } +} diff --git a/src/config/configuration.ts b/src/config/configuration.ts index 70e7faa..221ede6 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -22,6 +22,11 @@ export default () => ({ tagKey: process.env.SES_TAG_KEY || 'ses:configuration-set', tagValue: process.env.SES_TAG_VALUE || 'prod', }, + s3: { + bucketName: process.env.BUCKET_NAME, + bucketRegion: + process.env.BUCKET_REGION || process.env.AWS_REGION || 'us-east-1', + }, }, email: { templatesDir: process.env.EMAIL_TEMPLATES_DIR || 'src/email/templates', @@ -30,8 +35,24 @@ export default () => ({ schemasDir: process.env.FORM_SCHEMAS_DIR || 'schemas', }, ezpay: { - apiKey: process.env.EZPAY_API_KEY || 'HWqgTn5EXIHLAzVjXtGpB2mIjgQgj0Ql', + apiKey: process.env.EZPAY_API_KEY || 'HWqgTn5EXIHLAzVjXtGpB2mIjgQgj0Ql', // Default API key for backward compatibility baseUrl: process.env.EZPAY_BASE_URL || 'https://test.ezpay.gov.bb', webhookSecret: process.env.EZPAY_WEBHOOK_SECRET, + // Department-specific API keys + departmentApiKeys: { + ministry_of_youth: process.env.EZPAY_MINISTRY_OF_YOUTH_API_KEY, + town_and_country: process.env.EZPAY_TOWN_AND_COUNTRY_API_KEY, + licensing_authority: process.env.EZPAY_LICENSING_AUTHORITY_API_KEY, + immigration_department: process.env.EZPAY_IMMIGRATION_DEPARTMENT_API_KEY, + ministry_of_transport_and_works: + process.env.EZPAY_MINISTRY_OF_TRANSPORT_AND_WORKS_API_KEY, + government_electrical_engineering_department: + process.env.EZPAY_GOVERNMENT_ELECTRICAL_ENGINEERING_DEPARTMENT_API_KEY, + revenue_authority: process.env.EZPAY_REVENUE_AUTHORITY_API_KEY, + ministry_of_agriculture: + process.env.EZPAY_MINISTRY_OF_AGRICULTURE_API_KEY, + oag_registration: process.env.EZPAY_OAG_REGISTRATION_API_KEY, + default: process.env.EZPAY_API_KEY || 'HWqgTn5EXIHLAzVjXtGpB2mIjgQgj0Ql', + }, }, }); diff --git a/src/email/templates/death-certificate.hbs b/src/email/templates/death-certificate.hbs new file mode 100644 index 0000000..07e22a6 --- /dev/null +++ b/src/email/templates/death-certificate.hbs @@ -0,0 +1,208 @@ + + + + + + +
+

New Death Certificate Application

+ +
+ Application Submitted: + {{processedAt}}
+ Application Type: + {{#if (eq applyingForYourself 'yes')}} + Self Application + {{else}} + Third Party Application + {{/if}} +
+ + +
+

Applicant Information

+ + + {{#if applicant.title}} + + + + + {{/if}} + {{#if applicant.firstName}} + + + + + {{/if}} + {{#if applicant.middleName}} + + + + + {{/if}} + {{#if applicant.lastName}} + + + + + {{/if}} + {{#if applicant.email}} + + + + + {{/if}} + {{#if applicant.phoneNumber}} + + + + + {{/if}} + +
Title:{{#if (eq applicant.title 'mr')}}Mr{{else if + (eq applicant.title 'ms') + }}Ms{{else if + (eq applicant.title 'mrs') + }}Mrs{{else}}{{applicant.title}}{{/if}}
First Name:{{applicant.firstName}}
Middle Name:{{applicant.middleName}}
Last Name:{{applicant.lastName}}
Email:{{applicant.email}}
Phone Number:{{applicant.phoneNumber}}
+ +

Address

+ + + {{#if applicant.addressLine1}} + + + + + {{/if}} + {{#if applicant.addressLine2}} + + + + + {{/if}} + {{#if applicant.parish}} + + + + + {{/if}} + {{#if applicant.postalCode}} + + + + + {{/if}} + +
Address Line 1:{{applicant.addressLine1}}
Address Line 2:{{applicant.addressLine2}}
Parish:{{applicant.parish}}
Postal Code:{{applicant.postalCode}}
+ +

Identification

+ + + {{#if applicant.usePassportInstead}} + {{#if applicant.passportNumber}} + + + + + {{/if}} + {{else}} + {{#if applicant.idNumber}} + + + + + {{/if}} + {{/if}} + +
Passport Number:{{applicant.passportNumber}}
National ID Number:{{applicant.idNumber}}
+
+ + +
+

Deceased Information

+ + + {{#if deceased.firstName}} + + + + + {{/if}} + {{#if deceased.middleName}} + + + + + {{/if}} + {{#if deceased.lastName}} + + + + + {{/if}} + {{#if deceased.dateOfBirth}} + + + + + {{/if}} + {{#if deceased.placeOfDeath}} + + + + + {{/if}} + {{#if deceased.causeOfDeath}} + + + + + {{/if}} + +
First Name:{{deceased.firstName}}
Middle Name:{{deceased.middleName}}
Last Name:{{deceased.lastName}}
Date of Birth:{{deceased.dateOfBirth}}
Place of Death:{{deceased.placeOfDeath}}
Cause of Death:{{deceased.causeOfDeath}}
+
+ + +
+

Order Information

+
+ Number of Copies Requested: + {{order.numberOfCopies}} +
+
+ + + +
+ + \ No newline at end of file diff --git a/src/email/templates/marriage-certificate.hbs b/src/email/templates/marriage-certificate.hbs new file mode 100644 index 0000000..406855b --- /dev/null +++ b/src/email/templates/marriage-certificate.hbs @@ -0,0 +1,208 @@ + + + + + + +
+

New Marriage Certificate Application

+ +
+ Application Submitted: + {{processedAt}}
+ Application Type: + {{#if (eq applyingForYourself 'yes')}} + Self Application + {{else}} + Third Party Application + {{/if}} +
+ + +
+

Applicant Information

+ + + {{#if applicant.title}} + + + + + {{/if}} + {{#if applicant.firstName}} + + + + + {{/if}} + {{#if applicant.middleName}} + + + + + {{/if}} + {{#if applicant.lastName}} + + + + + {{/if}} + {{#if applicant.email}} + + + + + {{/if}} + {{#if applicant.phoneNumber}} + + + + + {{/if}} + +
Title:{{#if (eq applicant.title 'mr')}}Mr{{else if + (eq applicant.title 'ms') + }}Ms{{else if + (eq applicant.title 'mrs') + }}Mrs{{else}}{{applicant.title}}{{/if}}
First Name:{{applicant.firstName}}
Middle Name:{{applicant.middleName}}
Last Name:{{applicant.lastName}}
Email:{{applicant.email}}
Phone Number:{{applicant.phoneNumber}}
+ +

Address

+ + + {{#if applicant.addressLine1}} + + + + + {{/if}} + {{#if applicant.addressLine2}} + + + + + {{/if}} + {{#if applicant.parish}} + + + + + {{/if}} + {{#if applicant.postalCode}} + + + + + {{/if}} + +
Address Line 1:{{applicant.addressLine1}}
Address Line 2:{{applicant.addressLine2}}
Parish:{{applicant.parish}}
Postal Code:{{applicant.postalCode}}
+ +

Identification

+ + + {{#if applicant.usePassportInstead}} + {{#if applicant.passportNumber}} + + + + + {{/if}} + {{else}} + {{#if applicant.idNumber}} + + + + + {{/if}} + {{/if}} + +
Passport Number:{{applicant.passportNumber}}
National ID Number:{{applicant.idNumber}}
+
+ + +
+

Parents' Information

+ + {{#if parents.father}} +

Father

+ + + {{#if parents.father.firstName}} + + + + + {{/if}} + {{#if parents.father.lastName}} + + + + + {{/if}} + +
First Name:{{parents.father.firstName}}
Last Name:{{parents.father.lastName}}
+ {{/if}} + + {{#if parents.mother}} +

Mother

+ + + {{#if parents.mother.firstName}} + + + + + {{/if}} + {{#if parents.mother.lastName}} + + + + + {{/if}} + +
First Name:{{parents.mother.firstName}}
Last Name:{{parents.mother.lastName}}
+ {{/if}} +
+ + +
+

Order Information

+
+ Number of Copies Requested: + {{order.numberOfCopies}} +
+
+ + + +
+ + \ No newline at end of file diff --git a/src/file/file.controller.ts b/src/file/file.controller.ts new file mode 100644 index 0000000..77b894d --- /dev/null +++ b/src/file/file.controller.ts @@ -0,0 +1,27 @@ +import { + Controller, + Post, + Get, + Query, + UploadedFile, + BadRequestException, +} from '@nestjs/common'; +import { ApiFile } from '../common/decorators'; +import { ParseFile } from '../common/pipes'; +import { ApiResponse } from '../common/dto'; +import { FileService } from './file.service'; + +@Controller('file') +export class FileController { + constructor(private readonly filesService: FileService) {} + + @Post('upload') + @ApiFile('file') + async uploadFile( + @UploadedFile(ParseFile) file: Express.Multer.File, + @Query('folder') folder?: string, + ) { + const data = await this.filesService.uploadFile(file, folder); + return ApiResponse.success(data, 'File uploaded successfully'); + } +} diff --git a/src/file/file.module.ts b/src/file/file.module.ts new file mode 100644 index 0000000..a0258d5 --- /dev/null +++ b/src/file/file.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { FileService } from './file.service'; +import { FileController } from './file.controller'; + +@Module({ + imports: [ConfigModule], + providers: [FileService], + controllers: [FileController], + exports: [FileService], +}) +export class FileModule {} diff --git a/src/file/file.service.ts b/src/file/file.service.ts new file mode 100644 index 0000000..0832eb4 --- /dev/null +++ b/src/file/file.service.ts @@ -0,0 +1,82 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Upload } from '@aws-sdk/lib-storage'; +import { + PutObjectCommandInput, + S3Client, + GetObjectCommand, +} from '@aws-sdk/client-s3'; +import * as S3Presigner from '@aws-sdk/s3-request-presigner'; +import { AttachmentDTO } from '../common/dto'; + +@Injectable() +export class FileService { + constructor(private configService: ConfigService) {} + + async uploadFile( + file: Express.Multer.File, + folder = 'general', + ): Promise { + const result = await this.aws(file, folder.toLowerCase()); + + if (!result.url) { + throw new Error('File upload failed'); + } + + return { + url: result.url, + key: result.key, + type: file.mimetype, + name: file.originalname, + size: file.size, + }; + } + + private async aws( + file: Express.Multer.File, + folder = 'general', + ): Promise<{ url: string; key: string }> { + const s3 = new S3Client({ + region: this.configService.get('aws.s3.bucketRegion'), + }); + + const fileName = `${folder}/${Date.now()}-${file.originalname}`.replace( + /\s/g, + '_', + ); + + const params: PutObjectCommandInput = { + Bucket: this.configService.get('aws.s3.bucketName'), + Key: fileName, + Body: file.buffer, + }; + + await new Upload({ + client: s3, + params, + }).done(); + + const url = await this.getSignedUrl(fileName, 7 * 24 * 3600); // 7 days + + return { + url, + key: fileName, + }; + } + + private async getSignedUrl( + fileKey: string, + expiresIn = 3600, + ): Promise { + const s3 = new S3Client({ + region: this.configService.get('aws.s3.bucketRegion'), + }); + + const command = new GetObjectCommand({ + Bucket: this.configService.get('aws.s3.bucketName'), + Key: fileKey, + }); + + return S3Presigner.getSignedUrl(s3, command, { expiresIn }); + } +} diff --git a/src/forms/dto/form-submission-response.dto.ts b/src/forms/dto/form-submission-response.dto.ts index 9449640..087b19a 100644 --- a/src/forms/dto/form-submission-response.dto.ts +++ b/src/forms/dto/form-submission-response.dto.ts @@ -13,6 +13,9 @@ export class FormSubmissionResponseDto { amount?: number; description?: string; + // Dynamic additional data + [key: string]: any; + constructor( submissionId: string, formId: string, @@ -26,6 +29,7 @@ export class FormSubmissionResponseDto { amount?: number; description?: string; }, + additionalData?: Record, ) { this.submissionId = submissionId; this.formId = formId; @@ -41,5 +45,10 @@ export class FormSubmissionResponseDto { this.amount = paymentInfo.amount; this.description = paymentInfo.description; } + + // Add any additional dynamic data + if (additionalData) { + Object.assign(this, additionalData); + } } } diff --git a/src/forms/expression-resolver.service.ts b/src/forms/expression-resolver.service.ts new file mode 100644 index 0000000..869177d --- /dev/null +++ b/src/forms/expression-resolver.service.ts @@ -0,0 +1,326 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Repository } from 'typeorm'; +import { FormConfig } from '../database/entities'; + +export interface ExpressionContext { + formId: string; + formData?: Record; + secrets?: Map; + configRepository?: Repository; +} + +@Injectable() +export class ExpressionResolverService { + private readonly logger = new Logger(ExpressionResolverService.name); + + constructor(private readonly configService: ConfigService) {} + + /** + * Resolve any expression with support for: + * - Database secrets: {{db:form-id:key}} or {{db:key}} + * - Form data references: {{formData.path}} + * - Mathematical expressions: {{formData.field * db:form-id:amount}} + * - Simple values: direct strings or numbers + */ + async resolveExpression( + expression: string | number, + context: ExpressionContext, + ): Promise { + // If it's not a string, return as-is + if (typeof expression !== 'string') { + return expression; + } + + // If it doesn't contain expression markers, return as-is + if (!expression.includes('{{') || !expression.includes('}}')) { + return expression; + } + + // Handle complex expressions (mathematical operations) + if (this.isMathematicalExpression(expression)) { + return await this.resolveMathematicalExpression(expression, context); + } + + // Handle simple variable replacement + return await this.resolveSimpleExpression(expression, context); + } + + /** + * Check if expression contains mathematical operators + */ + private isMathematicalExpression(expression: string): boolean { + // Extract content between {{ and }} + const match = expression.match(/^\{\{(.+)\}\}$/); + if (!match) { + return false; + } + + const content = match[1]; + // Look for mathematical operators with proper spacing or clear mathematical context + // This is more specific to avoid matching hyphens in identifiers like "get-birth-certificate" + const pattern1 = /\s+[+\-*/]\s+/.test(content); // operators with spaces on both sides + const pattern2 = /formData\.[a-zA-Z0-9_.]+\s*[+\-*/]\s*/.test(content); // formData followed by operator + const pattern3 = /[+\-*/]\s*formData\./.test(content); // operator followed by formData + const pattern4 = /[+\-*/]\s*db:/.test(content); // operator followed by db reference + const pattern5 = /\)\s*[+\-*/]\s*/.test(content); // operator after closing parenthesis + const pattern6 = /\s*[+\-*/]\s*\(/.test(content); // operator before opening parenthesis + + const hasMathOperators = + pattern1 || pattern2 || pattern3 || pattern4 || pattern5 || pattern6; + + return hasMathOperators; + } + + /** + * Resolve mathematical expressions like {{formData.numberOfCopies * db:form-id:amount}} + */ + private async resolveMathematicalExpression( + expression: string, + context: ExpressionContext, + ): Promise { + // Extract the inner expression + const match = expression.match(/^\{\{(.+)\}\}$/); + if (!match) { + throw new Error(`Invalid expression format: ${expression}`); + } + + let mathExpression = match[1]; + + // Replace database references first + mathExpression = await this.replaceDatabaseReferences( + mathExpression, + context, + ); + + // Replace form data references + mathExpression = this.replaceFormDataReferences(mathExpression, context); + + // Evaluate the mathematical expression + return this.evaluateMathExpression(mathExpression); + } + + /** + * Resolve simple expressions like {{db:key}} or {{formData.field}} + */ + private async resolveSimpleExpression( + expression: string, + context: ExpressionContext, + ): Promise { + // Extract the inner expression + const match = expression.match(/^\{\{(.+)\}\}$/); + if (!match) { + return expression; // Return as-is if not a proper expression + } + + let innerExpression = match[1]; + + // Replace database secrets in the inner expression + innerExpression = await this.replaceDatabaseReferences( + innerExpression, + context, + ); + + // Replace form data references in the inner expression + innerExpression = this.replaceFormDataReferences(innerExpression, context); + + return innerExpression; // Return the resolved inner content without {{}} + } + + /** + * Replace database references in expression + */ + private async replaceDatabaseReferences( + expression: string, + context: ExpressionContext, + ): Promise { + // Match patterns like db:form-id:key or db:key + const dbPattern = /db:([^:]+)(?::([^}\s+\-*/()]+))?/g; + let result = expression; + + const matches = [...expression.matchAll(dbPattern)]; + for (const match of matches) { + const [fullMatch, part1, part2] = match; + let formId: string; + let secretKey: string; + + if (part2) { + // Format: db:form-id:key + formId = part1; + secretKey = part2; + } else { + // Format: db:key (use current form ID) + formId = context.formId; + secretKey = part1; + } + + try { + const secretValue = await this.getSecretValue( + formId, + secretKey, + context, + ); + result = result.replace(fullMatch, secretValue); + } catch (error) { + this.logger.warn( + `Failed to resolve database reference ${fullMatch}:`, + error.message, + ); + // Replace with empty string or default value + result = result.replace(fullMatch, '0'); + } + } + + return result; + } + + /** + * Replace form data references in expression + */ + private replaceFormDataReferences( + expression: string, + context: ExpressionContext, + ): string { + if (!context.formData) { + return expression; + } + + // Match patterns like formData.field.path + const formDataPattern = /formData\.([a-zA-Z0-9._]+)/g; + let result = expression; + + const matches = [...expression.matchAll(formDataPattern)]; + for (const match of matches) { + const [fullMatch, fieldPath] = match; + const value = this.getNestedValue(context.formData, fieldPath); + + if (value !== undefined && value !== null) { + result = result.replace(fullMatch, String(value)); + } else { + this.logger.warn(`Form data field not found: ${fieldPath}`); + result = result.replace(fullMatch, '0'); + } + } + + return result; + } + + /** + * Get secret value from database repository or environment variables + */ + private async getSecretValue( + formId: string, + secretKey: string, + context: ExpressionContext, + ): Promise { + // Try database repository first if available + if (context.configRepository) { + try { + const config = await context.configRepository.findOne({ + where: { formId, key: secretKey }, + }); + + if (config) { + return config.value; + } + } catch (error) { + this.logger.warn( + `Error querying database for secret ${formId}:${secretKey}:`, + error.message, + ); + } + } + + // Fallback to secrets Map if provided + if (context.secrets) { + const secretMapKey = `${formId}:${secretKey}`; + const value = context.secrets.get(secretMapKey); + if (value) { + return value; + } + } + + // Final fallback to environment variables + const envKey = `${formId + .toUpperCase() + .replace(/-/g, '_')}_${secretKey.toUpperCase()}`; + + const value = this.configService.get(envKey); + if (value) { + return value; + } + + throw new Error( + `Secret not found: ${formId}:${secretKey} (tried database, secrets map, and env ${envKey})`, + ); + } + + /** + * Safely evaluate mathematical expression + */ + private evaluateMathExpression(expression: string): number { + // Validate that expression only contains safe characters + if (!/^[\d\s+\-*/.()]+$/.test(expression)) { + throw new Error(`Unsafe mathematical expression: ${expression}`); + } + + try { + // Use Function constructor for safe evaluation + const result = new Function('return ' + expression)(); + const numericResult = Number(result); + + if (isNaN(numericResult)) { + throw new Error(`Invalid calculation result: ${result}`); + } + + return numericResult; + } catch (error) { + this.logger.error( + `Failed to evaluate expression ${expression}:`, + error.message, + ); + return 0; + } + } + + /** + * Get nested value from object using dot notation + */ + private getNestedValue(obj: any, path: string): any { + return path.split('.').reduce((current, key) => { + return current && typeof current === 'object' ? current[key] : undefined; + }, obj); + } + + /** + * Recursively resolve all expressions in an object + */ + async resolveObjectExpressions( + obj: any, + context: ExpressionContext, + ): Promise { + if (obj === null || obj === undefined) { + return obj; + } + + if (Array.isArray(obj)) { + return Promise.all( + obj.map((item) => this.resolveObjectExpressions(item, context)), + ); + } + + if (typeof obj === 'object') { + const resolved: any = {}; + for (const [key, value] of Object.entries(obj)) { + resolved[key] = await this.resolveObjectExpressions(value, context); + } + return resolved; + } + + if (typeof obj === 'string' || typeof obj === 'number') { + return await this.resolveExpression(obj, context); + } + + return obj; + } +} diff --git a/src/forms/form-utils.service.ts b/src/forms/form-utils.service.ts index b0fdf87..69033d7 100644 --- a/src/forms/form-utils.service.ts +++ b/src/forms/form-utils.service.ts @@ -11,6 +11,7 @@ import * as fs from 'fs/promises'; import * as path from 'path'; import { FormConfig } from '../database/entities'; import { FormSchema } from './interfaces'; +import { ExpressionResolverService } from './expression-resolver.service'; @Injectable() export class FormUtilsService implements OnModuleInit { @@ -22,6 +23,7 @@ export class FormUtilsService implements OnModuleInit { @InjectRepository(FormConfig) private readonly formConfigRepository: Repository, private readonly configService: ConfigService, + private readonly expressionResolver: ExpressionResolverService, ) { this.schemasDir = path.join( process.cwd(), @@ -86,162 +88,23 @@ export class FormUtilsService implements OnModuleInit { ): Promise { const schema = this.getSchema(formId); - // Replace secret variables and form data in processor configs + // Use centralized expression resolver for processor configs + const context = { + formId, + formData: formData || {}, + configRepository: this.formConfigRepository, + }; + for (const processor of schema.processors) { - processor.config = await this.replaceVariables( - formId, + processor.config = await this.expressionResolver.resolveObjectExpressions( processor.config, - formData, + context, ); } return schema; } - /** - * Replace all variable patterns (database secrets and form data) in a config object - */ - private async replaceVariables( - formId: string, - config: Record, - formData?: Record, - ): Promise> { - const result = { ...config }; - - for (const [key, value] of Object.entries(result)) { - if (typeof value === 'string') { - result[key] = await this.replaceVariableValue(formId, value, formData); - } else if (typeof value === 'object' && value !== null) { - result[key] = await this.replaceVariables(formId, value, formData); - } - } - - return result; - } - - /** - * Replace both database secrets and form data variables in a string value - * Supports: - * - {{db:key}} or {{db:formId:key}} for database secrets - * - {{formData.fieldName}} for form field values - */ - private async replaceVariableValue( - formId: string, - value: string, - formData?: Record, - ): Promise { - let result = value; - - // Replace database secrets: {{db:key}} or {{db:formId:key}} - result = await this.replaceDbSecrets(formId, result); - - // Replace form data variables: {{formData.fieldName}} - if (formData) { - result = this.replaceFormData(result, formData); - } - - return result; - } - - /** - * Replace database secret patterns in a string - */ - private async replaceDbSecrets( - formId: string, - value: string, - ): Promise { - const regex = /\{\{db:([^}]+)\}\}/g; - let result = value; - - const matches = value.matchAll(regex); - for (const match of matches) { - const parts = match[1].split(':'); - let targetFormId: string; - let targetKey: string; - - if (parts.length === 1) { - // Shorthand: {{db:key}} - use current formId - targetFormId = formId; - targetKey = parts[0]; - } else { - // Full format: {{db:formId:key}} - targetFormId = parts[0]; - targetKey = parts[1]; - } - - const secretValue = await this.getSecret(targetFormId, targetKey); - result = result.replace(match[0], secretValue); - } - - return result; - } - - /** - * Replace form data patterns in a string - * Supports: {{formData.fieldName}} and nested paths like {{formData.nested.keyName}} - */ - private replaceFormData( - value: string, - formData: Record, - ): string { - const regex = /\{\{formData\.([^}]+)\}\}/g; - let result = value; - - const matches = value.matchAll(regex); - for (const match of matches) { - const fieldPath = match[1]; - const fieldValue = this.getNestedValue(formData, fieldPath); - - if (fieldValue !== undefined && fieldValue !== null) { - // Convert to string and replace - result = result.replace(match[0], String(fieldValue)); - } else { - this.logger.warn( - `Form data field not found: ${fieldPath}, removing from string`, - ); - // Remove the placeholder if field not found - result = result.replace(match[0], ''); - } - } - - return result; - } - - /** - * Get a nested value from an object using a dot-separated path - * Example: getNestedValue({child: {firstName: 'John'}}, 'child.firstName') => 'John' - */ - private getNestedValue(obj: Record, path: string): any { - const keys = path.split('.'); - let current = obj; - - for (const key of keys) { - if (current && typeof current === 'object' && key in current) { - current = current[key]; - } else { - return undefined; - } - } - - return current; - } - - /** - * Retrieve a secret value from the database - */ - private async getSecret(formId: string, key: string): Promise { - const config = await this.formConfigRepository.findOne({ - where: { formId, key }, - }); - - if (!config) { - this.logger.warn(`Secret not found: ${formId}:${key}`); - return `{{db:${formId}:${key}}}`; // Return placeholder if not found - } - - return config.value; - } - /** * Reload all form schemas from disk */ diff --git a/src/forms/forms.module.ts b/src/forms/forms.module.ts index c6489f7..193c700 100644 --- a/src/forms/forms.module.ts +++ b/src/forms/forms.module.ts @@ -5,11 +5,12 @@ import { ProcessorsModule } from '../processors/processors.module'; import { FormsController } from './forms.controller'; import { FormsService } from './forms.service'; import { FormUtilsService } from './form-utils.service'; +import { ExpressionResolverService } from './expression-resolver.service'; @Module({ imports: [DatabaseModule, ValidationModule, ProcessorsModule], controllers: [FormsController], - providers: [FormsService, FormUtilsService], - exports: [FormsService, FormUtilsService], + providers: [FormsService, FormUtilsService, ExpressionResolverService], + exports: [FormsService, FormUtilsService, ExpressionResolverService], }) export class FormsModule {} diff --git a/src/forms/forms.service.ts b/src/forms/forms.service.ts index 3aa678c..434a2c2 100644 --- a/src/forms/forms.service.ts +++ b/src/forms/forms.service.ts @@ -173,6 +173,7 @@ export class FormsService { amount: paymentProcessor.config.amount, description: paymentProcessor.config.description, }, + paymentResult.additionalData, ); return { diff --git a/src/forms/interfaces/form-schema.interface.ts b/src/forms/interfaces/form-schema.interface.ts index 702badb..9556d6f 100644 --- a/src/forms/interfaces/form-schema.interface.ts +++ b/src/forms/interfaces/form-schema.interface.ts @@ -57,6 +57,7 @@ export interface PaymentProcessorConfig extends ProcessorConfig { type: 'payment'; config: { provider: 'ezpay'; + department: string; // Department name (education, health, social_services, transport, etc.) paymentCode: string; // EZPay+ payment code (stored in secrets) amount: number | string; // Fixed amount or formula like "{{formData.calculatedFee}}" description: string; @@ -65,5 +66,10 @@ export interface PaymentProcessorConfig extends ProcessorConfig { allowPayce?: boolean; required?: boolean; // Whether payment is mandatory for form submission timing?: 'immediate' | 'after_validation'; // When to create payment + responseData?: ResponseDataConfig; // Additional data to include in response }; } + +export interface ResponseDataConfig { + include?: string[]; // Form field paths to include (e.g., ['order.numberOfCopies', 'applicant.email']) +} diff --git a/src/payments/department-mapping.service.ts b/src/payments/department-mapping.service.ts new file mode 100644 index 0000000..6036c3a --- /dev/null +++ b/src/payments/department-mapping.service.ts @@ -0,0 +1,62 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; + +@Injectable() +export class DepartmentMappingService { + private readonly logger = new Logger(DepartmentMappingService.name); + + constructor(private configService: ConfigService) {} + + /** + * Get the API key for a given department + */ + getApiKeyForDepartment(department: string): string { + const departmentApiKeys = this.configService.get('ezpay.departmentApiKeys'); + + if (!departmentApiKeys) { + this.logger.warn( + 'Department API keys not configured, falling back to default', + ); + return this.configService.get('ezpay.apiKey'); + } + + const apiKey = departmentApiKeys[department]; + + if (!apiKey) { + this.logger.warn( + `No API key configured for department: ${department}, using default`, + ); + return ( + departmentApiKeys.default || + this.configService.get('ezpay.apiKey') + ); + } + + this.logger.log(`Using API key for department: ${department}`); + return apiKey; + } + + /** + * Get all available departments and their API key status + */ + getAvailableDepartments(): { + department: string; + hasApiKey: boolean; + }[] { + const departmentApiKeys = + this.configService.get('ezpay.departmentApiKeys') || {}; + + return Object.keys(departmentApiKeys).map((department) => ({ + department, + hasApiKey: !!departmentApiKeys[department], + })); + } + + /** + * Validate if a department has a configured API key + */ + isDepartmentConfigured(department: string): boolean { + const departmentApiKeys = this.configService.get('ezpay.departmentApiKeys'); + return !!(departmentApiKeys && departmentApiKeys[department]); + } +} diff --git a/src/payments/ezpay/ezpay.service.ts b/src/payments/ezpay/ezpay.service.ts index ad85e3c..aa613e8 100644 --- a/src/payments/ezpay/ezpay.service.ts +++ b/src/payments/ezpay/ezpay.service.ts @@ -1,5 +1,6 @@ import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; +import { DepartmentMappingService } from '../department-mapping.service'; import { CreatePaymentParams, CreatePaymentResult, @@ -23,13 +24,16 @@ import { @Injectable() export class EZPayService { private readonly logger = new Logger(EZPayService.name); - private readonly config: EZPayConfig; + private readonly defaultConfig: EZPayConfig; - constructor(private configService: ConfigService) { - this.config = this.getConfig(); + constructor( + private configService: ConfigService, + private departmentMappingService: DepartmentMappingService, + ) { + this.defaultConfig = this.getDefaultConfig(); } - private getConfig(): EZPayConfig { + private getDefaultConfig(): EZPayConfig { const apiKey = this.configService.get('ezpay.apiKey'); const baseUrl = this.configService.get('ezpay.baseUrl'); @@ -43,6 +47,49 @@ export class EZPayService { return { apiKey, baseUrl }; } + /** + * Get configuration with optional custom API key + */ + private getConfigWithApiKey(customApiKey?: string): EZPayConfig { + if (customApiKey) { + return { + apiKey: customApiKey, + baseUrl: this.defaultConfig.baseUrl, + }; + } + return this.defaultConfig; + } + + /** + * Extract department from reference number + * Expected format: DEPARTMENT-formId-submissionId (e.g., EDUCATION-form123-sub456) + */ + private extractDepartmentFromReference(reference: string): string | null { + // Check if reference follows the department format + const match = reference.match(/^([A-Z_]+)-(.+)-(.+)$/); + if (match) { + return match[1].toLowerCase(); // Convert EDUCATION to education + } + + this.logger.warn( + `Reference ${reference} does not follow expected format DEPARTMENT-formId-submissionId. Using default API key.`, + ); + return null; + } + + /** + * Get API key for a reference number by extracting department + */ + private getApiKeyForReference(reference: string): string { + const department = this.extractDepartmentFromReference(reference); + if (department) { + return this.departmentMappingService.getApiKeyForDepartment(department); + } + + // Fallback to default API key + return this.departmentMappingService.getApiKeyForDepartment('default'); + } + /** * Generate a unique process ID (20 characters) */ @@ -53,8 +100,9 @@ export class EZPayService { /** * Get the payment page URL for a token */ - getPaymentPageUrl(token: string): string { - return `${this.config.baseUrl}/payment_page?token=${token}`; + getPaymentPageUrl(token: string, customApiKey?: string): string { + const config = this.getConfigWithApiKey(customApiKey); + return `${config.baseUrl}/payment_page?token=${token}`; } /** @@ -136,6 +184,7 @@ export class EZPayService { */ async createPayment( params: CreatePaymentParams, + customApiKey?: string, ): Promise { const { cartItems, @@ -148,6 +197,8 @@ export class EZPayService { allowPayce = true, } = params; + const config = this.getConfigWithApiKey(customApiKey); + try { this.validateCartItems(cartItems); @@ -165,11 +216,11 @@ export class EZPayService { }); const response = await this.makeRequest( - `${this.config.baseUrl}/ezpay_receivecart`, + `${config.baseUrl}/ezpay_receivecart`, { method: 'POST', headers: { - EZPluginKey: this.config.apiKey, + EZPluginKey: config.apiKey, }, body: formData, }, @@ -224,6 +275,7 @@ export class EZPayService { */ async verifyPayment( params: VerifyPaymentParams, + customApiKey?: string, ): Promise { if (!(params.transactionNumber || params.reference)) { throw new EZPayValidationException( @@ -231,6 +283,14 @@ export class EZPayService { ); } + // Determine API key: use custom if provided, otherwise extract from reference + let apiKey = customApiKey; + if (!apiKey && params.reference) { + apiKey = this.getApiKeyForReference(params.reference); + } + + const config = this.getConfigWithApiKey(apiKey); + try { this.logger.log('Verifying payment', params); @@ -243,11 +303,11 @@ export class EZPayService { } const response = await this.makeRequest( - `${this.config.baseUrl}/check_api`, + `${config.baseUrl}/check_api`, { method: 'POST', headers: { - EZPluginKey: this.config.apiKey, + EZPluginKey: config.apiKey, }, body: formData, }, @@ -275,16 +335,19 @@ export class EZPayService { async queryTransactions( startDate: string, endDate: string, + customApiKey?: string, ): Promise { + const config = this.getConfigWithApiKey(customApiKey); + try { this.logger.log(`Querying transactions from ${startDate} to ${endDate}`); const response = await this.makeRequest( - `${this.config.baseUrl}/transactions_api`, + `${config.baseUrl}/transactions_api`, { method: 'POST', headers: { - Apikey: this.config.apiKey, + Apikey: config.apiKey, Startdate: startDate, Enddate: endDate, }, diff --git a/src/payments/index.ts b/src/payments/index.ts index 15f9154..077759f 100644 --- a/src/payments/index.ts +++ b/src/payments/index.ts @@ -1,3 +1,4 @@ export * from './payments.module'; export * from './payment-integration.service'; +export * from './department-mapping.service'; export * from './ezpay'; diff --git a/src/payments/payment-integration.service.ts b/src/payments/payment-integration.service.ts index 84532b9..b2635c7 100644 --- a/src/payments/payment-integration.service.ts +++ b/src/payments/payment-integration.service.ts @@ -1,10 +1,12 @@ import { Injectable, Logger } from '@nestjs/common'; import { EZPayService } from './ezpay/ezpay.service'; +import { DepartmentMappingService } from './department-mapping.service'; import { EZPayCartItem, CreatePaymentResult } from './ezpay/interfaces'; export interface PaymentIntegrationOptions { formId: string; submissionId: string; + department: string; // Required department for API key selection paymentCode: string; amount: number; description: string; @@ -17,7 +19,10 @@ export interface PaymentIntegrationOptions { export class PaymentIntegrationService { private readonly logger = new Logger(PaymentIntegrationService.name); - constructor(private readonly ezpayService: EZPayService) {} + constructor( + private readonly ezpayService: EZPayService, + private readonly departmentMappingService: DepartmentMappingService, + ) {} /** * Create a payment for a form submission @@ -28,6 +33,7 @@ export class PaymentIntegrationService { const { formId, submissionId, + department, paymentCode, amount, description, @@ -39,31 +45,48 @@ export class PaymentIntegrationService { this.logger.log(`Creating payment for form submission`, { formId, submissionId, + department, amount, customerEmail, }); + // Get the correct API key for the department + const apiKey = department + ? this.departmentMappingService.getApiKeyForDepartment(department) + : this.departmentMappingService.getApiKeyForDepartment('default'); + + // Create reference number with department prefix - department is required + if (!department) { + throw new Error('Department is required for payment creation'); + } + + const referenceNumber = + reference || `${department.toUpperCase()}-${formId}-${submissionId}`; + // Create cart item for the form payment const cartItems: EZPayCartItem[] = [ { code: paymentCode, amount, details: description, - reference: reference || `${formId}-${submissionId}`, + reference: referenceNumber, }, ]; try { - const result = await this.ezpayService.createPayment({ - cartItems, - customerEmail, - customerName, - referenceNumber: reference || `FORM-${formId}-${submissionId}`, - processId: this.ezpayService.generateProcessId(), - allowCredit: true, - allowDebit: true, - allowPayce: true, - }); + const result = await this.ezpayService.createPayment( + { + cartItems, + customerEmail, + customerName, + referenceNumber, + processId: this.ezpayService.generateProcessId(), + allowCredit: true, + allowDebit: true, + allowPayce: true, + }, + apiKey, + ); if (result.success) { this.logger.log(`Payment created for submission ${submissionId}`, { @@ -109,6 +132,7 @@ export class PaymentIntegrationService { }); try { + // EZPayService automatically determines the correct API key from the reference const result = await this.ezpayService.verifyPayment({ transactionNumber, reference, @@ -120,9 +144,6 @@ export class PaymentIntegrationService { amount: result.data._amount, transactionNumber: result.data._transaction_number, }); - - // Here you could update the form submission status based on payment status - // Example: update database record, send confirmation email, etc. } return result; @@ -148,15 +169,26 @@ export class PaymentIntegrationService { }); try { - // Extract form ID and submission ID from reference if it follows the pattern + // Extract form ID and submission ID from reference + // Format: DEPARTMENT-formId-submissionId (e.g., EDUCATION-form123-sub456) const referenceParts = callbackData._reference.split('-'); - if (referenceParts.length >= 3 && referenceParts[0] === 'FORM') { + + if (referenceParts.length >= 3) { + const department = referenceParts[0].toLowerCase(); const formId = referenceParts[1]; const submissionId = referenceParts[2]; + this.logger.log('Processing callback for department-based reference', { + department, + formId, + submissionId, + reference: callbackData._reference, + }); + this.logger.log('Extracted form info from payment reference', { formId, submissionId, + department, reference: callbackData._reference, }); @@ -168,12 +200,16 @@ export class PaymentIntegrationService { if (callbackData._status === 'Success') { this.logger.log( - `Payment successful for form ${formId}, submission ${submissionId}`, + `Payment successful for form ${formId}, submission ${submissionId}${ + department ? ` (department: ${department})` : '' + }`, ); // Handle successful payment } else if (callbackData._status === 'Failed') { this.logger.warn( - `Payment failed for form ${formId}, submission ${submissionId}`, + `Payment failed for form ${formId}, submission ${submissionId}${ + department ? ` (department: ${department})` : '' + }`, ); // Handle failed payment } diff --git a/src/payments/payment-webhook.service.ts b/src/payments/payment-webhook.service.ts index a908365..5c97a1b 100644 --- a/src/payments/payment-webhook.service.ts +++ b/src/payments/payment-webhook.service.ts @@ -439,15 +439,27 @@ export class PaymentWebhookService { * Manually verify and synchronize payment status with EZPay * This method can be used for manual verification or periodic reconciliation */ - async manualPaymentVerification(transactionNumber: string): Promise<{ + async manualPaymentVerification( + transactionNumber?: string, + reference?: string, + ): Promise<{ success: boolean; message: string; data?: Payment; }> { try { + // Validate that both parameters are provided + if (!transactionNumber || !reference) { + return { + success: false, + message: 'Both transactionNumber and reference must be provided', + }; + } + // Verify with EZPay const verificationResult = await this.verifyPaymentStatus({ transactionNumber, + reference, }); if (!verificationResult.success || !verificationResult.data) { @@ -463,17 +475,32 @@ export class PaymentWebhookService { ); // Find the payment in our database - const payment = await this.paymentRepository.findOne({ - where: { - referenceNumber: serializedDetails.reference, - }, - relations: ['formSubmissions'], - }); + let payment: Payment | null = null; + + if (serializedDetails?.reference) { + // Use reference from verified data if available + payment = await this.paymentRepository.findOne({ + where: { + referenceNumber: serializedDetails.reference, + }, + relations: ['formSubmissions'], + }); + } else if (reference) { + // Use provided reference parameter + payment = await this.paymentRepository.findOne({ + where: { + referenceNumber: reference, + }, + relations: ['formSubmissions'], + }); + } if (!payment) { + const usedReference = + serializedDetails?.reference || reference || 'unknown'; return { success: false, - message: `Payment not found for reference: ${serializedDetails.reference}`, + message: `Payment not found for reference: ${usedReference}`, }; } @@ -495,7 +522,7 @@ export class PaymentWebhookService { // Create/update transaction record const callbackData: EZPayCallbackDto = { - _reference: serializedDetails.reference, + _reference: serializedDetails?.reference || reference || '', _status: paymentData._status as any, _transaction_number: paymentData._transaction_number, _ezpay_account: paymentData._ezpay_account, @@ -511,7 +538,7 @@ export class PaymentWebhookService { `Payment status updated from ${currentStatus} to ${verifiedStatus}`, { paymentId: payment.id, - referenceNumber: serializedDetails.reference, + referenceNumber: serializedDetails?.reference || reference, transactionNumber: paymentData._transaction_number, }, ); @@ -542,6 +569,8 @@ export class PaymentWebhookService { this.logger.error('Manual payment verification failed', { error: error.message, stack: error.stack, + transactionNumber, + reference, }); return { diff --git a/src/payments/payment.controller.ts b/src/payments/payment.controller.ts index 460d1a3..209df73 100644 --- a/src/payments/payment.controller.ts +++ b/src/payments/payment.controller.ts @@ -8,6 +8,7 @@ import { Logger, Param, Post, + Query, Req, } from '@nestjs/common'; import { Request } from 'express'; @@ -100,6 +101,25 @@ export class PaymentsController { data?: any; }> { const result = await this.paymentWebhookService.manualPaymentVerification( + undefined, + reference, + ); + + return result; + } + + @Get('verify') + @HttpCode(HttpStatus.OK) + async verifyPayment( + @Query('transactionNumber') transactionNumber?: string, + @Query('reference') reference?: string, + ): Promise<{ + success: boolean; + message: string; + data?: any; + }> { + const result = await this.paymentWebhookService.manualPaymentVerification( + transactionNumber, reference, ); diff --git a/src/payments/payments.module.ts b/src/payments/payments.module.ts index 6146bec..c5e48a3 100644 --- a/src/payments/payments.module.ts +++ b/src/payments/payments.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { PaymentIntegrationService } from './payment-integration.service'; import { PaymentWebhookService } from './payment-webhook.service'; +import { DepartmentMappingService } from './department-mapping.service'; import { Payment, PaymentTransaction, @@ -17,8 +18,17 @@ import { EZPayService, PaymentsController } from './ezpay'; FormSubmissionPayment, ]), ], - providers: [PaymentIntegrationService, PaymentWebhookService, EZPayService], + providers: [ + PaymentIntegrationService, + PaymentWebhookService, + EZPayService, + DepartmentMappingService, + ], controllers: [PaymentsController], - exports: [PaymentIntegrationService, PaymentWebhookService], + exports: [ + PaymentIntegrationService, + PaymentWebhookService, + DepartmentMappingService, + ], }) export class PaymentsModule {} diff --git a/src/processors/implementations/payment.processor.ts b/src/processors/implementations/payment.processor.ts index bed3e45..2fee8dd 100644 --- a/src/processors/implementations/payment.processor.ts +++ b/src/processors/implementations/payment.processor.ts @@ -1,15 +1,18 @@ import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { ConfigService } from '@nestjs/config'; import { EZPayService } from '../../payments/ezpay/ezpay.service'; +import { DepartmentMappingService } from '../../payments/department-mapping.service'; import { Payment, PaymentStatus, PaymentProvider, FormSubmissionPayment, } from '../../database/entities'; -import { PaymentProcessorConfig } from '../../forms/interfaces/form-schema.interface'; +import { + PaymentProcessorConfig, + ResponseDataConfig, +} from '../../forms/interfaces/form-schema.interface'; import { IProcessor } from '../interfaces/processor.interface'; export interface PaymentProcessorResult { @@ -22,6 +25,7 @@ export interface PaymentProcessorResult { amount?: number; description?: string; error?: string; + additionalData?: Record; } @Injectable() @@ -34,7 +38,7 @@ export class PaymentProcessor implements IProcessor { @InjectRepository(FormSubmissionPayment) private formSubmissionPaymentRepository: Repository, private ezpayService: EZPayService, - private configService: ConfigService, + private departmentMappingService: DepartmentMappingService, ) {} get type(): string { @@ -49,7 +53,7 @@ export class PaymentProcessor implements IProcessor { data: Record; }, ): Promise { - const result = await this.process(config, config, context); + const result = await this.process(context.data, config, context); if (!result.success) { throw new Error(result.error || 'Payment processing failed'); } @@ -71,7 +75,7 @@ export class PaymentProcessor implements IProcessor { }); // Resolve configuration values - const resolvedConfig = await this.resolveConfig(config, formData); + const resolvedConfig = await this.resolveConfig(config); // Validate payment configuration if (!resolvedConfig.paymentCode) { @@ -95,23 +99,26 @@ export class PaymentProcessor implements IProcessor { }); // Create EZPay payment session - const ezpayResult = await this.ezpayService.createPayment({ - cartItems: [ - { - code: resolvedConfig.paymentCode, - amount: resolvedConfig.amount, - details: resolvedConfig.description, - reference: payment.referenceNumber, - }, - ], - customerEmail: customerInfo.email, - customerName: customerInfo.name, - referenceNumber: payment.referenceNumber, - processId: payment.processId, - allowCredit: resolvedConfig.allowCredit ?? true, - allowDebit: resolvedConfig.allowDebit ?? true, - allowPayce: resolvedConfig.allowPayce ?? true, - }); + const ezpayResult = await this.ezpayService.createPayment( + { + cartItems: [ + { + code: resolvedConfig.paymentCode, + amount: resolvedConfig.amount, + details: resolvedConfig.description, + reference: payment.referenceNumber, + }, + ], + customerEmail: customerInfo.email, + customerName: customerInfo.name, + referenceNumber: payment.referenceNumber, + processId: payment.processId, + allowCredit: resolvedConfig.allowCredit ?? true, + allowDebit: resolvedConfig.allowDebit ?? true, + allowPayce: resolvedConfig.allowPayce ?? true, + }, + resolvedConfig.apiKey, + ); if (!ezpayResult.success) { // Update payment status to failed @@ -162,6 +169,11 @@ export class PaymentProcessor implements IProcessor { }, ); + // Process additional response data if configured + const additionalData = config.responseData + ? await this.processResponseData(config.responseData, formData) + : undefined; + return { success: true, paymentRequired: true, @@ -171,6 +183,7 @@ export class PaymentProcessor implements IProcessor { referenceNumber: payment.referenceNumber, amount: resolvedConfig.amount, description: resolvedConfig.description, + additionalData, }; } catch (error) { this.logger.error( @@ -188,75 +201,44 @@ export class PaymentProcessor implements IProcessor { private async resolveConfig( config: PaymentProcessorConfig['config'], - formData: Record, ): Promise<{ + department: string; paymentCode: string; amount: number; description: string; allowCredit: boolean; allowDebit: boolean; allowPayce: boolean; + apiKey: string; }> { - // Resolve payment code from database secrets - let paymentCode = config.paymentCode; - if (paymentCode.startsWith('{{db:')) { - paymentCode = await this.resolveDbSecret(paymentCode); - } + // At this point, all expressions should already be resolved by FormUtilsService + const paymentCode = config.paymentCode; + const amount = Number(config.amount) || 0; - // Resolve amount (could be dynamic based on form data) - let amount = config.amount; - if (typeof amount === 'string') { - amount = this.evaluateAmountFormula(amount, formData); - } + // Get the department and corresponding API key + const department = config.department || 'default'; + const apiKey = + this.departmentMappingService.getApiKeyForDepartment(department); + + this.logger.log(`Resolved payment config:`, { + department, + paymentCode, + amount, + description: config.description, + }); return { + department, paymentCode, - amount: Number(amount), + amount, description: config.description, allowCredit: config.allowCredit ?? true, allowDebit: config.allowDebit ?? true, allowPayce: config.allowPayce ?? true, + apiKey, }; } - private async resolveDbSecret(secretRef: string): Promise { - // Extract secret path from {{db:form-id:secret-key}} format - const match = secretRef.match(/\{\{db:([^:]+):([^}]+)\}\}/); - if (!match) { - throw new Error(`Invalid secret reference format: ${secretRef}`); - } - - const [, formId, secretKey] = match; - - // This would typically query your form config or secrets table - // For now, using environment variables as fallback - const envKey = `${formId - .toUpperCase() - .replace(/-/g, '_')}_${secretKey.toUpperCase()}`; - const value = this.configService.get(envKey); - - if (!value) { - throw new Error(`Secret not found: ${secretRef} (tried ${envKey})`); - } - - return value; - } - - private evaluateAmountFormula( - formula: string, - formData: Record, - ): number { - // Simple formula evaluation (could be extended with a proper expression parser) - if (formula.startsWith('{{formData.') && formula.endsWith('}}')) { - const path = formula.slice(12, -2); // Remove {{formData. and }} - const value = this.getNestedValue(formData, path); - return Number(value) || 0; - } - - // If it's just a number as string - return Number(formula) || 0; - } - private getNestedValue(obj: any, path: string): any { return path.split('.').reduce((current, key) => current?.[key], obj); } @@ -287,6 +269,7 @@ export class PaymentProcessor implements IProcessor { } private async createPaymentRecord(data: { + department: string; paymentCode: string; amount: number; description: string; @@ -295,8 +278,13 @@ export class PaymentProcessor implements IProcessor { formId: string; submissionId: string; }): Promise { + // Include department in reference number for later API key resolution + const referenceNumber = `${data.department.toUpperCase()}-${data.formId}-${ + data.submissionId + }`; + const payment = this.paymentRepository.create({ - referenceNumber: `${data.formId}-${data.submissionId}`, + referenceNumber, processId: this.ezpayService.generateProcessId(), paymentProvider: PaymentProvider.EZPAY, totalAmount: data.amount, @@ -398,4 +386,28 @@ export class PaymentProcessor implements IProcessor { payment: formSubmissionPayment.payment, }; } + + /** + * Process response data configuration to extract additional data from form data + */ + private async processResponseData( + responseConfig: ResponseDataConfig, + formData: Record, + ): Promise> { + const result: Record = {}; + + // Include specified form fields + if (responseConfig.include) { + for (const fieldPath of responseConfig.include) { + const value = this.getNestedValue(formData, fieldPath); + if (value !== undefined) { + // Use the last part of the field path as the key (e.g., order.numberOfCopies -> numberOfCopies) + const fieldName = fieldPath.split('.').pop() || fieldPath; + result[fieldName] = value; + } + } + } + + return result; + } } diff --git a/src/processors/processors.module.ts b/src/processors/processors.module.ts index aeade56..9e98dbe 100644 --- a/src/processors/processors.module.ts +++ b/src/processors/processors.module.ts @@ -6,7 +6,7 @@ import { PaymentsModule } from '../payments/payments.module'; import { Payment, FormSubmissionPayment } from '../database/entities'; import { EmailProcessor } from './implementations/email.processor'; import { PaymentProcessor } from './implementations/payment.processor'; -import { EZPayService } from '../payments'; +import { EZPayService, DepartmentMappingService } from '../payments'; @Module({ imports: [ @@ -19,6 +19,7 @@ import { EZPayService } from '../payments'; EmailProcessor, PaymentProcessor, EZPayService, + DepartmentMappingService, ], exports: [ProcessorPipelineService], })