diff --git a/backend/package-lock.json b/backend/package-lock.json index b1d941de..108d3b94 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -12,6 +12,7 @@ "@aws-sdk/client-bedrock": "^3.782.0", "@aws-sdk/client-bedrock-runtime": "^3.782.0", "@aws-sdk/client-dynamodb": "^3.758.0", + "@aws-sdk/client-s3": "^3.787.0", "@aws-sdk/client-secrets-manager": "^3.758.0", "@aws-sdk/client-textract": "^3.782.0", "@aws-sdk/util-dynamodb": "^3.758.0", @@ -56,8 +57,8 @@ "@types/jwk-to-pem": "^2.0.2", "@types/multer": "^1.4.12", "@types/node": "^20.12.7", - "@typescript-eslint/eslint-plugin": "^7.9.0", - "@typescript-eslint/parser": "^7.9.0", + "@typescript-eslint/eslint-plugin": "^8.30.1", + "@typescript-eslint/parser": "^8.30.1", "@vitest/coverage-v8": "^3.1.1", "aws-cdk": "2.139.0", "aws-cdk-lib": "^2.185.0", @@ -369,6 +370,69 @@ "node": ">=16.0.0" } }, + "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==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "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==", + "license": "Apache-2.0", + "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==", + "license": "Apache-2.0", + "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==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/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==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", @@ -1490,131 +1554,45 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-secrets-manager": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.758.0.tgz", - "integrity": "sha512-Vi4cdCim0jQx3rrU5R1W4v3czoWL0ajBtoI15oSSt7cwLjzNA0xq4nXSa6rahjTgtZWlLeBprbquvxNzY3qg5Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/credential-provider-node": "3.758.0", - "@aws-sdk/middleware-host-header": "3.734.0", - "@aws-sdk/middleware-logger": "3.734.0", - "@aws-sdk/middleware-recursion-detection": "3.734.0", - "@aws-sdk/middleware-user-agent": "3.758.0", - "@aws-sdk/region-config-resolver": "3.734.0", - "@aws-sdk/types": "3.734.0", - "@aws-sdk/util-endpoints": "3.743.0", - "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", - "@smithy/config-resolver": "^4.0.1", - "@smithy/core": "^3.1.5", - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/hash-node": "^4.0.1", - "@smithy/invalid-dependency": "^4.0.1", - "@smithy/middleware-content-length": "^4.0.1", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/middleware-retry": "^4.0.7", - "@smithy/middleware-serde": "^4.0.2", - "@smithy/middleware-stack": "^4.0.1", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/protocol-http": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.7", - "@smithy/util-defaults-mode-node": "^4.0.7", - "@smithy/util-endpoints": "^3.0.1", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-retry": "^4.0.1", - "@smithy/util-utf8": "^4.0.0", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.758.0.tgz", - "integrity": "sha512-BoGO6IIWrLyLxQG6txJw6RT2urmbtlwfggapNCrNPyYjlXpzTSJhBYjndg7TpDATFd0SXL0zm8y/tXsUXNkdYQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/middleware-host-header": "3.734.0", - "@aws-sdk/middleware-logger": "3.734.0", - "@aws-sdk/middleware-recursion-detection": "3.734.0", - "@aws-sdk/middleware-user-agent": "3.758.0", - "@aws-sdk/region-config-resolver": "3.734.0", - "@aws-sdk/types": "3.734.0", - "@aws-sdk/util-endpoints": "3.743.0", - "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", - "@smithy/config-resolver": "^4.0.1", - "@smithy/core": "^3.1.5", - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/hash-node": "^4.0.1", - "@smithy/invalid-dependency": "^4.0.1", - "@smithy/middleware-content-length": "^4.0.1", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/middleware-retry": "^4.0.7", - "@smithy/middleware-serde": "^4.0.2", - "@smithy/middleware-stack": "^4.0.1", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/protocol-http": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.7", - "@smithy/util-defaults-mode-node": "^4.0.7", - "@smithy/util-endpoints": "^3.0.1", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-retry": "^4.0.1", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-textract": { - "version": "3.782.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-textract/-/client-textract-3.782.0.tgz", - "integrity": "sha512-LQa5wA6OtdcKS7ViKRqhmRpm7k8EHHUQB6Z85R5a0w+3MSsL6NyQYMxkWpxUs4HQ9rq5XRAK7LxL6cya3jzXsw==", + "node_modules/@aws-sdk/client-s3": { + "version": "3.787.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.787.0.tgz", + "integrity": "sha512-eGLCWkN0NlntJ9yPU6OKUggVS4cFvuZJog+cFg1KD5hniLqz7Y0YRtB4uBxW212fK3XCfddgyscEOEeHaTQQTw==", "license": "Apache-2.0", "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.775.0", - "@aws-sdk/credential-provider-node": "3.782.0", + "@aws-sdk/credential-provider-node": "3.787.0", + "@aws-sdk/middleware-bucket-endpoint": "3.775.0", + "@aws-sdk/middleware-expect-continue": "3.775.0", + "@aws-sdk/middleware-flexible-checksums": "3.787.0", "@aws-sdk/middleware-host-header": "3.775.0", + "@aws-sdk/middleware-location-constraint": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", - "@aws-sdk/middleware-user-agent": "3.782.0", + "@aws-sdk/middleware-sdk-s3": "3.775.0", + "@aws-sdk/middleware-ssec": "3.775.0", + "@aws-sdk/middleware-user-agent": "3.787.0", "@aws-sdk/region-config-resolver": "3.775.0", + "@aws-sdk/signature-v4-multi-region": "3.775.0", "@aws-sdk/types": "3.775.0", - "@aws-sdk/util-endpoints": "3.782.0", + "@aws-sdk/util-endpoints": "3.787.0", "@aws-sdk/util-user-agent-browser": "3.775.0", - "@aws-sdk/util-user-agent-node": "3.782.0", + "@aws-sdk/util-user-agent-node": "3.787.0", + "@aws-sdk/xml-builder": "3.775.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", + "@smithy/eventstream-serde-browser": "^4.0.2", + "@smithy/eventstream-serde-config-resolver": "^4.1.0", + "@smithy/eventstream-serde-node": "^4.0.2", "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/hash-blob-browser": "^4.0.2", "@smithy/hash-node": "^4.0.2", + "@smithy/hash-stream-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", + "@smithy/md5-js": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", @@ -1634,19 +1612,19 @@ "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", + "@smithy/util-stream": "^4.2.0", "@smithy/util-utf8": "^4.0.0", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" + "@smithy/util-waiter": "^4.0.3", + "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/client-sso": { - "version": "3.782.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.782.0.tgz", - "integrity": "sha512-5GlJBejo8wqMpSSEKb45WE82YxI2k73YuebjLH/eWDNQeE6VI5Bh9lA1YQ7xNkLLH8hIsb0pSfKVuwh0VEzVrg==", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": { + "version": "3.787.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.787.0.tgz", + "integrity": "sha512-L8R+Mh258G0DC73ktpSVrG4TT9i2vmDLecARTDR/4q5sRivdDQSL5bUp3LKcK80Bx+FRw3UETIlX6mYMLL9PJQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -1655,12 +1633,12 @@ "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", - "@aws-sdk/middleware-user-agent": "3.782.0", + "@aws-sdk/middleware-user-agent": "3.787.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", - "@aws-sdk/util-endpoints": "3.782.0", + "@aws-sdk/util-endpoints": "3.787.0", "@aws-sdk/util-user-agent-browser": "3.775.0", - "@aws-sdk/util-user-agent-node": "3.782.0", + "@aws-sdk/util-user-agent-node": "3.787.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", @@ -1692,7 +1670,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/core": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/core": { "version": "3.775.0", "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.775.0.tgz", "integrity": "sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA==", @@ -1714,7 +1692,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-env": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-env": { "version": "3.775.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.775.0.tgz", "integrity": "sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw==", @@ -1730,7 +1708,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-http": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": { "version": "3.775.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.775.0.tgz", "integrity": "sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww==", @@ -1751,19 +1729,19 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.782.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.782.0.tgz", - "integrity": "sha512-wd4KdRy2YjLsE4Y7pz00470Iip06GlRHkG4dyLW7/hFMzEO2o7ixswCWp6J2VGZVAX64acknlv2Q0z02ebjmhw==", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.787.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.787.0.tgz", + "integrity": "sha512-hc2taRoDlXn2uuNuHWDJljVWYrp3r9JF1a/8XmOAZhVUNY+ImeeStylHXhXXKEA4JOjW+5PdJj0f1UDkVCHJiQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/credential-provider-env": "3.775.0", "@aws-sdk/credential-provider-http": "3.775.0", "@aws-sdk/credential-provider-process": "3.775.0", - "@aws-sdk/credential-provider-sso": "3.782.0", - "@aws-sdk/credential-provider-web-identity": "3.782.0", - "@aws-sdk/nested-clients": "3.782.0", + "@aws-sdk/credential-provider-sso": "3.787.0", + "@aws-sdk/credential-provider-web-identity": "3.787.0", + "@aws-sdk/nested-clients": "3.787.0", "@aws-sdk/types": "3.775.0", "@smithy/credential-provider-imds": "^4.0.2", "@smithy/property-provider": "^4.0.2", @@ -1775,18 +1753,18 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.782.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.782.0.tgz", - "integrity": "sha512-HZiAF+TCEyKjju9dgysjiPIWgt/+VerGaeEp18mvKLNfgKz1d+/82A2USEpNKTze7v3cMFASx3CvL8yYyF7mJw==", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.787.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.787.0.tgz", + "integrity": "sha512-JioVi44B1vDMaK2CdzqimwvJD3uzvzbQhaEWXsGMBcMcNHajXAXf08EF50JG3ZhLrhhUsT1ObXpbTaPINOhh+g==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/credential-provider-env": "3.775.0", "@aws-sdk/credential-provider-http": "3.775.0", - "@aws-sdk/credential-provider-ini": "3.782.0", + "@aws-sdk/credential-provider-ini": "3.787.0", "@aws-sdk/credential-provider-process": "3.775.0", - "@aws-sdk/credential-provider-sso": "3.782.0", - "@aws-sdk/credential-provider-web-identity": "3.782.0", + "@aws-sdk/credential-provider-sso": "3.787.0", + "@aws-sdk/credential-provider-web-identity": "3.787.0", "@aws-sdk/types": "3.775.0", "@smithy/credential-provider-imds": "^4.0.2", "@smithy/property-provider": "^4.0.2", @@ -1798,7 +1776,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-process": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-process": { "version": "3.775.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.775.0.tgz", "integrity": "sha512-A6k68H9rQp+2+7P7SGO90Csw6nrUEm0Qfjpn9Etc4EboZhhCLs9b66umUsTsSBHus4FDIe5JQxfCUyt1wgNogg==", @@ -1815,15 +1793,15 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.782.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.782.0.tgz", - "integrity": "sha512-1y1ucxTtTIGDSNSNxriQY8msinilhe9gGvQpUDYW9gboyC7WQJPDw66imy258V6osdtdi+xoHzVCbCz3WhosMQ==", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.787.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.787.0.tgz", + "integrity": "sha512-fHc08bsvwm4+dEMEQKnQ7c1irEQmmxbgS+Fq41y09pPvPh31nAhoMcjBSTWAaPHvvsRbTYvmP4Mf12ZGr8/nfg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.782.0", + "@aws-sdk/client-sso": "3.787.0", "@aws-sdk/core": "3.775.0", - "@aws-sdk/token-providers": "3.782.0", + "@aws-sdk/token-providers": "3.787.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", @@ -1834,14 +1812,14 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.782.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.782.0.tgz", - "integrity": "sha512-xCna0opVPaueEbJoclj5C6OpDNi0Gynj+4d7tnuXGgQhTHPyAz8ZyClkVqpi5qvHTgxROdUEDxWqEO5jqRHZHQ==", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.787.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.787.0.tgz", + "integrity": "sha512-SobmCwNbk6TfEsF283mZPQEI5vV2j6eY5tOCj8Er4Lzraxu9fBPADV+Bib2A8F6jlB1lMPJzOuDCbEasSt/RIw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "3.775.0", - "@aws-sdk/nested-clients": "3.782.0", + "@aws-sdk/nested-clients": "3.787.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", @@ -1851,7 +1829,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/middleware-host-header": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": { "version": "3.775.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.775.0.tgz", "integrity": "sha512-tkSegM0Z6WMXpLB8oPys/d+umYIocvO298mGvcMCncpRl77L9XkvSLJIFzaHes+o7djAgIduYw8wKIMStFss2w==", @@ -1866,7 +1844,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/middleware-logger": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { "version": "3.775.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.775.0.tgz", "integrity": "sha512-FaxO1xom4MAoUJsldmR92nT1G6uZxTdNYOFYtdHfd6N2wcNaTuxgjIvqzg5y7QIH9kn58XX/dzf1iTjgqUStZw==", @@ -1880,7 +1858,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/middleware-recursion-detection": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.775.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.775.0.tgz", "integrity": "sha512-GLCzC8D0A0YDG5u3F5U03Vb9j5tcOEFhr8oc6PDk0k0vm5VwtZOE6LvK7hcCSoAB4HXyOUM0sQuXrbaAh9OwXA==", @@ -1895,15 +1873,15 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.782.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.782.0.tgz", - "integrity": "sha512-i32H2R6IItX+bQ2p4+v2gGO2jA80jQoJO2m1xjU9rYWQW3+ErWy4I5YIuQHTBfb6hSdAHbaRfqPDgbv9J2rjEg==", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.787.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.787.0.tgz", + "integrity": "sha512-Lnfj8SmPLYtrDFthNIaNj66zZsBCam+E4XiUDr55DIHTGstH6qZ/q6vg0GfbukxwSmUcGMwSR4Qbn8rb8yd77g==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", - "@aws-sdk/util-endpoints": "3.782.0", + "@aws-sdk/util-endpoints": "3.787.0", "@smithy/core": "^3.2.0", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", @@ -1913,10 +1891,10 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/nested-clients": { - "version": "3.782.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.782.0.tgz", - "integrity": "sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA==", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/nested-clients": { + "version": "3.787.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.787.0.tgz", + "integrity": "sha512-xk03q1xpKNHgbuo+trEf1dFrI239kuMmjKKsqLEsHlAZbuFq4yRGMlHBrVMnKYOPBhVFDS/VineM991XI52fKg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -1925,12 +1903,12 @@ "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", - "@aws-sdk/middleware-user-agent": "3.782.0", + "@aws-sdk/middleware-user-agent": "3.787.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", - "@aws-sdk/util-endpoints": "3.782.0", + "@aws-sdk/util-endpoints": "3.787.0", "@aws-sdk/util-user-agent-browser": "3.775.0", - "@aws-sdk/util-user-agent-node": "3.782.0", + "@aws-sdk/util-user-agent-node": "3.787.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", @@ -1962,7 +1940,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/region-config-resolver": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/region-config-resolver": { "version": "3.775.0", "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.775.0.tgz", "integrity": "sha512-40iH3LJjrQS3LKUJAl7Wj0bln7RFPEvUYKFxtP8a+oKFDO0F65F52xZxIJbPn6sHkxWDAnZlGgdjZXM3p2g5wQ==", @@ -1979,13 +1957,13 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/token-providers": { - "version": "3.782.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.782.0.tgz", - "integrity": "sha512-4tPuk/3+THPrzKaXW4jE2R67UyGwHLFizZ47pcjJWbhb78IIJAy94vbeqEQ+veS84KF5TXcU7g5jGTXC0D70Wg==", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": { + "version": "3.787.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.787.0.tgz", + "integrity": "sha512-d7/NIqxq308Zg0RPMNrmn0QvzniL4Hx8Qdwzr6YZWLYAbUSvZYS2ppLR3BFWSkV6SsTJUx8BuDaj3P8vttkrog==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/nested-clients": "3.782.0", + "@aws-sdk/nested-clients": "3.787.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", @@ -1996,7 +1974,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/types": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/types": { "version": "3.775.0", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.775.0.tgz", "integrity": "sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==", @@ -2009,10 +1987,10 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/util-endpoints": { - "version": "3.782.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.782.0.tgz", - "integrity": "sha512-/RJOAO7o7HI6lEa4ASbFFLHGU9iPK876BhsVfnl54MvApPVYWQ9sHO0anOUim2S5lQTwd/6ghuH3rFYSq/+rdw==", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": { + "version": "3.787.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.787.0.tgz", + "integrity": "sha512-fd3zkiOkwnbdbN0Xp9TsP5SWrmv0SpT70YEdbb8wAj2DWQwiCmFszaSs+YCvhoCdmlR3Wl9Spu0pGpSAGKeYvQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.775.0", @@ -2024,7 +2002,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/util-user-agent-browser": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.775.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.775.0.tgz", "integrity": "sha512-txw2wkiJmZKVdDbscK7VBK+u+TJnRtlUjRTLei+elZg2ADhpQxfVAQl436FUeIv6AhB/oRHW6/K/EAGXUSWi0A==", @@ -2036,13 +2014,13 @@ "tslib": "^2.6.2" } }, - "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.782.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.782.0.tgz", - "integrity": "sha512-dMFkUBgh2Bxuw8fYZQoH/u3H4afQ12VSkzEi//qFiDTwbKYq+u+RYjc8GLDM6JSK1BShMu5AVR7HD4ap1TYUnA==", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.787.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.787.0.tgz", + "integrity": "sha512-mG7Lz8ydfG4SF9e8WSXiPQ/Lsn3n8A5B5jtPROidafi06I3ckV2WxyMLdwG14m919NoS6IOfWHyRGSqWIwbVKA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.782.0", + "@aws-sdk/middleware-user-agent": "3.787.0", "@aws-sdk/types": "3.775.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/types": "^4.2.0", @@ -2060,15 +2038,585 @@ } } }, - "node_modules/@aws-sdk/core": { + "node_modules/@aws-sdk/client-secrets-manager": { "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.758.0.tgz", - "integrity": "sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg==", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.758.0.tgz", + "integrity": "sha512-Vi4cdCim0jQx3rrU5R1W4v3czoWL0ajBtoI15oSSt7cwLjzNA0xq4nXSa6rahjTgtZWlLeBprbquvxNzY3qg5Q==", "license": "Apache-2.0", "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/credential-provider-node": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", - "@smithy/core": "^3.1.5", - "@smithy/node-config-provider": "^4.0.1", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.758.0.tgz", + "integrity": "sha512-BoGO6IIWrLyLxQG6txJw6RT2urmbtlwfggapNCrNPyYjlXpzTSJhBYjndg7TpDATFd0SXL0zm8y/tXsUXNkdYQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract": { + "version": "3.782.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-textract/-/client-textract-3.782.0.tgz", + "integrity": "sha512-LQa5wA6OtdcKS7ViKRqhmRpm7k8EHHUQB6Z85R5a0w+3MSsL6NyQYMxkWpxUs4HQ9rq5XRAK7LxL6cya3jzXsw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.775.0", + "@aws-sdk/credential-provider-node": "3.782.0", + "@aws-sdk/middleware-host-header": "3.775.0", + "@aws-sdk/middleware-logger": "3.775.0", + "@aws-sdk/middleware-recursion-detection": "3.775.0", + "@aws-sdk/middleware-user-agent": "3.782.0", + "@aws-sdk/region-config-resolver": "3.775.0", + "@aws-sdk/types": "3.775.0", + "@aws-sdk/util-endpoints": "3.782.0", + "@aws-sdk/util-user-agent-browser": "3.775.0", + "@aws-sdk/util-user-agent-node": "3.782.0", + "@smithy/config-resolver": "^4.1.0", + "@smithy/core": "^3.2.0", + "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/hash-node": "^4.0.2", + "@smithy/invalid-dependency": "^4.0.2", + "@smithy/middleware-content-length": "^4.0.2", + "@smithy/middleware-endpoint": "^4.1.0", + "@smithy/middleware-retry": "^4.1.0", + "@smithy/middleware-serde": "^4.0.3", + "@smithy/middleware-stack": "^4.0.2", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-http-handler": "^4.0.4", + "@smithy/protocol-http": "^5.1.0", + "@smithy/smithy-client": "^4.2.0", + "@smithy/types": "^4.2.0", + "@smithy/url-parser": "^4.0.2", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.8", + "@smithy/util-defaults-mode-node": "^4.0.8", + "@smithy/util-endpoints": "^3.0.2", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-retry": "^4.0.2", + "@smithy/util-utf8": "^4.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/client-sso": { + "version": "3.782.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.782.0.tgz", + "integrity": "sha512-5GlJBejo8wqMpSSEKb45WE82YxI2k73YuebjLH/eWDNQeE6VI5Bh9lA1YQ7xNkLLH8hIsb0pSfKVuwh0VEzVrg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.775.0", + "@aws-sdk/middleware-host-header": "3.775.0", + "@aws-sdk/middleware-logger": "3.775.0", + "@aws-sdk/middleware-recursion-detection": "3.775.0", + "@aws-sdk/middleware-user-agent": "3.782.0", + "@aws-sdk/region-config-resolver": "3.775.0", + "@aws-sdk/types": "3.775.0", + "@aws-sdk/util-endpoints": "3.782.0", + "@aws-sdk/util-user-agent-browser": "3.775.0", + "@aws-sdk/util-user-agent-node": "3.782.0", + "@smithy/config-resolver": "^4.1.0", + "@smithy/core": "^3.2.0", + "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/hash-node": "^4.0.2", + "@smithy/invalid-dependency": "^4.0.2", + "@smithy/middleware-content-length": "^4.0.2", + "@smithy/middleware-endpoint": "^4.1.0", + "@smithy/middleware-retry": "^4.1.0", + "@smithy/middleware-serde": "^4.0.3", + "@smithy/middleware-stack": "^4.0.2", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-http-handler": "^4.0.4", + "@smithy/protocol-http": "^5.1.0", + "@smithy/smithy-client": "^4.2.0", + "@smithy/types": "^4.2.0", + "@smithy/url-parser": "^4.0.2", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.8", + "@smithy/util-defaults-mode-node": "^4.0.8", + "@smithy/util-endpoints": "^3.0.2", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-retry": "^4.0.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/core": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.775.0.tgz", + "integrity": "sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.775.0", + "@smithy/core": "^3.2.0", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/property-provider": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/signature-v4": "^5.0.2", + "@smithy/smithy-client": "^4.2.0", + "@smithy/types": "^4.2.0", + "@smithy/util-middleware": "^4.0.2", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.775.0.tgz", + "integrity": "sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.775.0", + "@aws-sdk/types": "3.775.0", + "@smithy/property-provider": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.775.0.tgz", + "integrity": "sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.775.0", + "@aws-sdk/types": "3.775.0", + "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/node-http-handler": "^4.0.4", + "@smithy/property-provider": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/smithy-client": "^4.2.0", + "@smithy/types": "^4.2.0", + "@smithy/util-stream": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.782.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.782.0.tgz", + "integrity": "sha512-wd4KdRy2YjLsE4Y7pz00470Iip06GlRHkG4dyLW7/hFMzEO2o7ixswCWp6J2VGZVAX64acknlv2Q0z02ebjmhw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.775.0", + "@aws-sdk/credential-provider-env": "3.775.0", + "@aws-sdk/credential-provider-http": "3.775.0", + "@aws-sdk/credential-provider-process": "3.775.0", + "@aws-sdk/credential-provider-sso": "3.782.0", + "@aws-sdk/credential-provider-web-identity": "3.782.0", + "@aws-sdk/nested-clients": "3.782.0", + "@aws-sdk/types": "3.775.0", + "@smithy/credential-provider-imds": "^4.0.2", + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.782.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.782.0.tgz", + "integrity": "sha512-HZiAF+TCEyKjju9dgysjiPIWgt/+VerGaeEp18mvKLNfgKz1d+/82A2USEpNKTze7v3cMFASx3CvL8yYyF7mJw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.775.0", + "@aws-sdk/credential-provider-http": "3.775.0", + "@aws-sdk/credential-provider-ini": "3.782.0", + "@aws-sdk/credential-provider-process": "3.775.0", + "@aws-sdk/credential-provider-sso": "3.782.0", + "@aws-sdk/credential-provider-web-identity": "3.782.0", + "@aws-sdk/types": "3.775.0", + "@smithy/credential-provider-imds": "^4.0.2", + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.775.0.tgz", + "integrity": "sha512-A6k68H9rQp+2+7P7SGO90Csw6nrUEm0Qfjpn9Etc4EboZhhCLs9b66umUsTsSBHus4FDIe5JQxfCUyt1wgNogg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.775.0", + "@aws-sdk/types": "3.775.0", + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.782.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.782.0.tgz", + "integrity": "sha512-1y1ucxTtTIGDSNSNxriQY8msinilhe9gGvQpUDYW9gboyC7WQJPDw66imy258V6osdtdi+xoHzVCbCz3WhosMQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.782.0", + "@aws-sdk/core": "3.775.0", + "@aws-sdk/token-providers": "3.782.0", + "@aws-sdk/types": "3.775.0", + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.782.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.782.0.tgz", + "integrity": "sha512-xCna0opVPaueEbJoclj5C6OpDNi0Gynj+4d7tnuXGgQhTHPyAz8ZyClkVqpi5qvHTgxROdUEDxWqEO5jqRHZHQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.775.0", + "@aws-sdk/nested-clients": "3.782.0", + "@aws-sdk/types": "3.775.0", + "@smithy/property-provider": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.775.0.tgz", + "integrity": "sha512-tkSegM0Z6WMXpLB8oPys/d+umYIocvO298mGvcMCncpRl77L9XkvSLJIFzaHes+o7djAgIduYw8wKIMStFss2w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.775.0", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/middleware-logger": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.775.0.tgz", + "integrity": "sha512-FaxO1xom4MAoUJsldmR92nT1G6uZxTdNYOFYtdHfd6N2wcNaTuxgjIvqzg5y7QIH9kn58XX/dzf1iTjgqUStZw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.775.0", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.775.0.tgz", + "integrity": "sha512-GLCzC8D0A0YDG5u3F5U03Vb9j5tcOEFhr8oc6PDk0k0vm5VwtZOE6LvK7hcCSoAB4HXyOUM0sQuXrbaAh9OwXA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.775.0", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.782.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.782.0.tgz", + "integrity": "sha512-i32H2R6IItX+bQ2p4+v2gGO2jA80jQoJO2m1xjU9rYWQW3+ErWy4I5YIuQHTBfb6hSdAHbaRfqPDgbv9J2rjEg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.775.0", + "@aws-sdk/types": "3.775.0", + "@aws-sdk/util-endpoints": "3.782.0", + "@smithy/core": "^3.2.0", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/nested-clients": { + "version": "3.782.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.782.0.tgz", + "integrity": "sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.775.0", + "@aws-sdk/middleware-host-header": "3.775.0", + "@aws-sdk/middleware-logger": "3.775.0", + "@aws-sdk/middleware-recursion-detection": "3.775.0", + "@aws-sdk/middleware-user-agent": "3.782.0", + "@aws-sdk/region-config-resolver": "3.775.0", + "@aws-sdk/types": "3.775.0", + "@aws-sdk/util-endpoints": "3.782.0", + "@aws-sdk/util-user-agent-browser": "3.775.0", + "@aws-sdk/util-user-agent-node": "3.782.0", + "@smithy/config-resolver": "^4.1.0", + "@smithy/core": "^3.2.0", + "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/hash-node": "^4.0.2", + "@smithy/invalid-dependency": "^4.0.2", + "@smithy/middleware-content-length": "^4.0.2", + "@smithy/middleware-endpoint": "^4.1.0", + "@smithy/middleware-retry": "^4.1.0", + "@smithy/middleware-serde": "^4.0.3", + "@smithy/middleware-stack": "^4.0.2", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/node-http-handler": "^4.0.4", + "@smithy/protocol-http": "^5.1.0", + "@smithy/smithy-client": "^4.2.0", + "@smithy/types": "^4.2.0", + "@smithy/url-parser": "^4.0.2", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.8", + "@smithy/util-defaults-mode-node": "^4.0.8", + "@smithy/util-endpoints": "^3.0.2", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-retry": "^4.0.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.775.0.tgz", + "integrity": "sha512-40iH3LJjrQS3LKUJAl7Wj0bln7RFPEvUYKFxtP8a+oKFDO0F65F52xZxIJbPn6sHkxWDAnZlGgdjZXM3p2g5wQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.775.0", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/types": "^4.2.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/token-providers": { + "version": "3.782.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.782.0.tgz", + "integrity": "sha512-4tPuk/3+THPrzKaXW4jE2R67UyGwHLFizZ47pcjJWbhb78IIJAy94vbeqEQ+veS84KF5TXcU7g5jGTXC0D70Wg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "3.782.0", + "@aws-sdk/types": "3.775.0", + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/types": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.775.0.tgz", + "integrity": "sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/util-endpoints": { + "version": "3.782.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.782.0.tgz", + "integrity": "sha512-/RJOAO7o7HI6lEa4ASbFFLHGU9iPK876BhsVfnl54MvApPVYWQ9sHO0anOUim2S5lQTwd/6ghuH3rFYSq/+rdw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.775.0", + "@smithy/types": "^4.2.0", + "@smithy/util-endpoints": "^3.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.775.0.tgz", + "integrity": "sha512-txw2wkiJmZKVdDbscK7VBK+u+TJnRtlUjRTLei+elZg2ADhpQxfVAQl436FUeIv6AhB/oRHW6/K/EAGXUSWi0A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.775.0", + "@smithy/types": "^4.2.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-textract/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.782.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.782.0.tgz", + "integrity": "sha512-dMFkUBgh2Bxuw8fYZQoH/u3H4afQ12VSkzEi//qFiDTwbKYq+u+RYjc8GLDM6JSK1BShMu5AVR7HD4ap1TYUnA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.782.0", + "@aws-sdk/types": "3.775.0", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/types": "^4.2.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/core": { + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.758.0.tgz", + "integrity": "sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", @@ -2189,12 +2737,233 @@ "integrity": "sha512-x0FYJqcOLUCv8GLLFDYMXRAQKGjoM+L0BG4BiHYZRDf24yQWFCAZsCQAYKo6XZYh2qznbsW6f//qpyJ5b0QVKQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.758.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/token-providers": "3.758.0", + "@aws-sdk/client-sso": "3.758.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/token-providers": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.758.0.tgz", + "integrity": "sha512-XGguXhBqiCXMXRxcfCAVPlMbm3VyJTou79r/3mxWddHWF0XbhaQiBIbUz6vobVTD25YQRbWSmSch7VA8kI5Lrw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/nested-clients": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/endpoint-cache": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.723.0.tgz", + "integrity": "sha512-2+a4WXRc+07uiPR+zJiPGKSOWaNJQNqitkks+6Hhm/haTLJqNVTgY2OWDh2PXvwMNpKB+AlGdhE65Oy6NzUgXg==", + "license": "Apache-2.0", + "dependencies": { + "mnemonist": "0.38.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.775.0.tgz", + "integrity": "sha512-qogMIpVChDYr4xiUNC19/RDSw/sKoHkAhouS6Skxiy6s27HBhow1L3Z1qVYXuBmOZGSWPU0xiyZCvOyWrv9s+Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.775.0", + "@aws-sdk/util-arn-parser": "3.723.0", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "@smithy/util-config-provider": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@aws-sdk/types": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.775.0.tgz", + "integrity": "sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-endpoint-discovery": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.734.0.tgz", + "integrity": "sha512-hE3x9Sbqy64g/lcFIq7BF9IS1tSOyfBCyHf1xBgevWeFIDTWh647URuCNWoEwtw4HMEhO2MDUQcKf1PFh1dNDA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/endpoint-cache": "3.723.0", + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.775.0.tgz", + "integrity": "sha512-Apd3owkIeUW5dnk3au9np2IdW2N0zc9NjTjHiH+Mx3zqwSrc+m+ANgJVgk9mnQjMzU/vb7VuxJ0eqdEbp5gYsg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.775.0", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue/node_modules/@aws-sdk/types": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.775.0.tgz", + "integrity": "sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.787.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.787.0.tgz", + "integrity": "sha512-X71qEwWoixFmwowWzlPoZUR3u1CWJ7iAzU0EzIxqmPhQpQJLFmdL1+SRjqATynDPZQzLs1a5HBtPT++EnZ+Quw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.775.0", + "@aws-sdk/types": "3.775.0", + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-stream": "^4.2.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/core": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.775.0.tgz", + "integrity": "sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.775.0", + "@smithy/core": "^3.2.0", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/property-provider": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/signature-v4": "^5.0.2", + "@smithy/smithy-client": "^4.2.0", + "@smithy/types": "^4.2.0", + "@smithy/util-middleware": "^4.0.2", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/types": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.775.0.tgz", + "integrity": "sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.734.0.tgz", + "integrity": "sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.775.0.tgz", + "integrity": "sha512-8TMXEHZXZTFTckQLyBT5aEI8fX11HZcwZseRifvBKKpj0RZDk4F0EEYGxeNSPpUQ7n+PRWyfAEnnZNRdAj/1NQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.775.0", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint/node_modules/@aws-sdk/types": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.775.0.tgz", + "integrity": "sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.734.0.tgz", + "integrity": "sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w==", + "license": "Apache-2.0", + "dependencies": { "@aws-sdk/types": "3.734.0", - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, @@ -2202,16 +2971,14 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.758.0.tgz", - "integrity": "sha512-XGguXhBqiCXMXRxcfCAVPlMbm3VyJTou79r/3mxWddHWF0XbhaQiBIbUz6vobVTD25YQRbWSmSch7VA8kI5Lrw==", + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.734.0.tgz", + "integrity": "sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/nested-clients": "3.758.0", "@aws-sdk/types": "3.734.0", - "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, @@ -2219,74 +2986,87 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/endpoint-cache": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.723.0.tgz", - "integrity": "sha512-2+a4WXRc+07uiPR+zJiPGKSOWaNJQNqitkks+6Hhm/haTLJqNVTgY2OWDh2PXvwMNpKB+AlGdhE65Oy6NzUgXg==", + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.775.0.tgz", + "integrity": "sha512-zsvcu7cWB28JJ60gVvjxPCI7ZU7jWGcpNACPiZGyVtjYXwcxyhXbYEVDSWKsSA6ERpz9XrpLYod8INQWfW3ECg==", "license": "Apache-2.0", "dependencies": { - "mnemonist": "0.38.3", + "@aws-sdk/core": "3.775.0", + "@aws-sdk/types": "3.775.0", + "@aws-sdk/util-arn-parser": "3.723.0", + "@smithy/core": "^3.2.0", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/signature-v4": "^5.0.2", + "@smithy/smithy-client": "^4.2.0", + "@smithy/types": "^4.2.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-stream": "^4.2.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-endpoint-discovery": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.734.0.tgz", - "integrity": "sha512-hE3x9Sbqy64g/lcFIq7BF9IS1tSOyfBCyHf1xBgevWeFIDTWh647URuCNWoEwtw4HMEhO2MDUQcKf1PFh1dNDA==", + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/core": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.775.0.tgz", + "integrity": "sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/endpoint-cache": "3.723.0", - "@aws-sdk/types": "3.734.0", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", + "@aws-sdk/types": "3.775.0", + "@smithy/core": "^3.2.0", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/property-provider": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/signature-v4": "^5.0.2", + "@smithy/smithy-client": "^4.2.0", + "@smithy/types": "^4.2.0", + "@smithy/util-middleware": "^4.0.2", + "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.734.0.tgz", - "integrity": "sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==", + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/types": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.775.0.tgz", + "integrity": "sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.734.0.tgz", - "integrity": "sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w==", + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.775.0.tgz", + "integrity": "sha512-Iw1RHD8vfAWWPzBBIKaojO4GAvQkHOYIpKdAfis/EUSUmSa79QsnXnRqsdcE0mCB0Ylj23yi+ah4/0wh9FsekA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/types": "^4.1.0", + "@aws-sdk/types": "3.775.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.734.0.tgz", - "integrity": "sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA==", + "node_modules/@aws-sdk/middleware-ssec/node_modules/@aws-sdk/types": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.775.0.tgz", + "integrity": "sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -2377,6 +3157,36 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.775.0.tgz", + "integrity": "sha512-cnGk8GDfTMJ8p7+qSk92QlIk2bmTmFJqhYxcXZ9PysjZtx0xmfCMxnG3Hjy1oU2mt5boPCVSOptqtWixayM17g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.775.0", + "@aws-sdk/types": "3.775.0", + "@smithy/protocol-http": "^5.1.0", + "@smithy/signature-v4": "^5.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@aws-sdk/types": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.775.0.tgz", + "integrity": "sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/token-providers": { "version": "3.758.0", "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.758.0.tgz", @@ -2407,6 +3217,18 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.723.0.tgz", + "integrity": "sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/util-dynamodb": { "version": "3.767.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.767.0.tgz", @@ -2485,6 +3307,19 @@ } } }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.775.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.775.0.tgz", + "integrity": "sha512-b9NGO6FKJeLGYnV7Z1yvcP1TNU4dkD5jNsLWOF1/sygZoASaQhNOlaiJ/1OH331YQ1R1oWk38nBb0frsYkDsOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -5084,6 +5919,31 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.0.0.tgz", + "integrity": "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz", + "integrity": "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@smithy/config-resolver": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.0.tgz", @@ -5221,6 +6081,21 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.2.tgz", + "integrity": "sha512-3g188Z3DyhtzfBRxpZjU8R9PpOQuYsbNnyStc/ZVS+9nVX1f6XeNOa9IrAh35HwwIZg+XWk8bFVtNINVscBP+g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.0.0", + "@smithy/chunked-blob-reader-native": "^4.0.0", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@smithy/hash-node": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.2.tgz", @@ -5236,6 +6111,20 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.2.tgz", + "integrity": "sha512-POWDuTznzbIwlEXEvvXoPMS10y0WKXK790soe57tFRfvf4zBHyzE529HpZMqmDdwG9MfFflnyzndUQ8j78ZdSg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@smithy/invalid-dependency": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.2.tgz", @@ -5261,6 +6150,20 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/md5-js": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.2.tgz", + "integrity": "sha512-Hc0R8EiuVunUewCse2syVgA2AfSRco3LyAv07B/zCOMa+jpXI9ll+Q21Nc6FAlYPcpNcAXqBzMhNs1CD/pP2bA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@smithy/middleware-content-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.2.tgz", @@ -5707,13 +6610,13 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.2.tgz", - "integrity": "sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.3.tgz", + "integrity": "sha512-JtaY3FxmD+te+KSI2FJuEcfNC9T/DGGVf551babM7fAaXhjJUt7oSYurH1Devxd2+BOSUACCgt3buinx4UnmEA==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/abort-controller": "^4.0.2", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -6073,80 +6976,72 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", - "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", + "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/type-utils": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/type-utils": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.1" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", - "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", + "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", + "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -6154,41 +7049,37 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", - "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", + "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/utils": "8.30.1", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.1" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", + "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -6196,75 +7087,87 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", + "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.1" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", - "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", + "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", + "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.30.1", + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", @@ -6870,16 +7773,6 @@ "dev": true, "license": "MIT" }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/asn1.js": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", @@ -8537,19 +9430,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -9913,27 +10793,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globrex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", @@ -14154,16 +15013,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/ts-jest": { diff --git a/backend/package.json b/backend/package.json index 4ddc6d7e..fc11e452 100644 --- a/backend/package.json +++ b/backend/package.json @@ -30,6 +30,7 @@ "@aws-sdk/client-bedrock": "^3.782.0", "@aws-sdk/client-bedrock-runtime": "^3.782.0", "@aws-sdk/client-dynamodb": "^3.758.0", + "@aws-sdk/client-s3": "^3.787.0", "@aws-sdk/client-secrets-manager": "^3.758.0", "@aws-sdk/client-textract": "^3.782.0", "@aws-sdk/util-dynamodb": "^3.758.0", @@ -74,8 +75,8 @@ "@types/jwk-to-pem": "^2.0.2", "@types/multer": "^1.4.12", "@types/node": "^20.12.7", - "@typescript-eslint/eslint-plugin": "^7.9.0", - "@typescript-eslint/parser": "^7.9.0", + "@typescript-eslint/eslint-plugin": "^8.30.1", + "@typescript-eslint/parser": "^8.30.1", "@vitest/coverage-v8": "^3.1.1", "aws-cdk": "2.139.0", "aws-cdk-lib": "^2.185.0", diff --git a/backend/src/app.module.spec.ts b/backend/src/app.module.spec.ts index 9de63d57..018a5d00 100644 --- a/backend/src/app.module.spec.ts +++ b/backend/src/app.module.spec.ts @@ -49,7 +49,6 @@ describe('AppModule', () => { .useValue({ generateResponse: vi.fn().mockResolvedValue('test response'), analyzeMedicalDocument: vi.fn().mockResolvedValue({ - keyMedicalTerms: [], labValues: [], diagnoses: [], metadata: { diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 315508da..7d1eb538 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -26,6 +26,13 @@ import { DocumentProcessorModule } from './document-processor/document-processor }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { - consumer.apply(AuthMiddleware).exclude('document-processor/(.*)').forRoutes('*'); + consumer + .apply(AuthMiddleware) + .exclude( + 'document-processor/upload', + 'document-processor/test', + 'document-processor/test-form', + ) + .forRoutes('*'); } } diff --git a/backend/src/config/configuration.ts b/backend/src/config/configuration.ts index 0b98dec5..357d5ef0 100644 --- a/backend/src/config/configuration.ts +++ b/backend/src/config/configuration.ts @@ -3,6 +3,9 @@ export default () => ({ environment: process.env.NODE_ENV || 'development', aws: { region: process.env.AWS_REGION || 'us-east-1', + s3: { + uploadBucket: process.env.S3_UPLOAD_BUCKET || '', + }, cognito: { userPoolId: process.env.AWS_COGNITO_USER_POOL_ID, clientId: process.env.AWS_COGNITO_CLIENT_ID, @@ -35,4 +38,6 @@ export default () => ({ model: process.env.PERPLEXITY_MODEL || 'sonar', maxTokens: parseInt(process.env.PERPLEXITY_MAX_TOKENS || '2048', 10), }, + dynamodbReportsTable: + process.env.DYNAMODB_REPORTS_TABLE || 'AIMedicalReportReportsTabledevelopment', }); diff --git a/backend/src/document-processor/controllers/document-processor.controller.ts b/backend/src/document-processor/controllers/document-processor.controller.ts index 2c8851a7..5648b25a 100644 --- a/backend/src/document-processor/controllers/document-processor.controller.ts +++ b/backend/src/document-processor/controllers/document-processor.controller.ts @@ -8,6 +8,10 @@ import { Logger, Get, Res, + Req, + UnauthorizedException, + NotFoundException, + InternalServerErrorException, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { @@ -16,12 +20,21 @@ import { } from '../services/document-processor.service'; import { Express } from 'express'; import { Response } from 'express'; +import { ReportsService } from '../../reports/reports.service'; +import { RequestWithUser } from '../../auth/auth.middleware'; +import { Readable } from 'stream'; +import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'; +import { ConfigService } from '@nestjs/config'; @Controller('document-processor') export class DocumentProcessorController { private readonly logger = new Logger(DocumentProcessorController.name); - constructor(private readonly documentProcessorService: DocumentProcessorService) {} + constructor( + private readonly documentProcessorService: DocumentProcessorService, + private readonly reportsService: ReportsService, + private readonly configService: ConfigService, + ) {} @Post('upload') @UseInterceptors(FileInterceptor('file')) @@ -35,11 +48,17 @@ export class DocumentProcessorController { } // Validate file type - const validMimeTypes = ['image/jpeg', 'image/png', 'image/tiff', 'application/pdf']; + const validMimeTypes = [ + 'image/jpeg', + 'image/png', + 'image/heic', + 'image/heif', + 'application/pdf', + ]; if (!validMimeTypes.includes(file.mimetype)) { throw new BadRequestException( - `Invalid file type: ${file.mimetype}. Supported types: JPEG, PNG, TIFF, and PDF.`, + `Invalid file type: ${file.mimetype}. Supported types: JPEG, PNG, HEIC, HEIF, and PDF.`, ); } @@ -63,7 +82,6 @@ export class DocumentProcessorController { // Process the document const result = await this.documentProcessorService.processDocument( file.buffer, - file.mimetype, effectiveUserId, ); @@ -95,6 +113,138 @@ export class DocumentProcessorController { } } + @Post('process-file') + async processFileFromPath( + @Body('filePath') filePath: string, + @Req() request: RequestWithUser, + ): Promise { + if (!filePath) { + throw new BadRequestException('No filePath provided'); + } + + // Extract userId from the request (attached by auth middleware) + const userId = request.user?.sub; + if (!userId) { + throw new UnauthorizedException('User ID not found in request'); + } + + this.logger.log(`Processing document from file path: ${filePath}`); + + try { + // Fetch the associated report record from DynamoDB + const report = await this.reportsService.findByFilePath(filePath, userId); + if (!report) { + throw new NotFoundException(`Report with filePath ${filePath} not found`); + } + + // Get the file from S3 + const fileBuffer = await this.getFileFromS3(filePath); + + // Process the document + const result = await this.documentProcessorService.processDocument(fileBuffer, userId); + + // Update the report with analysis results + report.title = result.analysis.title || 'Untitled Report'; + report.category = result.analysis.category || 'general'; + report.isProcessed = true; + + // Extract lab values + report.labValues = result.analysis.labValues || []; + + // Create summary from simplified explanation or diagnoses + report.summary = + result.simplifiedExplanation || + result.analysis.diagnoses.map(d => d.condition).join(', ') || + 'No summary available'; + + report.updatedAt = new Date().toISOString(); + + // Update the report in DynamoDB + await this.reportsService.updateReport(report); + + return { + success: true, + reportId: report.id, + analysis: result.analysis, + }; + } catch (error: unknown) { + this.logger.error( + `Error processing document from path ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`, + ); + throw error; + } + } + + /** + * Retrieves a file from S3 storage + * @param filePath - The S3 key of the file + * @returns Buffer containing the file data + */ + private async getFileFromS3(filePath: string): Promise { + try { + const bucketName = this.configService.get('aws.s3.uploadBucket'); + if (!bucketName) { + throw new InternalServerErrorException('S3 bucket name not configured'); + } + + const region = this.configService.get('aws.region') || 'us-east-1'; + + // Get optional AWS credentials if they exist + const accessKeyId = this.configService.get('aws.aws.accessKeyId'); + const secretAccessKey = this.configService.get('aws.aws.secretAccessKey'); + const sessionToken = this.configService.get('aws.aws.sessionToken'); + + // Create S3 client with credentials if they exist + const s3ClientOptions: any = { region }; + + if (accessKeyId && secretAccessKey) { + s3ClientOptions.credentials = { + accessKeyId, + secretAccessKey, + ...(sessionToken && { sessionToken }), + }; + } + + const s3Client = new S3Client(s3ClientOptions); + + const command = new GetObjectCommand({ + Bucket: bucketName, + Key: filePath, + }); + + const response = await s3Client.send(command); + + // Check if response.Body exists before converting + if (!response.Body) { + throw new InternalServerErrorException('Empty response from S3'); + } + + // Convert the readable stream to a buffer + return await this.streamToBuffer(response.Body as Readable); + } catch (error) { + this.logger.error( + `Error retrieving file from S3: ${error instanceof Error ? error.message : 'Unknown error'}`, + ); + throw new InternalServerErrorException( + `Failed to retrieve file from S3: ${error instanceof Error ? error.message : 'Unknown error'}`, + ); + } + } + + /** + * Converts a readable stream to a buffer + * @param stream - The readable stream from S3 + * @returns Buffer containing the stream data + */ + private async streamToBuffer(stream: Readable): Promise { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + stream.on('data', chunk => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))); + stream.on('end', () => resolve(Buffer.concat(chunks))); + stream.on('error', reject); + }); + } + @Get('test') getTestStatus(): { status: string } { return { status: 'DocumentProcessorController is working' }; @@ -229,8 +379,8 @@ export class DocumentProcessorController {
- - + +
diff --git a/backend/src/document-processor/document-processor.module.ts b/backend/src/document-processor/document-processor.module.ts index 72b7f3cc..be76cc97 100644 --- a/backend/src/document-processor/document-processor.module.ts +++ b/backend/src/document-processor/document-processor.module.ts @@ -6,9 +6,10 @@ import { AwsBedrockService } from './services/aws-bedrock.service'; import { DocumentProcessorController } from './controllers/document-processor.controller'; import { PerplexityService } from '../services/perplexity.service'; import { AwsSecretsService } from '../services/aws-secrets.service'; +import { ReportsModule } from '../reports/reports.module'; @Module({ - imports: [ConfigModule], + imports: [ConfigModule, ReportsModule], controllers: [DocumentProcessorController], providers: [ DocumentProcessorService, diff --git a/backend/src/document-processor/services/aws-bedrock.service.spec.ts b/backend/src/document-processor/services/aws-bedrock.service.spec.ts index 95709afc..dc0bacbe 100644 --- a/backend/src/document-processor/services/aws-bedrock.service.spec.ts +++ b/backend/src/document-processor/services/aws-bedrock.service.spec.ts @@ -65,17 +65,19 @@ vi.mock('@aws-sdk/client-bedrock-runtime', () => { { type: 'text', text: JSON.stringify({ - keyMedicalTerms: [ - { term: 'RBC', definition: 'Red Blood Cells' }, - { term: 'WBC', definition: 'White Blood Cells' }, - ], + title: 'Blood Test Results', + category: 'general', labValues: [ { name: 'Hemoglobin', value: '14.2', unit: 'g/dL', normalRange: '13.5-17.5', - isAbnormal: false, + status: 'normal', + isCritical: false, + conclusion: + 'Normal hemoglobin levels indicate adequate oxygen-carrying capacity.', + suggestions: 'Continue regular health maintenance.', }, ], diagnoses: [], @@ -126,17 +128,18 @@ describe('AwsBedrockService', () => { `; const mockMedicalAnalysis: MedicalDocumentAnalysis = { - keyMedicalTerms: [ - { term: 'RBC', definition: 'Red Blood Cells' }, - { term: 'WBC', definition: 'White Blood Cells' }, - ], + title: 'Blood Test Results', + category: 'general', labValues: [ { name: 'Hemoglobin', value: '14.2', unit: 'g/dL', normalRange: '13.5-17.5', - isAbnormal: false, + status: 'normal', + isCritical: false, + conclusion: 'Normal hemoglobin levels indicate adequate oxygen-carrying capacity.', + suggestions: 'Continue regular health maintenance.', }, ], diagnoses: [], @@ -290,10 +293,8 @@ describe('AwsBedrockService', () => { const invalidResponses = [ null, {}, - { keyMedicalTerms: 'not an array' }, - { keyMedicalTerms: [], labValues: [], diagnoses: [] }, // Missing metadata + { labValues: [], diagnoses: [] }, // Missing metadata { - keyMedicalTerms: [], labValues: [], diagnoses: [], metadata: { isMedicalReport: 'not a boolean', confidence: 0.5, missingInformation: [] }, @@ -309,8 +310,21 @@ describe('AwsBedrockService', () => { // Test a valid response const validResponse: MedicalDocumentAnalysis = { - keyMedicalTerms: [], - labValues: [], + title: 'Test Report', + category: 'general', + labValues: [ + // Adding an empty lab value with required properties + { + name: 'Sample Test', + value: '0', + unit: 'units', + normalRange: '0-1', + status: 'normal', + isCritical: false, + conclusion: 'Normal test result', + suggestions: 'No action needed', + }, + ], diagnoses: [], metadata: { isMedicalReport: true, diff --git a/backend/src/document-processor/services/aws-bedrock.service.ts b/backend/src/document-processor/services/aws-bedrock.service.ts index cd42788d..06b86921 100644 --- a/backend/src/document-processor/services/aws-bedrock.service.ts +++ b/backend/src/document-processor/services/aws-bedrock.service.ts @@ -12,13 +12,17 @@ import { createHash } from 'crypto'; * Interface for medical document analysis result */ export interface MedicalDocumentAnalysis { - keyMedicalTerms: Array<{ term: string; definition: string }>; + title: string; + category: string; labValues: Array<{ name: string; value: string; unit: string; normalRange: string; - isAbnormal: boolean; + status: 'normal' | 'high' | 'low'; + isCritical: boolean; + conclusion: string; + suggestions: string; }>; diagnoses: Array<{ condition: string; details: string; recommendations: string }>; metadata: { @@ -44,17 +48,19 @@ export class AwsBedrockService { private readonly medicalAnalysisPrompt = `Please analyze this medical document carefully, with specific attention to medical lab reports. Look for and extract the following information: -1. Key medical terms visible in the document with their definitions -2. Lab test values with their normal ranges and whether they are abnormal (particularly important for blood work, metabolic panels, etc.) -3. Any diagnoses, findings, or medical observations with details and recommendations -4. Analyze if this is a medical document (lab report, test result, medical chart, prescription, etc.) and provide confidence level +1. Document title or main subject based on content +2. Document category based on organ system focus +3. Lab test values with their normal ranges and whether they are normal, high, or low (particularly important for blood work, metabolic panels, etc.) +4. Any diagnoses, findings, or medical observations with details and recommendations +5. Analyze if this is a medical document (lab report, test result, medical chart, prescription, etc.) and provide confidence level This document may be a lab report showing blood work or other test results, so please pay special attention to tables, numeric values, reference ranges, and medical terminology. Format the response as a JSON object with the following structure: { - "keyMedicalTerms": [{"term": string, "definition": string}], - "labValues": [{"name": string, "value": string, "unit": string, "normalRange": string, "isAbnormal": boolean}], + "title": string, + "category": string, + "labValues": [{"name": string, "value": string, "unit": string, "normalRange": string, "status": "normal" | "high" | "low", "isCritical": boolean, "conclusion": string, "suggestions": string}], "diagnoses": [{"condition": string, "details": string, "recommendations": string}], "metadata": { "isMedicalReport": boolean, @@ -63,6 +69,12 @@ Format the response as a JSON object with the following structure: } } +For the title field, create a concise title that summarizes what the document is about (e.g., "Complete Blood Count Results", "Liver Function Test", "MRI Report"). +For the category field, you MUST choose exactly one of these three values: +- "heart" - if the document focuses primarily on cardiac/cardiovascular issues or tests +- "brain" - if the document focuses primarily on neurological issues or brain-related tests +- "general" - for all other medical documents, or when the focus spans multiple systems + Set isMedicalReport to true if you see ANY medical content such as lab values, medical terminology, doctor's notes, or prescription information. Set confidence between 0 and 1 based on document clarity and how confident you are about the medical nature of the document. @@ -72,6 +84,10 @@ This is extremely important: If you see ANY lab values, numbers with units, or m When extracting lab values: 1. Look for tables with numeric values and reference ranges 2. Include any values even if you're not sure of the meaning +3. For each lab value, use "status" field with values "normal", "high", or "low" based on whether the value falls within, above, or below the normal range +4. Set "isCritical" to true when the value indicates an urgent medical situation. Set it to false for values that are normal or only slightly abnormal. +5. Include a "conclusion" field that provides a brief interpretation of what this value indicates about the patient's health +6. Include a "suggestions" field that provides brief recommendations based on this value EXTREMELY IMPORTANT FORMATTING INSTRUCTIONS: 1. ABSOLUTELY DO NOT START YOUR RESPONSE WITH ANY TEXT. Begin immediately with the JSON object. @@ -99,27 +115,36 @@ INCORRECT RESPONSE FORMATS (DO NOT DO THESE): "This appears to be a medical report. Here is the information extracted in the requested JSON format: { - \"keyMedicalTerms\": [...], + \"category\": \"heart\", ... }" 2) DO NOT DO THIS - Nested JSON: { - "keyMedicalTerms": [ + "labValues": [ { - "term": "Here is the information extracted", - "definition": "{\"keyMedicalTerms\": [{\"term\": \"RBC\", \"definition\": \"Red blood cells\"}]}" + "name": "Here is the information extracted", + "value": "{\"labValues\": [{\"name\": \"RBC\", \"value\": \"14.2\"}]}" } ] } CORRECT FORMAT (DO THIS): { - "keyMedicalTerms": [ - {"term": "RBC", "definition": "Red blood cells"}, - {"term": "WBC", "definition": "White blood cells"} + "title": "Complete Blood Count Results", + "category": "heart", + "labValues": [ + { + "name": "Hemoglobin", + "value": "14.2", + "unit": "g/dL", + "normalRange": "13.5-17.5", + "status": "normal", + "isCritical": false, + "conclusion": "Normal hemoglobin levels indicate adequate oxygen-carrying capacity.", + "suggestions": "Continue regular health maintenance." + } ], - "labValues": [...], "diagnoses": [...], "metadata": {...} } @@ -401,7 +426,8 @@ Document text: // Check if response has all required properties if ( !response || - !Array.isArray(response.keyMedicalTerms) || + typeof response.title !== 'string' || + typeof response.category !== 'string' || !Array.isArray(response.labValues) || !Array.isArray(response.diagnoses) || !response.metadata diff --git a/backend/src/document-processor/services/aws-textract.service.spec.ts b/backend/src/document-processor/services/aws-textract.service.spec.ts index 279fcb58..8c315187 100644 --- a/backend/src/document-processor/services/aws-textract.service.spec.ts +++ b/backend/src/document-processor/services/aws-textract.service.spec.ts @@ -5,7 +5,6 @@ import { vi, describe, it, expect, beforeEach } from 'vitest'; // Mock the security.utils module vi.mock('../../utils/security.utils', () => ({ - validateFileSecurely: vi.fn(), RateLimiter: vi.fn().mockImplementation(() => ({ tryRequest: vi.fn().mockReturnValue(true), })), @@ -173,11 +172,7 @@ describe('AwsTextractService', () => { describe('extractText', () => { it('should extract text from an image', async () => { - const result = await service.extractText( - Buffer.from('test image content'), - 'image/jpeg', - 'user-123', - ); + const result = await service.extractText(Buffer.from('test image content'), 'user-123'); expect(result).toBeDefined(); expect(result.rawText).toContain('This is a test medical report'); @@ -188,11 +183,7 @@ describe('AwsTextractService', () => { }); it('should extract text from a PDF', async () => { - const result = await service.extractText( - Buffer.from('test pdf content'), - 'application/pdf', - 'user-123', - ); + const result = await service.extractText(Buffer.from('test pdf content'), 'user-123'); expect(result).toBeDefined(); expect(result.rawText).toContain('This is a test medical report'); @@ -208,9 +199,9 @@ describe('AwsTextractService', () => { const userId = 'rate-limited-user'; // Should throw rate limit exception - await expect( - service.extractText(Buffer.from('test content'), 'image/jpeg', userId), - ).rejects.toThrow('Too many requests'); + await expect(service.extractText(Buffer.from('test content'), userId)).rejects.toThrow( + 'Too many requests', + ); // The textract API should not be called expect(mockTextractSend).not.toHaveBeenCalled(); @@ -222,11 +213,9 @@ describe('AwsTextractService', () => { const documents = [ { buffer: Buffer.from('test image 1'), - type: 'image/jpeg', }, { buffer: Buffer.from('test image 2'), - type: 'image/png', }, ]; @@ -242,7 +231,6 @@ describe('AwsTextractService', () => { it('should throw an error if batch size exceeds maximum', async () => { const documents = Array(11).fill({ buffer: Buffer.from('test image'), - type: 'image/jpeg', }); await expect(service.processBatch(documents, 'user-123')).rejects.toThrow( diff --git a/backend/src/document-processor/services/aws-textract.service.ts b/backend/src/document-processor/services/aws-textract.service.ts index 9e65db4d..c69d7830 100644 --- a/backend/src/document-processor/services/aws-textract.service.ts +++ b/backend/src/document-processor/services/aws-textract.service.ts @@ -1,7 +1,7 @@ import { Injectable, Logger, BadRequestException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { TextractClient, AnalyzeDocumentCommand, Block } from '@aws-sdk/client-textract'; -import { validateFileSecurely, RateLimiter } from '../../utils/security.utils'; +import { RateLimiter } from '../../utils/security.utils'; import { createHash } from 'crypto'; export interface ExtractedTextResult { @@ -81,37 +81,25 @@ export class AwsTextractService { /** * Extract text from a medical lab report image or PDF * @param fileBuffer The file buffer containing the image or PDF - * @param fileType The MIME type of the file (e.g., 'image/jpeg', 'application/pdf') * @param userId The authenticated user's ID for rate limiting * @returns Extracted text result with structured information */ - async extractText( - fileBuffer: Buffer, - fileType: string, - userId: string, - ): Promise { + async extractText(fileBuffer: Buffer, userId: string): Promise { try { const startTime = Date.now(); - // 1. Rate limiting check if (!this.rateLimiter.tryRequest(userId)) { throw new BadRequestException('Too many requests. Please try again later.'); } - // 2. Validate file securely - validateFileSecurely(fileBuffer, fileType); - // Add diagnostic information about the document being processed this.logger.debug('Processing document', { - fileType, fileSize: `${(fileBuffer.length / 1024).toFixed(2)} KB`, contentHashPrefix: createHash('sha256').update(fileBuffer).digest('hex').substring(0, 10), }); - // 3. Process document - const result = await this.processDocument(fileBuffer, fileType); + const result = await this.processDocument(fileBuffer); - // 4. Calculate processing time const processingTime = Date.now() - startTime; this.logger.log(`Document processed in ${processingTime}ms`, { @@ -125,7 +113,6 @@ export class AwsTextractService { // Log error securely without exposing sensitive details this.logger.error('Error processing document', { error: error instanceof Error ? error.message : 'Unknown error', - fileType, timestamp: new Date().toISOString(), userId: this.hashIdentifier(userId), }); @@ -143,13 +130,8 @@ export class AwsTextractService { /** * Process a document (image or PDF) */ - private async processDocument( - documentBuffer: Buffer, - documentType: string, - ): Promise { - this.logger.log( - `Processing ${documentType === 'application/pdf' ? 'PDF document' : 'single image'} with Textract`, - ); + private async processDocument(documentBuffer: Buffer): Promise { + this.logger.log(`Processing file with Textract`); // Use Analyze Document API for more comprehensive analysis const command = new AnalyzeDocumentCommand({ @@ -346,12 +328,12 @@ export class AwsTextractService { /** * Process multiple documents in batch - * @param documents Array of document buffers with their types + * @param documents Array of document buffers * @param userId The authenticated user's ID for rate limiting * @returns Array of extracted text results */ async processBatch( - documents: Array<{ buffer: Buffer; type: string }>, + documents: Array<{ buffer: Buffer }>, userId: string, ): Promise { // Validate batch size @@ -365,12 +347,11 @@ export class AwsTextractService { for (const doc of documents) { try { - const result = await this.extractText(doc.buffer, doc.type, userId); + const result = await this.extractText(doc.buffer, userId); results.push(result); } catch (error) { this.logger.error('Error processing document in batch', { error: error instanceof Error ? error.message : 'Unknown error', - fileType: doc.type, fileSize: doc.buffer.length, }); diff --git a/backend/src/document-processor/services/document-processor.service.spec.ts b/backend/src/document-processor/services/document-processor.service.spec.ts index 361b0eb5..c29c54b7 100644 --- a/backend/src/document-processor/services/document-processor.service.spec.ts +++ b/backend/src/document-processor/services/document-processor.service.spec.ts @@ -24,7 +24,6 @@ describe('DocumentProcessorService', () => { it('should extract text and analyze medical document', async () => { // Arrange const fileBuffer = Buffer.from('test'); - const fileType = 'application/pdf'; const userId = 'test-user'; const extractedTextResult = { @@ -35,7 +34,8 @@ describe('DocumentProcessorService', () => { }; const medicalAnalysis = { - keyMedicalTerms: [], + title: 'Test Report', + category: 'general', labValues: [], diagnoses: [], metadata: { @@ -65,10 +65,10 @@ describe('DocumentProcessorService', () => { ); // Act - const result = await testService.processDocument(fileBuffer, fileType, userId); + const result = await testService.processDocument(fileBuffer, userId); // Assert - expect(testTextractService.extractText).toHaveBeenCalledWith(fileBuffer, fileType, userId); + expect(testTextractService.extractText).toHaveBeenCalledWith(fileBuffer, userId); expect(testBedrockService.analyzeMedicalDocument).toHaveBeenCalledWith( extractedTextResult.rawText, userId, @@ -81,7 +81,6 @@ describe('DocumentProcessorService', () => { analysis: medicalAnalysis, simplifiedExplanation, processingMetadata: expect.objectContaining({ - fileType, fileSize: fileBuffer.length, }), }); @@ -90,7 +89,6 @@ describe('DocumentProcessorService', () => { it('should throw BadRequestException when text extraction fails', async () => { // Arrange const fileBuffer = Buffer.from('test'); - const fileType = 'application/pdf'; const userId = 'test-user'; // Create test-specific service with proper mocking @@ -109,7 +107,7 @@ describe('DocumentProcessorService', () => { ); // Act & Assert - await expect(testService.processDocument(fileBuffer, fileType, userId)).rejects.toThrow( + await expect(testService.processDocument(fileBuffer, userId)).rejects.toThrow( BadRequestException, ); }); @@ -147,7 +145,8 @@ describe('DocumentProcessorService', () => { keyValuePairs: [], }, analysis: { - keyMedicalTerms: [], + title: 'Document 1 Report', + category: 'general', labValues: [], diagnoses: [], metadata: { @@ -159,7 +158,6 @@ describe('DocumentProcessorService', () => { simplifiedExplanation: 'Simple explanation for document 1', processingMetadata: { processingTimeMs: 100, - fileType: 'application/pdf', fileSize: 4, }, }; @@ -172,7 +170,8 @@ describe('DocumentProcessorService', () => { keyValuePairs: [], }, analysis: { - keyMedicalTerms: [], + title: 'Document 2 Report', + category: 'general', labValues: [], diagnoses: [], metadata: { @@ -184,7 +183,6 @@ describe('DocumentProcessorService', () => { simplifiedExplanation: 'Simple explanation for document 2', processingMetadata: { processingTimeMs: 100, - fileType: 'image/jpeg', fileSize: 4, }, }; @@ -198,18 +196,8 @@ describe('DocumentProcessorService', () => { // Assert expect(processDocumentSpy).toHaveBeenCalledTimes(2); - expect(processDocumentSpy).toHaveBeenNthCalledWith( - 1, - documents[0].buffer, - documents[0].type, - userId, - ); - expect(processDocumentSpy).toHaveBeenNthCalledWith( - 2, - documents[1].buffer, - documents[1].type, - userId, - ); + expect(processDocumentSpy).toHaveBeenNthCalledWith(1, documents[0].buffer, userId); + expect(processDocumentSpy).toHaveBeenNthCalledWith(2, documents[1].buffer, userId); expect(result).toHaveLength(2); expect(result[0]).toEqual(mockResult1); expect(result[1]).toEqual(mockResult2); diff --git a/backend/src/document-processor/services/document-processor.service.ts b/backend/src/document-processor/services/document-processor.service.ts index bf4a0e7d..6b80a17e 100644 --- a/backend/src/document-processor/services/document-processor.service.ts +++ b/backend/src/document-processor/services/document-processor.service.ts @@ -13,7 +13,6 @@ export interface ProcessedDocumentResult { simplifiedExplanation?: string; processingMetadata: { processingTimeMs: number; - fileType: string; fileSize: number; }; } @@ -35,26 +34,20 @@ export class DocumentProcessorService { /** * Process a medical document by extracting text and performing analysis * @param fileBuffer The file buffer containing the image or PDF - * @param fileType The MIME type of the file (e.g., 'image/jpeg', 'application/pdf') * @param userId The authenticated user's ID for rate limiting * @returns Processed document result with extracted text, analysis, and simplified explanation */ - async processDocument( - fileBuffer: Buffer, - fileType: string, - userId: string, - ): Promise { + async processDocument(fileBuffer: Buffer, userId: string): Promise { try { const startTime = Date.now(); this.logger.log('Starting document processing', { - fileType, fileSize: `${(fileBuffer.length / 1024).toFixed(2)} KB`, userId: this.hashIdentifier(userId), }); // Step 1: Extract text from document using AWS Textract - const extractedText = await this.textractService.extractText(fileBuffer, fileType, userId); + const extractedText = await this.textractService.extractText(fileBuffer, userId); this.logger.log('Text extraction completed', { lineCount: extractedText.lines.length, @@ -91,7 +84,6 @@ export class DocumentProcessorService { this.logger.log(`Document processing completed in ${processingTime}ms`, { isMedicalReport: analysis.metadata.isMedicalReport, confidence: analysis.metadata.confidence, - keyTermCount: analysis.keyMedicalTerms.length, labValueCount: analysis.labValues.length, hasExplanation: !!simplifiedExplanation, }); @@ -103,7 +95,6 @@ export class DocumentProcessorService { simplifiedExplanation, processingMetadata: { processingTimeMs: processingTime, - fileType, fileSize: fileBuffer.length, }, }; @@ -111,7 +102,6 @@ export class DocumentProcessorService { // Log error securely without exposing sensitive details this.logger.error('Error processing document', { error: error instanceof Error ? error.message : 'Unknown error', - fileType, timestamp: new Date().toISOString(), userId: this.hashIdentifier(userId), }); @@ -146,12 +136,11 @@ export class DocumentProcessorService { for (const doc of documents) { try { - const result = await this.processDocument(doc.buffer, doc.type, userId); + const result = await this.processDocument(doc.buffer, userId); results.push(result); } catch (error) { this.logger.error('Error processing document in batch', { error: error instanceof Error ? error.message : 'Unknown error', - fileType: doc.type, fileSize: doc.buffer.length, }); @@ -164,7 +153,8 @@ export class DocumentProcessorService { keyValuePairs: [], }, analysis: { - keyMedicalTerms: [], + title: 'Failed Document', + category: 'general', labValues: [], diagnoses: [], metadata: { @@ -176,7 +166,6 @@ export class DocumentProcessorService { simplifiedExplanation: undefined, processingMetadata: { processingTimeMs: 0, - fileType: doc.type, fileSize: doc.buffer.length, }, }); diff --git a/backend/src/iac/backend-stack.ts b/backend/src/iac/backend-stack.ts index 2dc8165e..3478a662 100644 --- a/backend/src/iac/backend-stack.ts +++ b/backend/src/iac/backend-stack.ts @@ -85,7 +85,7 @@ export class BackendStack extends cdk.Stack { removalPolicy: isProd ? RemovalPolicy.RETAIN : RemovalPolicy.DESTROY, }); - // Add GSI for querying by date + // Add GSI for querying by createdAt reportsTable.addGlobalSecondaryIndex({ indexName: 'userIdDateIndex', partitionKey: { @@ -93,7 +93,7 @@ export class BackendStack extends cdk.Stack { type: AttributeType.STRING, }, sortKey: { - name: 'date', + name: 'createdAt', type: AttributeType.STRING, }, }); diff --git a/backend/src/reports/models/report.model.ts b/backend/src/reports/models/report.model.ts index 6394608d..b2d72cc6 100644 --- a/backend/src/reports/models/report.model.ts +++ b/backend/src/reports/models/report.model.ts @@ -21,6 +21,24 @@ export class Report { @ApiProperty({ description: 'Category of the report' }) category: string; + @ApiProperty({ description: 'Whether the report has been processed' }) + isProcessed: boolean; + + @ApiProperty({ description: 'List of lab values' }) + labValues: Array<{ + name: string; + value: string; + unit: string; + normalRange: string; + status: 'normal' | 'high' | 'low'; + isCritical: boolean; + conclusion: string; + suggestions: string; + }>; + + @ApiProperty({ description: 'Summary of the report' }) + summary: string; + @ApiProperty({ description: 'Status of the report', enum: ReportStatus, diff --git a/backend/src/reports/reports.service.ts b/backend/src/reports/reports.service.ts index aa8395ed..afdef573 100644 --- a/backend/src/reports/reports.service.ts +++ b/backend/src/reports/reports.service.ts @@ -27,26 +27,31 @@ export class ReportsService { private readonly logger = new Logger(ReportsService.name); constructor(private configService: ConfigService) { - const region = this.configService.get('AWS_REGION', 'us-east-1'); - const accessKeyId = this.configService.get('AWS_ACCESS_KEY_ID'); - const secretAccessKey = this.configService.get('AWS_SECRET_ACCESS_KEY'); + const region = this.configService.get('aws.region') || 'us-east-1'; - // Prepare client configuration - const clientConfig: any = { region }; + // Get optional AWS credentials if they exist + const accessKeyId = this.configService.get('aws.aws.accessKeyId'); + const secretAccessKey = this.configService.get('aws.aws.secretAccessKey'); + const sessionToken = this.configService.get('aws.aws.sessionToken'); + + const clientOptions: any = { region }; - // Only add credentials if both values are present if (accessKeyId && secretAccessKey) { - clientConfig.credentials = { accessKeyId, secretAccessKey }; + clientOptions.credentials = { + accessKeyId, + secretAccessKey, + ...(sessionToken && { sessionToken }), + }; } try { - this.dynamoClient = new DynamoDBClient(clientConfig); + this.dynamoClient = new DynamoDBClient(clientOptions); } catch (error: unknown) { this.logger.error('Failed to initialize DynamoDB client:', error); throw new InternalServerErrorException('Failed to initialize database connection'); } - this.tableName = this.configService.get('DYNAMODB_REPORTS_TABLE', 'reports'); + this.tableName = this.configService.get('dynamodbReportsTable')!; } async findAll(userId: string): Promise { @@ -270,6 +275,9 @@ export class ReportsService { title: 'New Report', bookmarked: false, category: '', + isProcessed: false, + labValues: [], + summary: '', status: ReportStatus.UNREAD, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), @@ -300,4 +308,101 @@ export class ReportsService { throw new InternalServerErrorException('Failed to save report to database'); } } + + /** + * Find a report by its filePath + * @param filePath The S3 path of the file + * @param userId User ID for authorization + * @returns Report record if found + */ + async findByFilePath(filePath: string, userId: string): Promise { + if (!filePath) { + throw new NotFoundException('File path is required'); + } + + if (!userId) { + throw new ForbiddenException('User ID is required'); + } + + try { + // Since filePath isn't a key attribute, we need to scan with filter + const command = new ScanCommand({ + TableName: this.tableName, + FilterExpression: 'filePath = :filePath AND userId = :userId', + ExpressionAttributeValues: marshall({ + ':filePath': filePath, + ':userId': userId, + }), + Limit: 1, // We only want one record + }); + + const response = await this.dynamoClient.send(command); + + if (!response.Items || response.Items.length === 0) { + return null; + } + + return unmarshall(response.Items[0]) as Report; + } catch (error: unknown) { + this.logger.error(`Error finding report with filePath ${filePath}:`); + this.logger.error(error); + + if (error instanceof DynamoDBServiceException) { + if (error.name === 'ResourceNotFoundException') { + throw new InternalServerErrorException( + `Table "${this.tableName}" not found. Please check your database configuration.`, + ); + } + } + + throw new InternalServerErrorException(`Failed to fetch report with filePath ${filePath}`); + } + } + + /** + * Update a report with new data + * @param report Updated report object + * @returns The updated report + */ + async updateReport(report: Report): Promise { + if (!report || !report.id) { + throw new NotFoundException('Report ID is required'); + } + + if (!report.userId) { + throw new ForbiddenException('User ID is required'); + } + + try { + // Update report in DynamoDB + const command = new PutItemCommand({ + TableName: this.tableName, + Item: marshall(report), + ConditionExpression: 'userId = :userId', + ExpressionAttributeValues: marshall({ + ':userId': report.userId, + }), + }); + + await this.dynamoClient.send(command); + this.logger.log(`Successfully updated report with ID ${report.id}`); + + return report; + } catch (error: unknown) { + this.logger.error(`Error updating report with ID ${report.id}:`); + this.logger.error(error); + + if (error instanceof DynamoDBServiceException) { + if (error.name === 'ConditionalCheckFailedException') { + throw new ForbiddenException('You do not have permission to update this report'); + } else if (error.name === 'ResourceNotFoundException') { + throw new InternalServerErrorException( + `Table "${this.tableName}" not found. Please check your database configuration.`, + ); + } + } + + throw new InternalServerErrorException(`Failed to update report with ID ${report.id}`); + } + } } diff --git a/backend/src/services/README.md b/backend/src/services/README.md index e4bf5dba..6f238ac1 100644 --- a/backend/src/services/README.md +++ b/backend/src/services/README.md @@ -30,7 +30,6 @@ export interface ProcessedDocumentResult { analysis: MedicalDocumentAnalysis; processingMetadata: { processingTimeMs: number; - fileType: string; fileSize: number; }; } @@ -60,13 +59,12 @@ The structured medical information from Bedrock: ```typescript export interface MedicalDocumentAnalysis { - keyMedicalTerms: Array<{ term: string; definition: string }>; labValues: Array<{ name: string; value: string; unit: string; normalRange: string; - isAbnormal: boolean; + isNormal: 'normal' | 'high' | 'low'; }>; diagnoses: Array<{ condition: string; details: string; recommendations: string }>; metadata: { @@ -112,16 +110,13 @@ curl -X POST \ ] }, "analysis": { - "keyMedicalTerms": [ - { "term": "Hemoglobin", "definition": "Oxygen-carrying protein in red blood cells" } - ], "labValues": [ { "name": "Hemoglobin", "value": "14.2", "unit": "g/dL", "normalRange": "13.5-17.5", - "isAbnormal": false + "isNormal": "normal" } ], "diagnoses": [], @@ -133,7 +128,6 @@ curl -X POST \ }, "processingMetadata": { "processingTimeMs": 2345, - "fileType": "application/pdf", "fileSize": 12345 } } @@ -146,17 +140,16 @@ curl -X POST \ constructor(private readonly documentProcessorService: DocumentProcessorService) {} // Process a document -async processReport(fileBuffer: Buffer, fileType: string, userId: string) { +async processReport(fileBuffer: Buffer, userId: string) { try { const result = await this.documentProcessorService.processDocument( fileBuffer, - fileType, userId ); // Use the structured medical data const labValues = result.analysis.labValues; - const abnormalValues = labValues.filter(lab => lab.isAbnormal); + const abnormalValues = labValues.filter(lab => lab.isNormal !== 'normal'); return result; } catch (error) { @@ -179,8 +172,8 @@ The service supports batch processing of multiple documents: ```typescript const results = await documentProcessorService.processBatch( [ - { buffer: fileBuffer1, type: fileType1 }, - { buffer: fileBuffer2, type: fileType2 } + { buffer: fileBuffer1 }, + { buffer: fileBuffer2 } ], userId ); @@ -214,4 +207,4 @@ Planned future enhancements: - Support for multi-page PDF processing using async APIs - Enhanced lab report detection and categorization - Integration with medical terminology databases -- OCR preprocessing for low-quality images \ No newline at end of file +- OCR preprocessing for low-quality images \ No newline at end of file diff --git a/backend/src/services/aws-secrets.service.ts b/backend/src/services/aws-secrets.service.ts index 54285baf..9d1517fc 100644 --- a/backend/src/services/aws-secrets.service.ts +++ b/backend/src/services/aws-secrets.service.ts @@ -76,7 +76,7 @@ export class AwsSecretsService { // If it's not an object, just stringify whatever we got secretValue = String(secretJson); } - } catch (e) { + } catch { // If it's not valid JSON, just use the raw string secretValue = response.SecretString; } diff --git a/backend/src/utils/security.utils.ts b/backend/src/utils/security.utils.ts index 9313423b..26cfcc14 100644 --- a/backend/src/utils/security.utils.ts +++ b/backend/src/utils/security.utils.ts @@ -1,270 +1,4 @@ -import { BadRequestException, Logger } from '@nestjs/common'; - -// Common malicious file signatures (magic numbers) -const MALICIOUS_FILE_SIGNATURES = new Set([ - '4D5A', // MZ - Windows executable - '7F454C46', // ELF - Linux executable - '504B0304', // ZIP - Could contain malicious files - 'CAFEBABE', // Java class file -]); - -// Maximum file size (10MB for images, 20MB for PDFs) -export const MAX_FILE_SIZES = { - 'image/jpeg': 10 * 1024 * 1024, - 'image/png': 10 * 1024 * 1024, - 'image/heic': 10 * 1024 * 1024, - 'image/heif': 10 * 1024 * 1024, - 'application/pdf': 50 * 1024 * 1024, -} as const; - -// Allowed MIME types -export const ALLOWED_MIME_TYPES = new Set(Object.keys(MAX_FILE_SIZES)); - -// Common JPEG signatures from different devices -const JPEG_SIGNATURES = new Set([ - 'FFD8FF', // Standard JPEG SOI marker - 'FFD8FFE0', // JPEG/JFIF - 'FFD8FFE1', // JPEG/Exif (common in mobile phones) - 'FFD8FFE2', // JPEG/SPIFF - 'FFD8FFE3', // JPEG/JPEG-LS - 'FFD8FFE8', // JPEG/SPIFF - 'FFD8FFED', // JPEG/IPTC - 'FFD8FFEE', // JPEG/JPEG-LS -]); - -// Common PNG signatures -const PNG_SIGNATURES = new Set([ - '89504E47', // Standard PNG - '89504E470D0A1A0A', // Full PNG header -]); - -// HEIC/HEIF signatures -const HEIC_SIGNATURES = new Set([ - '00000020667479706865696300', // HEIC - '0000001C667479706D696631', // HEIF - '00000018667479706D696631', // HEIF variation -]); - -// Common PDF signatures -const PDF_SIGNATURES = new Set([ - '25504446', // %PDF -]); - -/** - * Checks if a buffer starts with any of the malicious file signatures - */ -const hasExecutableSignature = (buffer: Buffer): boolean => { - // Get first 4 bytes as hex - const signature = buffer.slice(0, 4).toString('hex').toUpperCase(); - return MALICIOUS_FILE_SIGNATURES.has(signature); -}; - -/** - * Validates the actual content type of a file using its magic numbers - */ -const validateFileType = (buffer: Buffer, mimeType: string): boolean => { - // Get first 12 bytes to check for various signatures - const signature = buffer.slice(0, 12).toString('hex').toUpperCase(); - - switch (mimeType) { - case 'image/jpeg': - return Array.from(JPEG_SIGNATURES).some(sig => signature.startsWith(sig)); - case 'image/png': - return Array.from(PNG_SIGNATURES).some(sig => signature.startsWith(sig)); - case 'image/heic': - case 'image/heif': - return Array.from(HEIC_SIGNATURES).some(sig => signature.startsWith(sig)); - case 'application/pdf': - return Array.from(PDF_SIGNATURES).some(sig => signature.startsWith(sig)); - default: - return false; - } -}; - -/** - * Comprehensive file security validation - * @param buffer The file buffer to validate - * @param mimeType The declared MIME type of the file - */ -export const validateFileSecurely = (buffer: Buffer, mimeType: string): void => { - const logger = new Logger('SecurityUtils'); - - // 1. Check if file type is allowed - if (!ALLOWED_MIME_TYPES.has(mimeType)) { - throw new BadRequestException('Only JPEG, PNG, and HEIC/HEIF images are allowed'); - } - - // 2. Check file size - const maxSize = MAX_FILE_SIZES[mimeType as keyof typeof MAX_FILE_SIZES]; - if (buffer.length > maxSize) { - throw new BadRequestException( - `Image size exceeds maximum limit of ${maxSize / (1024 * 1024)}MB`, - ); - } - - // 3. Validate actual file content matches claimed type - if (!validateFileType(buffer, mimeType)) { - throw new BadRequestException('File content does not match claimed image type'); - } - - // 4. Check for executable signatures - if (hasExecutableSignature(buffer)) { - throw new BadRequestException('File contains executable content'); - } - - logger.log(`File validation: type: ${mimeType}, size: ${(buffer.length / 1024).toFixed(2)}KB`); - - // 5. Basic structure validation for files - try { - validateFileStructure(buffer); - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error'; - throw new BadRequestException(`Invalid image structure: ${errorMessage}`); - } -}; - -/** - * Validates basic image structure - * Checks for proper image headers and dimensions - */ -const validateFileStructure = (buffer: Buffer): void => { - const logger = new Logger('ImageValidator'); - - if (buffer.length < 12) { - throw new Error('File too small to be a valid image'); - } - - const signature = buffer.slice(0, 12).toString('hex').toUpperCase(); - logger.log(`Image signature: ${signature.substring(0, 8)}...`); - - // Check different image types - if (isJpegSignature(signature)) { - validateJpeg(buffer, logger); - } else if (isPngSignature(signature)) { - validatePng(buffer, logger); - } else if (isHeicSignature(signature)) { - validateHeic(buffer); - } else if (isPdfSignature(signature)) { - validatePdf(buffer, logger); - } else { - throw new Error('Unsupported image format'); - } -}; - -/** - * Checks if signature matches JPEG format - */ -const isJpegSignature = (signature: string): boolean => { - return Array.from(JPEG_SIGNATURES).some(sig => signature.startsWith(sig)); -}; - -/** - * Validates JPEG structure - */ -const validateJpeg = (buffer: Buffer, logger: Logger): void => { - // Skip JPEG end marker check as some valid JPEGs might not end with standard EOI marker - // Just check if the file size is reasonable - if (buffer.length < 100) { - logger.warn('JPEG file size too small, might be corrupted'); - throw new Error('JPEG file appears to be truncated or corrupted'); - } -}; - -/** - * Checks if signature matches PNG format - */ -const isPngSignature = (signature: string): boolean => { - return signature.startsWith('89504E47'); -}; - -/** - * Validates PNG structure - */ -const validatePng = (buffer: Buffer, logger: Logger): void => { - const hasIend = buffer.includes(Buffer.from([0x49, 0x45, 0x4e, 0x44])); - - if (!hasIend) { - logger.warn('PNG missing IEND chunk'); - throw new Error('Invalid PNG structure: missing IEND chunk'); - } -}; - -/** - * Checks if signature matches HEIC/HEIF format - */ -const isHeicSignature = (signature: string): boolean => { - return Array.from(HEIC_SIGNATURES).some(sig => signature.startsWith(sig)); -}; - -/** - * Validates HEIC/HEIF structure - */ -const validateHeic = (buffer: Buffer): void => { - // HEIC/HEIF validation is more complex, we'll do basic size validation - if (buffer.length < 512) { - // HEIC files are typically larger - throw new Error('Invalid HEIC/HEIF structure'); - } -}; - -/** - * Checks if signature matches PDF format - */ -const isPdfSignature = (signature: string): boolean => { - return signature.startsWith('25504446'); -}; - -/** - * Validates PDF structure - */ -const validatePdf = (buffer: Buffer, logger: Logger): void => { - // Basic PDF validation - if (buffer.length < 512) { - // PDFs are typically larger - throw new Error('Invalid PDF structure: file too small'); - } - - // Check for EOF marker (%%EOF) - const hasEof = buffer.includes(Buffer.from([0x25, 0x25, 0x45, 0x4f, 0x46])); - - if (!hasEof) { - logger.warn('PDF missing EOF marker'); - // We'll still accept it, but log a warning - } -}; - -/** - * Sanitizes extracted medical data to prevent XSS and injection attacks - */ -export const sanitizeMedicalData = >(data: T): T => { - const sanitizeValue = (value: any): any => { - if (typeof value === 'string') { - return value - .replace(/[<>]/g, '') // Remove < and > to prevent HTML injection - .replace(/javascript:/gi, '') // Remove javascript: protocols - .replace(/data:/gi, '') // Remove data: URLs - .replace(/on\w+=/gi, '') // Remove event handlers - .trim(); - } - if (Array.isArray(value)) { - return value.map(item => sanitizeValue(item)); - } - if (value && typeof value === 'object') { - return sanitizeObject(value); - } - return value; - }; - - const sanitizeObject = (obj: Record): Record => { - const sanitized: Record = {}; - for (const [key, value] of Object.entries(obj)) { - sanitized[key] = sanitizeValue(value); - } - return sanitized; - }; - - return sanitizeObject(data) as T; -}; +import { Logger } from '@nestjs/common'; /** * Rate limiting implementation using a rolling window @@ -275,10 +9,18 @@ export class RateLimiter { private readonly windowMs: number; private readonly maxRequests: number; private readonly cleanupThreshold: number = 10000; + private readonly logger: Logger; - constructor(windowMs = 60000, maxRequests = 20) { + /** + * Creates a new RateLimiter instance + * @param windowMs The time window in milliseconds for rate limiting (default: 60000) + * @param maxRequests The maximum number of requests allowed in the time window (default: 20) + * @param loggerName Optional name for the logger (default: RateLimiter) + */ + constructor(windowMs = 60000, maxRequests = 20, loggerName = 'RateLimiter') { this.windowMs = windowMs; this.maxRequests = maxRequests; + this.logger = new Logger(loggerName); } /** @@ -298,6 +40,9 @@ export class RateLimiter { // Check if limit is reached if (timestamps.length >= this.maxRequests) { + this.logger.warn( + `Rate limit exceeded for user ${userId}: ${timestamps.length} requests in ${this.windowMs}ms`, + ); return false; } @@ -311,6 +56,53 @@ export class RateLimiter { return true; } + /** + * Checks if a user would exceed their rate limit without incrementing the counter + * @param userId The authenticated user's unique identifier + * @returns boolean True if the request would be allowed, false if rate limit would be exceeded + */ + public wouldExceedLimit(userId: string): boolean { + const now = Date.now(); + const windowStart = now - this.windowMs; + + // Get or initialize request timestamps for this user + const timestamps = this.requests.get(userId) || []; + + // Count the number of timestamps within the current window + const activeRequestCount = timestamps.filter(time => time > windowStart).length; + + // Check if limit would be reached + return activeRequestCount >= this.maxRequests; + } + + /** + * Gets the number of remaining requests allowed for a user + * @param userId The authenticated user's unique identifier + * @returns number The number of requests remaining in the current time window + */ + public getRemainingRequests(userId: string): number { + const now = Date.now(); + const windowStart = now - this.windowMs; + + // Get or initialize request timestamps for this user + const timestamps = this.requests.get(userId) || []; + + // Count the number of timestamps within the current window + const activeRequestCount = timestamps.filter(time => time > windowStart).length; + + // Calculate remaining requests + return Math.max(0, this.maxRequests - activeRequestCount); + } + + /** + * Resets the rate limit for a specific user + * @param userId The authenticated user's unique identifier + */ + public resetLimit(userId: string): void { + this.requests.delete(userId); + this.logger.log(`Rate limit reset for user ${userId}`); + } + /** * Cleans up old entries from the requests map when total size exceeds threshold * @param currentTime The current timestamp to calculate window @@ -318,6 +110,7 @@ export class RateLimiter { private cleanupOldEntries(currentTime: number): void { if (this.requests.size >= this.cleanupThreshold) { const windowStart = currentTime - this.windowMs; + let cleanedEntries = 0; // Identify users with no recent requests const usersToRemove: string[] = []; @@ -329,9 +122,11 @@ export class RateLimiter { if (activeTimestamps.length === 0) { // If no active timestamps remain, mark this user for removal usersToRemove.push(userId); + cleanedEntries++; } else if (activeTimestamps.length !== timestamps.length) { // If we filtered some timestamps, update the array this.requests.set(userId, activeTimestamps); + cleanedEntries++; } }); @@ -339,6 +134,10 @@ export class RateLimiter { usersToRemove.forEach(userId => { this.requests.delete(userId); }); + + if (cleanedEntries > 0) { + this.logger.log(`Cleaned up ${cleanedEntries} rate limit entries`); + } } } } diff --git a/frontend/src/common/models/medicalReport.ts b/frontend/src/common/models/medicalReport.ts index a200c737..fda69458 100644 --- a/frontend/src/common/models/medicalReport.ts +++ b/frontend/src/common/models/medicalReport.ts @@ -26,11 +26,6 @@ export interface MedicalReport { id: string; title: string; category: ReportCategory; - date: string; // ISO date string + createdAt: string; // ISO date string status: ReportStatus; - documentUrl?: string; - summary?: string; - content?: string; - doctor?: string; - facility?: string; } \ No newline at end of file diff --git a/frontend/src/pages/Home/components/ReportItem/ReportItem.tsx b/frontend/src/pages/Home/components/ReportItem/ReportItem.tsx index b3a1280e..dff3efc9 100644 --- a/frontend/src/pages/Home/components/ReportItem/ReportItem.tsx +++ b/frontend/src/pages/Home/components/ReportItem/ReportItem.tsx @@ -15,11 +15,11 @@ interface ReportItemProps { */ const ReportItem: React.FC = ({ report, onClick }) => { const { t } = useTranslation(); - const { title, category, date, status } = report; + const { title, category, createdAt, status } = report; const isUnread = status === ReportStatus.UNREAD; - - // Format the date from ISO string to MM/DD/YYYY - const formattedDate = format(new Date(date), 'MM/dd/yyyy'); + + // Format the createdAt from ISO string to MM/DD/YYYY + const formattedDate = format(new Date(createdAt), 'MM/dd/yyyy'); // Get the appropriate icon based on report category const getCategoryIcon = () => {