diff --git a/api/.env.example b/api/.env.example index 352ed8fb9..ef8c61e01 100644 --- a/api/.env.example +++ b/api/.env.example @@ -69,3 +69,11 @@ AZURE_KEYVAULT_ACCOUNT= # Azure KeyVault account name CLIENT_ID= # Active Directory app client ID CERTIFICATE_THUMBPRINT= # AD app certificate thumbprint REFRESH_CREDENTIALS_INTERVAL=86400000 # Credential refresh interval (in ms, default: 1 day) + + +# ============================== +# AWS Configuration +# ============================== +AWS_REGION=us-east-1 # AWS region +AWS_ACCESS_KEY_ID= # AWS access key ID +AWS_SECRET_ACCESS_KEY= # AWS secret access key diff --git a/api/package-lock.json b/api/package-lock.json index e102e2e9c..231222100 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -8,12 +8,17 @@ "name": "code-push-server", "version": "0.0.1", "dependencies": { + "@aws-sdk/client-dynamodb": "^3.709.0", + "@aws-sdk/client-s3": "^3.709.0", + "@aws-sdk/client-secrets-manager": "^3.709.0", + "@aws-sdk/util-dynamodb": "^3.709.0", "@azure/data-tables": "^13.2.2", "@azure/identity": "^4.2.1", "@azure/keyvault-secrets": "^4.7.0", "@azure/storage-blob": "^12.14.0", "@azure/storage-queue": "^12.13.0", "applicationinsights": "^2.7.0", + "aws-sdk": "^2.1692.0", "body-parser": "^1.20.2", "cookie-session": "^2.0.0", "ejs": "^3.1.10", @@ -71,863 +76,2674 @@ "node": ">=0.10.0" } }, - "node_modules/@azure/abort-controller": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", - "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.2.0" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/core-auth": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.7.2.tgz", - "integrity": "sha512-Igm/S3fDYmnMq1uKS38Ae1/m37B3zigdlZw+kocwEhh5GjyKjPrXKO2J6rzpC1wAxrNil/jX9BJRqBshyjnF3g==", - "license": "MIT", + "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": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-util": "^1.1.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" } }, - "node_modules/@azure/core-auth/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", - "license": "MIT", + "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": ">=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/core-client": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", - "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", + "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": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.4.0", - "@azure/core-rest-pipeline": "^1.9.1", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.6.1", - "@azure/logger": "^1.0.0", + "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/core-client/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "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": ">=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/core-http": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.2.tgz", - "integrity": "sha512-o1wR9JrmoM0xEAa0Ue7Sp8j+uJvmqYaGoHOCT5qaVYmvgmnZDC0OvQimPA/JR3u77Sz6D1y3Xmk1y69cDU9q9A==", + "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", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-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": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/core-util": "^1.1.1", - "@azure/logger": "^1.0.0", - "@types/node-fetch": "^2.5.0", - "@types/tunnel": "^0.0.3", - "form-data": "^4.0.0", - "node-fetch": "^2.6.7", - "process": "^0.11.10", - "tslib": "^2.2.0", - "tunnel": "^0.0.6", - "uuid": "^8.3.0", - "xml2js": "^0.5.0" + "tslib": "^2.6.2" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@azure/core-http-compat": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-1.3.0.tgz", - "integrity": "sha512-ZN9avruqbQ5TxopzG3ih3KRy52n8OAbitX3fnZT5go4hzu0J+KVPSzkL+Wt3hpJpdG8WIfg1sBD1tWkgUdEpBA==", + "node_modules/@aws-crypto/sha256-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": { - "@azure/abort-controller": "^1.0.4", - "@azure/core-client": "^1.3.0", - "@azure/core-rest-pipeline": "^1.3.0" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/core-http/node_modules/@azure/core-tracing": { - "version": "1.0.0-preview.13", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", - "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "node_modules/@aws-crypto/sha256-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": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/core-http/node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=4.0.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/core-http/node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "engines": { - "node": ">=4.0" + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" } }, - "node_modules/@azure/core-lro": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.3.tgz", - "integrity": "sha512-ubkOf2YCnVtq7KqEJQqAI8dDD5rH1M6OP5kW0KO/JQyTaxLA0N0pjFWvvaysCj9eHMNBcuuoZXhhl0ypjod2DA==", + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-util": "^1.2.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/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/@azure/core-paging": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz", - "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", + "node_modules/@aws-crypto/util/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": { - "tslib": "^2.2.0" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@azure/core-rest-pipeline": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.11.0.tgz", - "integrity": "sha512-nB4KXl6qAyJmBVLWA7SakT4tzpYZTCk4pvRBeI+Ye0WYSOrlTqlMhc4MSS/8atD3ufeYWdkN380LLoXlUUzThw==", + "node_modules/@aws-crypto/util/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": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.4.0", - "@azure/core-tracing": "^1.0.1", - "@azure/core-util": "^1.3.0", - "@azure/logger": "^1.0.0", - "form-data": "^4.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "tslib": "^2.2.0" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@azure/core-tracing": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", - "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", - "dependencies": { - "tslib": "^2.2.0" + "node_modules/@aws-sdk/client-dynamodb": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.709.0.tgz", + "integrity": "sha512-p/GVuEgfPccFUm5lxr7EPi5gQAsUO4SDdKcIV+v/dNwtH2SXEgnFN0o1TEIJtuVY3BsQyXyR1aMjeQ81O832kw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.709.0", + "@aws-sdk/client-sts": "3.709.0", + "@aws-sdk/core": "3.709.0", + "@aws-sdk/credential-provider-node": "3.709.0", + "@aws-sdk/middleware-endpoint-discovery": "3.709.0", + "@aws-sdk/middleware-host-header": "3.709.0", + "@aws-sdk/middleware-logger": "3.709.0", + "@aws-sdk/middleware-recursion-detection": "3.709.0", + "@aws-sdk/middleware-user-agent": "3.709.0", + "@aws-sdk/region-config-resolver": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@aws-sdk/util-endpoints": "3.709.0", + "@aws-sdk/util-user-agent-browser": "3.709.0", + "@aws-sdk/util-user-agent-node": "3.709.0", + "@smithy/config-resolver": "^3.0.13", + "@smithy/core": "^2.5.5", + "@smithy/fetch-http-handler": "^4.1.2", + "@smithy/hash-node": "^3.0.11", + "@smithy/invalid-dependency": "^3.0.11", + "@smithy/middleware-content-length": "^3.0.13", + "@smithy/middleware-endpoint": "^3.2.5", + "@smithy/middleware-retry": "^3.0.30", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/node-http-handler": "^3.3.2", + "@smithy/protocol-http": "^4.1.8", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.30", + "@smithy/util-defaults-mode-node": "^3.0.30", + "@smithy/util-endpoints": "^2.1.7", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.2.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.709.0.tgz", + "integrity": "sha512-IvC7coELoQ4YenTdULArVdL5yk6jNRVUALX1aqv9JlPdrXxb3Om6YrM9e7AlSTLxrULTsAe1ubm8i/DmcSY/Ng==", + "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/client-sso-oidc": "3.709.0", + "@aws-sdk/client-sts": "3.709.0", + "@aws-sdk/core": "3.709.0", + "@aws-sdk/credential-provider-node": "3.709.0", + "@aws-sdk/middleware-bucket-endpoint": "3.709.0", + "@aws-sdk/middleware-expect-continue": "3.709.0", + "@aws-sdk/middleware-flexible-checksums": "3.709.0", + "@aws-sdk/middleware-host-header": "3.709.0", + "@aws-sdk/middleware-location-constraint": "3.709.0", + "@aws-sdk/middleware-logger": "3.709.0", + "@aws-sdk/middleware-recursion-detection": "3.709.0", + "@aws-sdk/middleware-sdk-s3": "3.709.0", + "@aws-sdk/middleware-ssec": "3.709.0", + "@aws-sdk/middleware-user-agent": "3.709.0", + "@aws-sdk/region-config-resolver": "3.709.0", + "@aws-sdk/signature-v4-multi-region": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@aws-sdk/util-endpoints": "3.709.0", + "@aws-sdk/util-user-agent-browser": "3.709.0", + "@aws-sdk/util-user-agent-node": "3.709.0", + "@aws-sdk/xml-builder": "3.709.0", + "@smithy/config-resolver": "^3.0.13", + "@smithy/core": "^2.5.5", + "@smithy/eventstream-serde-browser": "^3.0.14", + "@smithy/eventstream-serde-config-resolver": "^3.0.11", + "@smithy/eventstream-serde-node": "^3.0.13", + "@smithy/fetch-http-handler": "^4.1.2", + "@smithy/hash-blob-browser": "^3.1.10", + "@smithy/hash-node": "^3.0.11", + "@smithy/hash-stream-node": "^3.1.10", + "@smithy/invalid-dependency": "^3.0.11", + "@smithy/md5-js": "^3.0.11", + "@smithy/middleware-content-length": "^3.0.13", + "@smithy/middleware-endpoint": "^3.2.5", + "@smithy/middleware-retry": "^3.0.30", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/node-http-handler": "^3.3.2", + "@smithy/protocol-http": "^4.1.8", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.30", + "@smithy/util-defaults-mode-node": "^3.0.30", + "@smithy/util-endpoints": "^2.1.7", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "@smithy/util-stream": "^3.3.2", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.709.0.tgz", + "integrity": "sha512-AiNuMTCqc2gt6n5sjtcnEGvDgW+PJPcDSWi9BB/0dUStqRsrJ6LqqQXWs0sXixHWX1PSkCZeYCt/4dw5VOwwag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.709.0", + "@aws-sdk/client-sts": "3.709.0", + "@aws-sdk/core": "3.709.0", + "@aws-sdk/credential-provider-node": "3.709.0", + "@aws-sdk/middleware-host-header": "3.709.0", + "@aws-sdk/middleware-logger": "3.709.0", + "@aws-sdk/middleware-recursion-detection": "3.709.0", + "@aws-sdk/middleware-user-agent": "3.709.0", + "@aws-sdk/region-config-resolver": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@aws-sdk/util-endpoints": "3.709.0", + "@aws-sdk/util-user-agent-browser": "3.709.0", + "@aws-sdk/util-user-agent-node": "3.709.0", + "@smithy/config-resolver": "^3.0.13", + "@smithy/core": "^2.5.5", + "@smithy/fetch-http-handler": "^4.1.2", + "@smithy/hash-node": "^3.0.11", + "@smithy/invalid-dependency": "^3.0.11", + "@smithy/middleware-content-length": "^3.0.13", + "@smithy/middleware-endpoint": "^3.2.5", + "@smithy/middleware-retry": "^3.0.30", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/node-http-handler": "^3.3.2", + "@smithy/protocol-http": "^4.1.8", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.30", + "@smithy/util-defaults-mode-node": "^3.0.30", + "@smithy/util-endpoints": "^2.1.7", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "@smithy/util-utf8": "^3.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/@azure/core-util": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.9.2.tgz", - "integrity": "sha512-l1Qrqhi4x1aekkV+OlcqsJa4AnAkj5p0JV8omgwjaV9OAbP41lvrMvs+CptfetKkeEaGRGSzby7sjPZEX7+kkQ==", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^2.0.0", + "node_modules/@aws-sdk/client-sso": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.709.0.tgz", + "integrity": "sha512-Qxeo8cN0jNy6Wnbqq4wucffAGJM6sJjofoTgNtPA6cC7sPYx7aYC6OAAAo6NaMRY+WywOKdS9Wgjx2QYRxKx7w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.709.0", + "@aws-sdk/middleware-host-header": "3.709.0", + "@aws-sdk/middleware-logger": "3.709.0", + "@aws-sdk/middleware-recursion-detection": "3.709.0", + "@aws-sdk/middleware-user-agent": "3.709.0", + "@aws-sdk/region-config-resolver": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@aws-sdk/util-endpoints": "3.709.0", + "@aws-sdk/util-user-agent-browser": "3.709.0", + "@aws-sdk/util-user-agent-node": "3.709.0", + "@smithy/config-resolver": "^3.0.13", + "@smithy/core": "^2.5.5", + "@smithy/fetch-http-handler": "^4.1.2", + "@smithy/hash-node": "^3.0.11", + "@smithy/invalid-dependency": "^3.0.11", + "@smithy/middleware-content-length": "^3.0.13", + "@smithy/middleware-endpoint": "^3.2.5", + "@smithy/middleware-retry": "^3.0.30", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/node-http-handler": "^3.3.2", + "@smithy/protocol-http": "^4.1.8", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.30", + "@smithy/util-defaults-mode-node": "^3.0.30", + "@smithy/util-endpoints": "^2.1.7", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.709.0.tgz", + "integrity": "sha512-1w6egz17QQy661lNCRmZZlqIANEbD6g2VFAQIJbVwSiu7brg+GUns+mT1eLLLHAMQc1sL0Ds8/ybSK2SrgGgIA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.709.0", + "@aws-sdk/credential-provider-node": "3.709.0", + "@aws-sdk/middleware-host-header": "3.709.0", + "@aws-sdk/middleware-logger": "3.709.0", + "@aws-sdk/middleware-recursion-detection": "3.709.0", + "@aws-sdk/middleware-user-agent": "3.709.0", + "@aws-sdk/region-config-resolver": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@aws-sdk/util-endpoints": "3.709.0", + "@aws-sdk/util-user-agent-browser": "3.709.0", + "@aws-sdk/util-user-agent-node": "3.709.0", + "@smithy/config-resolver": "^3.0.13", + "@smithy/core": "^2.5.5", + "@smithy/fetch-http-handler": "^4.1.2", + "@smithy/hash-node": "^3.0.11", + "@smithy/invalid-dependency": "^3.0.11", + "@smithy/middleware-content-length": "^3.0.13", + "@smithy/middleware-endpoint": "^3.2.5", + "@smithy/middleware-retry": "^3.0.30", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/node-http-handler": "^3.3.2", + "@smithy/protocol-http": "^4.1.8", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.30", + "@smithy/util-defaults-mode-node": "^3.0.30", + "@smithy/util-endpoints": "^2.1.7", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.709.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.709.0.tgz", + "integrity": "sha512-cBAvlPg6yslXNL385UUGFPw+XY+lA9BzioNdIFkMo3fEUlTShogTtiWz4LsyLHoN6LhKojssP9DSmmWKWjCZIw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.709.0", + "@aws-sdk/core": "3.709.0", + "@aws-sdk/credential-provider-node": "3.709.0", + "@aws-sdk/middleware-host-header": "3.709.0", + "@aws-sdk/middleware-logger": "3.709.0", + "@aws-sdk/middleware-recursion-detection": "3.709.0", + "@aws-sdk/middleware-user-agent": "3.709.0", + "@aws-sdk/region-config-resolver": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@aws-sdk/util-endpoints": "3.709.0", + "@aws-sdk/util-user-agent-browser": "3.709.0", + "@aws-sdk/util-user-agent-node": "3.709.0", + "@smithy/config-resolver": "^3.0.13", + "@smithy/core": "^2.5.5", + "@smithy/fetch-http-handler": "^4.1.2", + "@smithy/hash-node": "^3.0.11", + "@smithy/invalid-dependency": "^3.0.11", + "@smithy/middleware-content-length": "^3.0.13", + "@smithy/middleware-endpoint": "^3.2.5", + "@smithy/middleware-retry": "^3.0.30", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/node-http-handler": "^3.3.2", + "@smithy/protocol-http": "^4.1.8", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.30", + "@smithy/util-defaults-mode-node": "^3.0.30", + "@smithy/util-endpoints": "^2.1.7", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@azure/core-util/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", - "license": "MIT", + "node_modules/@aws-sdk/core": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.709.0.tgz", + "integrity": "sha512-7kuSpzdOTAE026j85wq/fN9UDZ70n0OHw81vFqMWwlEFtm5IQ/MRCLKcC4HkXxTdfy1PqFlmoXxWqeBa15tujw==", + "license": "Apache-2.0", "dependencies": { + "@aws-sdk/types": "3.709.0", + "@smithy/core": "^2.5.5", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/signature-v4": "^4.2.4", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/core-xml": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.3.3.tgz", - "integrity": "sha512-Go/xGz7nGqVINsD9O7gOfe8uiR1S+IFcw9WTUPJHSzoFT6F5ZWjXIIlSikLZm77TtmxzXGnQYjjiZIoIZ4x14A==", + "node_modules/@aws-sdk/core/node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", "dependencies": { - "fast-xml-parser": "^4.0.8", - "tslib": "^2.2.0" + "strnum": "^1.0.5" }, - "engines": { - "node": ">=14.0.0" + "bin": { + "fxparser": "src/cli/cli.js" } }, - "node_modules/@azure/data-tables": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@azure/data-tables/-/data-tables-13.2.2.tgz", - "integrity": "sha512-Dq2Aq0mMMF0BPzYQKdBY/OtO7VemP/foh6z+mJpUO1hRL+65C1rGQUJf20LJHotSyU8wHb4HJzOs+Z50GXSy1w==", + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.709.0.tgz", + "integrity": "sha512-ZMAp9LSikvHDFVa84dKpQmow6wsg956Um20cKuioPpX2GGreJFur7oduD+tRJT6FtIOHn+64YH+0MwiXLhsaIQ==", + "license": "Apache-2.0", "dependencies": { - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.0.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-rest-pipeline": "^1.1.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-xml": "^1.0.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0", - "uuid": "^8.3.0" + "@aws-sdk/core": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@smithy/property-provider": "^3.1.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/identity": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.4.1.tgz", - "integrity": "sha512-DwnG4cKFEM7S3T+9u05NstXU/HN0dk45kPOinUyNKsn5VWwpXd9sbPKEg6kgJzGbm1lMuhx9o31PVbCtM5sfBA==", + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.709.0.tgz", + "integrity": "sha512-lIS7XLwCOyJnLD70f+VIRr8DNV1HPQe9oN6aguYrhoczqz7vDiVZLe3lh714cJqq9rdxzFypK5DqKHmcscMEPQ==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.5.0", - "@azure/core-client": "^1.9.2", - "@azure/core-rest-pipeline": "^1.1.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.3.0", - "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^3.14.0", - "@azure/msal-node": "^2.9.2", - "events": "^3.0.0", - "jws": "^4.0.0", - "open": "^8.0.0", - "stoppable": "^1.1.0", - "tslib": "^2.2.0" + "@aws-sdk/core": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@smithy/fetch-http-handler": "^4.1.2", + "@smithy/node-http-handler": "^3.3.2", + "@smithy/property-provider": "^3.1.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", + "@smithy/util-stream": "^3.3.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/keyvault-secrets": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@azure/keyvault-secrets/-/keyvault-secrets-4.7.0.tgz", - "integrity": "sha512-YvlFXRQ+SI5NT4GtSFbb6HGo6prW3yzDab8tr6vga2/SjDQew3wJsCAAr/xwZz6XshFXCYEX26CDKmPf+SJKJg==", + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.709.0.tgz", + "integrity": "sha512-qCF8IIGcPoUp+Ib3ANhbF5gElxFd+kIrtv2/1tKdvhudMANstQbMiWV0LTH47ZZR6c3as4iSrm09NZnpEoD/pA==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.5.0", - "@azure/core-http-compat": "^1.3.0", - "@azure/core-lro": "^2.2.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-rest-pipeline": "^1.8.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.0.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" + "@aws-sdk/core": "3.709.0", + "@aws-sdk/credential-provider-env": "3.709.0", + "@aws-sdk/credential-provider-http": "3.709.0", + "@aws-sdk/credential-provider-process": "3.709.0", + "@aws-sdk/credential-provider-sso": "3.709.0", + "@aws-sdk/credential-provider-web-identity": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@smithy/credential-provider-imds": "^3.2.8", + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.709.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.709.0.tgz", + "integrity": "sha512-4HRX9KYWPSjO5O/Vg03YAsebKpvTjTvpK1n7zHYBmlLMBLxUrVsL1nNKKC5p2/7OW3RL8XR1ki3QkoV7kGRxUQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.709.0", + "@aws-sdk/credential-provider-http": "3.709.0", + "@aws-sdk/credential-provider-ini": "3.709.0", + "@aws-sdk/credential-provider-process": "3.709.0", + "@aws-sdk/credential-provider-sso": "3.709.0", + "@aws-sdk/credential-provider-web-identity": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@smithy/credential-provider-imds": "^3.2.8", + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@azure/logger": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", - "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.709.0.tgz", + "integrity": "sha512-IAC+jPlGQII6jhIylHOwh3RgSobqlgL59nw2qYTURr8hMCI0Z1p5y2ee646HTVt4WeCYyzUAXfxr6YI/Vitv+Q==", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.2.0" + "@aws-sdk/core": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/msal-browser": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.23.0.tgz", - "integrity": "sha512-+QgdMvaeEpdtgRTD7AHHq9aw8uga7mXVHV1KshO1RQ2uI5B55xJ4aEpGlg/ga3H+0arEVcRfT4ZVmX7QLXiCVw==", - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.709.0.tgz", + "integrity": "sha512-rYdTDOxazS2GdGScelsRK5CAkktRLCCdRjlwXaxrcW57j749hEqxcF5uTv9RD6WBwInfedcSywErNZB+hylQlg==", + "license": "Apache-2.0", "dependencies": { - "@azure/msal-common": "14.14.2" + "@aws-sdk/client-sso": "3.709.0", + "@aws-sdk/core": "3.709.0", + "@aws-sdk/token-providers": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.8.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/msal-common": { - "version": "14.14.2", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.14.2.tgz", - "integrity": "sha512-XV0P5kSNwDwCA/SjIxTe9mEAsKB0NqGNSuaVrkCCE2lAyBr/D6YtD80Vkdp4tjWnPFwjzkwldjr1xU/facOJog==", - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.709.0.tgz", + "integrity": "sha512-2lbDfE0IQ6gma/7BB2JpkjW5G0wGe4AS0x80oybYAYYviJmUtIR3Cn2pXun6bnAWElt4wYKl4su7oC36rs5rNA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@smithy/property-provider": "^3.1.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.8.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.709.0" } }, - "node_modules/@azure/msal-node": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.13.1.tgz", - "integrity": "sha512-sijfzPNorKt6+9g1/miHwhj6Iapff4mPQx1azmmZExgzUROqWTM1o3ACyxDja0g47VpowFy/sxTM/WsuCyXTiw==", - "license": "MIT", + "node_modules/@aws-sdk/endpoint-cache": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.693.0.tgz", + "integrity": "sha512-/zK0ZZncBf5FbTfo8rJMcQIXXk4Ibhe5zEMiwFNivVPR2uNC0+oqfwXz7vjxwY0t6BPE3Bs4h9uFEz4xuGCY6w==", + "license": "Apache-2.0", "dependencies": { - "@azure/msal-common": "14.14.2", - "jsonwebtoken": "^9.0.0", - "uuid": "^8.3.0" + "mnemonist": "0.38.3", + "tslib": "^2.6.2" }, "engines": { - "node": ">=16" + "node": ">=16.0.0" } }, - "node_modules/@azure/opentelemetry-instrumentation-azure-sdk": { - "version": "1.0.0-beta.5", - "resolved": "https://registry.npmjs.org/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz", - "integrity": "sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA==", + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.709.0.tgz", + "integrity": "sha512-03+tJOd7KIZOiqWH7Z8BOfQIWkKJgjcpKOJKZ6FR2KjWGUOE1G+bo11wF4UuHQ0RmpKnApt+pQghZmSnE7WEeg==", + "license": "Apache-2.0", "dependencies": { - "@azure/core-tracing": "^1.0.0", - "@azure/logger": "^1.0.0", - "@opentelemetry/api": "^1.4.1", - "@opentelemetry/core": "^1.15.2", - "@opentelemetry/instrumentation": "^0.41.2", - "tslib": "^2.2.0" + "@aws-sdk/types": "3.709.0", + "@aws-sdk/util-arn-parser": "3.693.0", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-config-provider": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/@opentelemetry/core": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.15.2.tgz", - "integrity": "sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw==", + "node_modules/@aws-sdk/middleware-endpoint-discovery": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.709.0.tgz", + "integrity": "sha512-6CSHoAy3sVBJdeGiBpoRqVHpqLPqv5QuDxKsEMHoGdbGATmffyn2whTFfo5hfRYsN9WPz/XxUX2iynqQCnlrzw==", + "license": "Apache-2.0", "dependencies": { - "@opentelemetry/semantic-conventions": "1.15.2" + "@aws-sdk/endpoint-cache": "3.693.0", + "@aws-sdk/types": "3.709.0", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.5.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz", - "integrity": "sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw==", + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.709.0.tgz", + "integrity": "sha512-Tbl/DFvE4rHl8lMb9IzetwK4tf5R3VeHZkvEXQalsWoK0tbEQ8kXWi7wAYO4qbE7bFVvaxKX+irjJjTxf3BrCQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.709.0", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=14" + "node": ">=16.0.0" } }, - "node_modules/@azure/storage-blob": { - "version": "12.14.0", - "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.14.0.tgz", - "integrity": "sha512-g8GNUDpMisGXzBeD+sKphhH5yLwesB4JkHr1U6be/X3F+cAMcyGLPD1P89g2M7wbEtUJWoikry1rlr83nNRBzg==", + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.709.0.tgz", + "integrity": "sha512-wbYm9tkyCaqMeU82yjaXw7V5BxCSlSLNupENW63LC7Fvyo/aQzj6LjSMHcBpR2QwjBEhXCtF47L7aQ8SPTNhdw==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-http": "^3.0.0", - "@azure/core-lro": "^2.2.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/logger": "^1.0.0", - "events": "^3.0.0", - "tslib": "^2.2.0" + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-stream": "^3.3.2", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/storage-blob/node_modules/@azure/core-tracing": { - "version": "1.0.0-preview.13", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", - "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.709.0.tgz", + "integrity": "sha512-8gQYCYAaIw4lOCd5WYdf15Y/61MgRsAnrb2eiTl+icMlUOOzl8aOl5iDwm/Idp0oHZTflwxM4XSvGXO83PRWcw==", + "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" + "@aws-sdk/types": "3.709.0", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/storage-queue": { - "version": "12.13.0", - "resolved": "https://registry.npmjs.org/@azure/storage-queue/-/storage-queue-12.13.0.tgz", - "integrity": "sha512-baU7QlFwKFat8zLUMzfLHTgcpdKpvUsGl05zKVRslKIb3iPB7TPudqDfkpaFf2YL5hv88joz/fyuuYJnzz7log==", + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.709.0.tgz", + "integrity": "sha512-5YQWPXfZq7OE0jB2G0PP8K10GBod/YPJXb+1CfJS6FbQaglRoIm8KZmVEvJNnptSKyGtE62veeCcCQcfAUfFig==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-http": "^3.0.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" + "@aws-sdk/types": "3.709.0", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/storage-queue/node_modules/@azure/core-tracing": { - "version": "1.0.0-preview.13", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", - "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.709.0.tgz", + "integrity": "sha512-jDoGSccXv9zebnpUoisjWd5u5ZPIalrmm6TjvPzZ8UqzQt3Beiz0tnQwmxQD6KRc7ADweWP5Ntiqzbw9xpVajg==", + "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" + "@aws-sdk/types": "3.709.0", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", - "dev": true, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.709.0.tgz", + "integrity": "sha512-PObL/wLr4lkfbQ0yXUWaoCWu/jcwfwZzCjsUiXW/H6hW9b/00enZxmx7OhtJYaR6xmh/Lcx5wbhIoDCbzdv0tw==", + "license": "Apache-2.0", "dependencies": { - "@babel/highlight": "^7.18.6" + "@aws-sdk/types": "3.709.0", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.709.0.tgz", + "integrity": "sha512-FwtOG9t9xsLoLOQZ6qAdsWOjx9dsO6t28IjIDV1l6Ixiu2oC0Yks7goONjJUH0IDE4pDDDGzmuq0sn1XtHhheA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@aws-sdk/util-arn-parser": "3.693.0", + "@smithy/core": "^2.5.5", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/signature-v4": "^4.2.4", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-stream": "^3.3.2", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.709.0.tgz", + "integrity": "sha512-2muiLe7YkmlwZp2SKz+goZrDThGfRq3o0FcJF3Puc0XGmcEPEDjih537mCoTrGgcXNFlBc7YChd84r3t72ySaQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.709.0", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.709.0.tgz", + "integrity": "sha512-ooc9ZJvgkjPhi9q05XwSfNTXkEBEIfL4hleo5rQBKwHG3aTHvwOM7LLzhdX56QZVa6sorPBp6fwULuRDSqiQHw==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@aws-sdk/core": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@aws-sdk/util-endpoints": "3.709.0", + "@smithy/core": "^2.5.5", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.709.0.tgz", + "integrity": "sha512-/NoCAMEVKAg3kBKOrNtgOfL+ECt6nrl+L7q2SyYmrcY4tVCmwuECVqewQaHc03fTnJijfKLccw0Fj+6wOCnB6w==", + "license": "Apache-2.0", "dependencies": { - "color-convert": "^1.9.0" + "@aws-sdk/types": "3.709.0", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.709.0.tgz", + "integrity": "sha512-m0vhJEy6SLbjL11K9cHzX/ZhCIj//1GkTbYk2d4tTQFSuPyJEkjmoeHk9dYm2mJy0wH48j29OJadI1JUsR5bOw==", + "license": "Apache-2.0", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@aws-sdk/middleware-sdk-s3": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@smithy/protocol-http": "^4.1.8", + "@smithy/signature-v4": "^4.2.4", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, + "node_modules/@aws-sdk/token-providers": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.709.0.tgz", + "integrity": "sha512-q5Ar6k71nci43IbULFgC8a89d/3EHpmd7HvBzqVGRcHnoPwh8eZDBfbBXKH83NGwcS1qPSRYiDbVfeWPm4/1jA==", + "license": "Apache-2.0", "dependencies": { - "color-name": "1.1.3" + "@aws-sdk/types": "3.709.0", + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.709.0" } }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, + "node_modules/@aws-sdk/types": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.709.0.tgz", + "integrity": "sha512-ArtLTMxgjf13Kfu3gWH3Ez9Q5TkDdcRZUofpKH3pMGB/C6KAbeSCtIIDKfoRTUABzyGlPyCrZdnFjKyH+ypIpg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.693.0.tgz", + "integrity": "sha512-WC8x6ca+NRrtpAH64rWu+ryDZI3HuLwlEr8EU6/dbC/pt+r/zC0PBoC15VEygUaBA+isppCikQpGyEDu0Yj7gQ==", + "license": "Apache-2.0", "dependencies": { - "has-flag": "^3.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, + "node_modules/@aws-sdk/util-dynamodb": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.709.0.tgz", + "integrity": "sha512-rGr9+Po6Ma2BHV2hIhfXdn8hWxLtmgFzFRqqtxOlRRIDN55wkb2AYXz/ydzf4kgb+PzT8sQxtn6hf7pDkl+yAg==", + "license": "Apache-2.0", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "tslib": "^2.6.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=16.0.0" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "@aws-sdk/client-dynamodb": "^3.709.0" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", - "dev": true, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.709.0.tgz", + "integrity": "sha512-Mbc7AtL5WGCTKC16IGeUTz+sjpC3ptBda2t0CcK0kMVw3THDdcSq6ZlNKO747cNqdbwUvW34oHteUiHv4/z88Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.709.0", + "@smithy/types": "^3.7.2", + "@smithy/util-endpoints": "^2.1.7", + "tslib": "^2.6.2" + }, "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=16.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", - "dev": true, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.693.0.tgz", + "integrity": "sha512-ttrag6haJLWABhLqtg1Uf+4LgHWIMOVSYL+VYZmAp2v4PUGOwWmWQH0Zk8RM7YuQcLfH/EoR72/Yxz6A4FKcuw==", + "license": "Apache-2.0", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "tslib": "^2.6.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=16.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.709.0.tgz", + "integrity": "sha512-/rL2GasJzdTWUURCQKFldw2wqBtY4k4kCiA2tVZSKg3y4Ey7zO34SW8ebaeCE2/xoWOyLR2/etdKyphoo4Zrtg==", + "license": "Apache-2.0", "dependencies": { - "ms": "2.1.2" + "@aws-sdk/types": "3.709.0", + "@smithy/types": "^3.7.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.709.0.tgz", + "integrity": "sha512-trBfzSCVWy7ILgqhEXgiuM7hfRCw4F4a8IK90tjk9YL0jgoJ6eJuOp7+DfCtHJaygoBxD3cdMFkOu+lluFmGBA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.709.0", + "@aws-sdk/types": "3.709.0", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" }, "peerDependenciesMeta": { - "supports-color": { + "aws-crt": { "optional": true } } }, - "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/@aws-sdk/xml-builder": { + "version": "3.709.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.709.0.tgz", + "integrity": "sha512-2GPCwlNxeHspoK/Mc8nbk9cBOkSpp3j2SJUQmFnyQK6V/pR6II2oPRyZkMomug1Rc10hqlBHByMecq4zhV2uUw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", - "dev": true, + "node_modules/@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "dependencies": { + "tslib": "^2.2.0" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=12.0.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", - "dev": true, + "node_modules/@azure/core-auth": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.7.2.tgz", + "integrity": "sha512-Igm/S3fDYmnMq1uKS38Ae1/m37B3zigdlZw+kocwEhh5GjyKjPrXKO2J6rzpC1wAxrNil/jX9BJRqBshyjnF3g==", + "license": "MIT", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10.10.0" + "node": ">=18.0.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, + "node_modules/@azure/core-auth/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0" + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", + "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "node_modules/@azure/core-client/node_modules/@azure/abort-controller": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, + "node_modules/@azure/core-http": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.2.tgz", + "integrity": "sha512-o1wR9JrmoM0xEAa0Ue7Sp8j+uJvmqYaGoHOCT5qaVYmvgmnZDC0OvQimPA/JR3u77Sz6D1y3Xmk1y69cDU9q9A==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.1", + "@azure/logger": "^1.0.0", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.3", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "process": "^0.11.10", + "tslib": "^2.2.0", + "tunnel": "^0.0.6", + "uuid": "^8.3.0", + "xml2js": "^0.5.0" + }, "engines": { - "node": ">=12.22" + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-http-compat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-1.3.0.tgz", + "integrity": "sha512-ZN9avruqbQ5TxopzG3ih3KRy52n8OAbitX3fnZT5go4hzu0J+KVPSzkL+Wt3hpJpdG8WIfg1sBD1tWkgUdEpBA==", + "dependencies": { + "@azure/abort-controller": "^1.0.4", + "@azure/core-client": "^1.3.0", + "@azure/core-rest-pipeline": "^1.3.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": ">=12.0.0" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "node_modules/@azure/core-http/node_modules/@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "dependencies": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, + "node_modules/@azure/core-http/node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" }, "engines": { - "node": ">=12" + "node": ">=4.0.0" } }, - "node_modules/@jest/expect-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", - "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", - "dev": true, + "node_modules/@azure/core-lro": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.3.tgz", + "integrity": "sha512-ubkOf2YCnVtq7KqEJQqAI8dDD5rH1M6OP5kW0KO/JQyTaxLA0N0pjFWvvaysCj9eHMNBcuuoZXhhl0ypjod2DA==", "dependencies": { - "jest-get-type": "^29.4.3" + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@jest/schemas": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", - "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", - "dev": true, + "node_modules/@azure/core-paging": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz", + "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", "dependencies": { - "@sinclair/typebox": "^0.25.16" + "tslib": "^2.2.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.11.0.tgz", + "integrity": "sha512-nB4KXl6qAyJmBVLWA7SakT4tzpYZTCk4pvRBeI+Ye0WYSOrlTqlMhc4MSS/8atD3ufeYWdkN380LLoXlUUzThw==", "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "form-data": "^4.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "tslib": "^2.2.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@microsoft/applicationinsights-web-snippet": { + "node_modules/@azure/core-tracing": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-snippet/-/applicationinsights-web-snippet-1.0.1.tgz", - "integrity": "sha512-2IHAOaLauc8qaAitvWS+U931T+ze+7MNWrDHY47IENP5y2UA0vqJDu67kWZDdpCN1fFC77sfgfB+HV7SrKshnQ==" + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", + "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, + "node_modules/@azure/core-util": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.9.2.tgz", + "integrity": "sha512-l1Qrqhi4x1aekkV+OlcqsJa4AnAkj5p0JV8omgwjaV9OAbP41lvrMvs+CptfetKkeEaGRGSzby7sjPZEX7+kkQ==", + "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-util/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-xml": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.3.3.tgz", + "integrity": "sha512-Go/xGz7nGqVINsD9O7gOfe8uiR1S+IFcw9WTUPJHSzoFT6F5ZWjXIIlSikLZm77TtmxzXGnQYjjiZIoIZ4x14A==", + "dependencies": { + "fast-xml-parser": "^4.0.8", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/data-tables": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@azure/data-tables/-/data-tables-13.2.2.tgz", + "integrity": "sha512-Dq2Aq0mMMF0BPzYQKdBY/OtO7VemP/foh6z+mJpUO1hRL+65C1rGQUJf20LJHotSyU8wHb4HJzOs+Z50GXSy1w==", + "dependencies": { + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.0.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-xml": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.4.1.tgz", + "integrity": "sha512-DwnG4cKFEM7S3T+9u05NstXU/HN0dk45kPOinUyNKsn5VWwpXd9sbPKEg6kgJzGbm1lMuhx9o31PVbCtM5sfBA==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.5.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^3.14.0", + "@azure/msal-node": "^2.9.2", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/keyvault-secrets": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-secrets/-/keyvault-secrets-4.7.0.tgz", + "integrity": "sha512-YvlFXRQ+SI5NT4GtSFbb6HGo6prW3yzDab8tr6vga2/SjDQew3wJsCAAr/xwZz6XshFXCYEX26CDKmPf+SJKJg==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.5.0", + "@azure/core-http-compat": "^1.3.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-rest-pipeline": "^1.8.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", + "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.23.0.tgz", + "integrity": "sha512-+QgdMvaeEpdtgRTD7AHHq9aw8uga7mXVHV1KshO1RQ2uI5B55xJ4aEpGlg/ga3H+0arEVcRfT4ZVmX7QLXiCVw==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "14.14.2" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "14.14.2", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.14.2.tgz", + "integrity": "sha512-XV0P5kSNwDwCA/SjIxTe9mEAsKB0NqGNSuaVrkCCE2lAyBr/D6YtD80Vkdp4tjWnPFwjzkwldjr1xU/facOJog==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.13.1.tgz", + "integrity": "sha512-sijfzPNorKt6+9g1/miHwhj6Iapff4mPQx1azmmZExgzUROqWTM1o3ACyxDja0g47VpowFy/sxTM/WsuCyXTiw==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "14.14.2", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@azure/opentelemetry-instrumentation-azure-sdk": { + "version": "1.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz", + "integrity": "sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA==", + "dependencies": { + "@azure/core-tracing": "^1.0.0", + "@azure/logger": "^1.0.0", + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/core": "^1.15.2", + "@opentelemetry/instrumentation": "^0.41.2", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/@opentelemetry/core": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.15.2.tgz", + "integrity": "sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz", + "integrity": "sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@azure/storage-blob": { + "version": "12.14.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.14.0.tgz", + "integrity": "sha512-g8GNUDpMisGXzBeD+sKphhH5yLwesB4JkHr1U6be/X3F+cAMcyGLPD1P89g2M7wbEtUJWoikry1rlr83nNRBzg==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^3.0.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/logger": "^1.0.0", + "events": "^3.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/storage-blob/node_modules/@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "dependencies": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/storage-queue": { + "version": "12.13.0", + "resolved": "https://registry.npmjs.org/@azure/storage-queue/-/storage-queue-12.13.0.tgz", + "integrity": "sha512-baU7QlFwKFat8zLUMzfLHTgcpdKpvUsGl05zKVRslKIb3iPB7TPudqDfkpaFf2YL5hv88joz/fyuuYJnzz7log==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^3.0.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/storage-queue/node_modules/@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "dependencies": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", + "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", + "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@eslint/js": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", + "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.25.16" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@microsoft/applicationinsights-web-snippet": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-snippet/-/applicationinsights-web-snippet-1.0.1.tgz", + "integrity": "sha512-2IHAOaLauc8qaAitvWS+U931T+ze+7MNWrDHY47IENP5y2UA0vqJDu67kWZDdpCN1fFC77sfgfB+HV7SrKshnQ==" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.14.0.tgz", + "integrity": "sha512-MnMZ+sxsnlzloeuXL2nm5QcNczt/iO82UOeQQDHhV83F2fP3sgntW2evvtoxJki0MBLxEsh5ADD7PR/Hn5uzjw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.14.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz", + "integrity": "sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw==", + "dependencies": { + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "1.4.2", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.1", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.14.0.tgz", + "integrity": "sha512-qRfWIgBxxl3z47E036Aey0Lj2ZjlFb27Q7Xnj1y1z/P293RXJZGLtcfn/w8JF7v1Q2hs3SDGxz7Wb9Dko1YUQA==", + "dependencies": { + "@opentelemetry/core": "1.14.0", + "@opentelemetry/semantic-conventions": "1.14.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.14.0.tgz", + "integrity": "sha512-NzRGt3PS+HPKfQYMb6Iy8YYc5OKA73qDwci/6ujOIvyW9vcqBJSWbjZ8FeLEAmuatUB5WrRhEKu9b0sIiIYTrQ==", + "dependencies": { + "@opentelemetry/core": "1.14.0", + "@opentelemetry/resources": "1.14.0", + "@opentelemetry/semantic-conventions": "1.14.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.14.0.tgz", + "integrity": "sha512-rJfCY8rCWz3cb4KI6pEofnytvMPuj3YLQwoscCCYZ5DkdiPjo15IQ0US7+mjcWy9H3fcZIzf2pbJZ7ck/h4tug==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "node_modules/@smithy/abort-controller": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", + "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-4.0.0.tgz", + "integrity": "sha512-jSqRnZvkT4egkq/7b6/QRCNXmmYVcHwnJldqJ3IhVpQE2atObVJ137xmGeuGFhjFUr8gCEVAOKwSY79OvpbDaQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-3.0.1.tgz", + "integrity": "sha512-VEYtPvh5rs/xlyqpm5NRnfYLZn+q0SRPELbvBV+C/G7IQ+ouTuo+NKKa3ShG5OaFR8NYVMXls9hPYLTvIKKDrQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", + "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.5.tgz", + "integrity": "sha512-G8G/sDDhXA7o0bOvkc7bgai6POuSld/+XhNnWAbpQTpLv2OZPvyqQ58tLPPlz0bSNsXktldDDREIv1LczFeNEw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-stream": "^3.3.2", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", + "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.10.tgz", + "integrity": "sha512-323B8YckSbUH0nMIpXn7HZsAVKHYHFUODa8gG9cHo0ySvA1fr5iWaNT+iIL0UCqUzG6QPHA3BSsBtRQou4mMqQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^3.7.2", + "@smithy/util-hex-encoding": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.14.tgz", + "integrity": "sha512-kbrt0vjOIihW3V7Cqj1SXQvAI5BR8SnyQYsandva0AOR307cXAc+IhPngxIPslxTLfxwDpNu0HzCAq6g42kCPg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.13", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.11.tgz", + "integrity": "sha512-P2pnEp4n75O+QHjyO7cbw/vsw5l93K/8EWyjNCAAybYwUmj3M+hjSQZ9P5TVdUgEG08ueMAP5R4FkuSkElZ5tQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.13.tgz", + "integrity": "sha512-zqy/9iwbj8Wysmvi7Lq7XFLeDgjRpTbCfwBhJa8WbrylTAHiAu6oQTwdY7iu2lxigbc9YYr9vPv5SzYny5tCXQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.13", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.13.tgz", + "integrity": "sha512-L1Ib66+gg9uTnqp/18Gz4MDpJPKRE44geOjOQ2SVc0eiaO5l255ADziATZgjQjqumC7yPtp1XnjHlF1srcwjKw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^3.1.10", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.2.tgz", + "integrity": "sha512-R7rU7Ae3ItU4rC0c5mB2sP5mJNbCfoDc8I5XlYjIZnquyUwec7fEo78F6DA3SmgJgkU1qTMcZJuGblxZsl10ZA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.10.tgz", + "integrity": "sha512-elwslXOoNunmfS0fh55jHggyhccobFkexLYC1ZeZ1xP2BTSrcIBaHV2b4xUQOdctrSNOpMqOZH1r2XzWTEhyfA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^4.0.0", + "@smithy/chunked-blob-reader-native": "^3.0.1", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-node": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", + "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-3.1.10.tgz", + "integrity": "sha512-olomK/jZQ93OMayW1zfTHwcbwBdhcZOHsyWyiZ9h9IXvc1mCD/VuvzbLb3Gy/qNJwI4MANPLctTp2BucV2oU/Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", + "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.11.tgz", + "integrity": "sha512-3NM0L3i2Zm4bbgG6Ymi9NBcxXhryi3uE8fIfHJZIOfZVxOkGdjdgjR9A06SFIZCfnEIWKXZdm6Yq5/aPXFFhsQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", + "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.5.tgz", + "integrity": "sha512-VhJNs/s/lyx4weiZdXSloBgoLoS8osV0dKIain8nGmx7of3QFKu5BSdEuk1z/U8x9iwes1i+XCiNusEvuK1ijg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.5", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "3.0.30", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.30.tgz", + "integrity": "sha512-6323RL2BvAR3VQpTjHpa52kH/iSHyxd/G9ohb2MkBk2Ucu+oMtRXT8yi7KTSIS9nb58aupG6nO0OlXnQOAcvmQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", + "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", + "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", + "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.2.tgz", + "integrity": "sha512-t4ng1DAd527vlxvOfKFYEe6/QFBcsj7WpNlWTyjorwXXcKw3XlltBGbyHfSJ24QT84nF+agDha9tNYpzmSRZPA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", + "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", + "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", + "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", + "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", + "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", + "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.4.tgz", + "integrity": "sha512-5JWeMQYg81TgU4cG+OexAWdvDTs5JDdbEZx+Qr1iPbvo91QFGzjy0IkXAKaXUHqmKUJgSHK0ZxnCkgZpzkeNTA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.5.0.tgz", + "integrity": "sha512-Y8FeOa7gbDfCWf7njrkoRATPa5eNLUEjlJS5z5rXatYuGkCb80LbHcu8AQR8qgAZZaNHCLyo2N+pxPsV7l+ivg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.5", + "@smithy/middleware-endpoint": "^3.2.5", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-stream": "^3.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", + "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", + "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.30", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.30.tgz", + "integrity": "sha512-nLuGmgfcr0gzm64pqF2UT4SGWVG8UGviAdayDlVzJPNa6Z4lqvpDzdRXmLxtOdEjVlTOEdpZ9dd3ZMMu488mzg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">= 10.0.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.30", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.30.tgz", + "integrity": "sha512-OD63eWoH68vp75mYcfYyuVH+p7Li/mY4sYOROnauDrtObo1cS4uWfsy/zhOTW8F8ZPxQC1ZXZKVxoxvMGUv2Ow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^3.0.13", + "@smithy/credential-provider-imds": "^3.2.8", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 8" + "node": ">= 10.0.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, + "node_modules/@smithy/util-endpoints": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", + "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", + "license": "Apache-2.0", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">=16.0.0" } }, - "node_modules/@opentelemetry/api": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", - "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", + "node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8.0.0" + "node": ">=16.0.0" } }, - "node_modules/@opentelemetry/core": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.14.0.tgz", - "integrity": "sha512-MnMZ+sxsnlzloeuXL2nm5QcNczt/iO82UOeQQDHhV83F2fP3sgntW2evvtoxJki0MBLxEsh5ADD7PR/Hn5uzjw==", + "node_modules/@smithy/util-middleware": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", + "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", + "license": "Apache-2.0", "dependencies": { - "@opentelemetry/semantic-conventions": "1.14.0" + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.5.0" + "node": ">=16.0.0" } }, - "node_modules/@opentelemetry/instrumentation": { - "version": "0.41.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz", - "integrity": "sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw==", + "node_modules/@smithy/util-retry": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", + "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", + "license": "Apache-2.0", "dependencies": { - "@types/shimmer": "^1.0.2", - "import-in-the-middle": "1.4.2", - "require-in-the-middle": "^7.1.1", - "semver": "^7.5.1", - "shimmer": "^1.2.1" + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "node": ">=16.0.0" } }, - "node_modules/@opentelemetry/resources": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.14.0.tgz", - "integrity": "sha512-qRfWIgBxxl3z47E036Aey0Lj2ZjlFb27Q7Xnj1y1z/P293RXJZGLtcfn/w8JF7v1Q2hs3SDGxz7Wb9Dko1YUQA==", + "node_modules/@smithy/util-stream": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.2.tgz", + "integrity": "sha512-sInAqdiVeisUGYAv/FrXpmJ0b4WTFmciTRqzhb7wVuem9BHvhIG7tpiYHLDWrl2stOokNZpTTGqz3mzB2qFwXg==", + "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "1.14.0", - "@opentelemetry/semantic-conventions": "1.14.0" + "@smithy/fetch-http-handler": "^4.1.2", + "@smithy/node-http-handler": "^3.3.2", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.5.0" + "node": ">=16.0.0" } }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.14.0.tgz", - "integrity": "sha512-NzRGt3PS+HPKfQYMb6Iy8YYc5OKA73qDwci/6ujOIvyW9vcqBJSWbjZ8FeLEAmuatUB5WrRhEKu9b0sIiIYTrQ==", + "node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "1.14.0", - "@opentelemetry/resources": "1.14.0", - "@opentelemetry/semantic-conventions": "1.14.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.5.0" + "node": ">=16.0.0" } }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.14.0.tgz", - "integrity": "sha512-rJfCY8rCWz3cb4KI6pEofnytvMPuj3YLQwoscCCYZ5DkdiPjo15IQ0US7+mjcWy9H3fcZIzf2pbJZ7ck/h4tug==", + "node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=14" + "node": ">=16.0.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, + "node_modules/@smithy/util-waiter": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.2.0.tgz", + "integrity": "sha512-PpjSboaDUE6yl+1qlg3Si57++e84oXdWGbuFUSAciXsVfEZJJJupR2Nb0QuXHiunt2vGR+1PTizOMvnUPaG2Qg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=14" + "node": ">=16.0.0" } }, - "node_modules/@sinclair/typebox": { - "version": "0.25.24", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", - "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", - "dev": true - }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -1130,6 +2946,12 @@ "@types/node": "*" } }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "license": "MIT" + }, "node_modules/@types/yargs": { "version": "17.0.24", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", @@ -1699,6 +3521,84 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1692.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1692.0.tgz", + "integrity": "sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-sdk/node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/aws-sdk/node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "license": "MIT", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/aws-sdk/node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "license": "BSD-3-Clause" + }, + "node_modules/aws-sdk/node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "license": "ISC" + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1754,6 +3654,12 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2964,6 +4870,15 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -3229,6 +5144,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -3455,6 +5385,34 @@ "node": ">= 0.10" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-core-module": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", @@ -3498,6 +5456,21 @@ "node": ">=8" } }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -3537,6 +5510,21 @@ "node": ">=0.10.0" } }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -3670,6 +5658,15 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3997,6 +5994,15 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mnemonist": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", + "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", + "license": "MIT", + "dependencies": { + "obliterator": "^1.6.1" + } + }, "node_modules/module-details-from-path": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", @@ -4210,6 +6216,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obliterator": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", + "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==", + "license": "MIT" + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -4546,6 +6558,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.4.35", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", @@ -4680,6 +6701,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5588,6 +7618,35 @@ "punycode": "^2.1.0" } }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "license": "MIT", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "license": "MIT" + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -5651,6 +7710,25 @@ "node": ">= 8" } }, + "node_modules/which-typed-array": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.16.tgz", + "integrity": "sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -5744,6 +7822,28 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/api/package.json b/api/package.json index da3db1ada..4f8a252e0 100644 --- a/api/package.json +++ b/api/package.json @@ -22,12 +22,17 @@ }, "author": "Microsoft", "dependencies": { + "@aws-sdk/client-dynamodb": "^3.709.0", + "@aws-sdk/client-s3": "^3.709.0", + "@aws-sdk/client-secrets-manager": "^3.709.0", + "@aws-sdk/util-dynamodb": "^3.709.0", "@azure/data-tables": "^13.2.2", "@azure/identity": "^4.2.1", "@azure/keyvault-secrets": "^4.7.0", "@azure/storage-blob": "^12.14.0", "@azure/storage-queue": "^12.13.0", "applicationinsights": "^2.7.0", + "aws-sdk": "^2.1692.0", "body-parser": "^1.20.2", "cookie-session": "^2.0.0", "ejs": "^3.1.10", diff --git a/api/script/default-server.ts b/api/script/default-server.ts index 1c9234584..bce66ebeb 100644 --- a/api/script/default-server.ts +++ b/api/script/default-server.ts @@ -2,14 +2,13 @@ // Licensed under the MIT License. import * as api from "./api"; -import { AzureStorage } from "./storage/azure-storage"; +import { AWSStorage } from "./storage/aws-storage"; import { fileUploadMiddleware } from "./file-upload-manager"; import { JsonStorage } from "./storage/json-storage"; import { RedisManager } from "./redis-manager"; import { Storage } from "./storage/storage"; import { Response } from "express"; -const { DefaultAzureCredential } = require("@azure/identity"); -const { SecretClient } = require("@azure/keyvault-secrets"); +import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager"; import * as bodyParser from "body-parser"; const domain = require("express-domain-middleware"); @@ -36,26 +35,33 @@ function bodyParserErrorHandler(err: any, req: express.Request, res: express.Res export function start(done: (err?: any, server?: express.Express, storage?: Storage) => void, useJsonStorage?: boolean): void { let storage: Storage; - let isKeyVaultConfigured: boolean; - let keyvaultClient: any; + let isSecretsManagerConfigured: boolean; + let secretsClient: SecretsManagerClient; q(null) .then(async () => { if (useJsonStorage) { storage = new JsonStorage(); - } else if (!process.env.AZURE_KEYVAULT_ACCOUNT) { - storage = new AzureStorage(); + } else if (!process.env.AWS_SECRETS_MANAGER_REGION) { + storage = new AWSStorage(); } else { - isKeyVaultConfigured = true; + isSecretsManagerConfigured = true; - const credential = new DefaultAzureCredential(); + const region = process.env.AWS_SECRETS_MANAGER_REGION; + secretsClient = new SecretsManagerClient({ region }); - const vaultName = process.env.AZURE_KEYVAULT_ACCOUNT; - const url = `https://${vaultName}.vault.azure.net`; + const command = new GetSecretValueCommand({ + SecretId: `storage-${process.env.AWS_STORAGE_NAME}` + }); + + const response = await secretsClient.send(command); + const secret = JSON.parse(response.SecretString); - const keyvaultClient = new SecretClient(url, credential); - const secret = await keyvaultClient.getSecret(`storage-${process.env.AZURE_STORAGE_ACCOUNT}`); - storage = new AzureStorage(process.env.AZURE_STORAGE_ACCOUNT, secret); + storage = new AWSStorage( + process.env.AWS_REGION, + secret.accessKeyId, + secret.secretAccessKey + ); } }) .then(() => { @@ -67,7 +73,7 @@ export function start(done: (err?: any, server?: express.Express, storage?: Stor // First, to wrap all requests and catch all exceptions. app.use(domain); - // Monkey-patch res.send and res.setHeader to no-op after the first call and prevent "already sent" errors. + // Monkey-patch res.send and res.setHeader to no-op after the first call app.use((req: express.Request, res: express.Response, next: (err?: any) => void): any => { const originalSend = res.send; const originalSetHeader = res.setHeader; @@ -75,7 +81,6 @@ export function start(done: (err?: any, server?: express.Express, storage?: Stor if (!res.headersSent) { originalSetHeader.apply(res, [name, value]); } - return {} as Response; }; @@ -83,29 +88,24 @@ export function start(done: (err?: any, server?: express.Express, storage?: Stor if (res.headersSent) { return res; } - return originalSend.apply(res, [body]); }; - next(); }); if (process.env.LOGGING) { app.use((req: express.Request, res: express.Response, next: (err?: any) => void): any => { - console.log(); // Newline to mark new request + console.log(); console.log(`[REST] Received ${req.method} request at ${req.originalUrl}`); next(); }); } - // Enforce a timeout on all requests. + // Configure middleware and routes app.use(api.requestTimeoutHandler()); - - // Before other middleware which may use request data that this middleware modifies. app.use(api.inputSanitizer()); - - // body-parser must be before the Application Insights router. app.use(bodyParser.urlencoded({ extended: true })); + const jsonOptions: any = { limit: "10kb", strict: true }; if (process.env.LOG_INVALID_JSON_REQUESTS === "true") { jsonOptions.verify = (req: express.Request, res: express.Response, buf: Buffer, encoding: string) => { @@ -116,17 +116,15 @@ export function start(done: (err?: any, server?: express.Express, storage?: Stor } app.use(bodyParser.json(jsonOptions)); - - // If body-parser throws an error, catch it and set the request body to null. app.use(bodyParserErrorHandler); - - // Before all other middleware to ensure all requests are tracked. app.use(appInsights.router()); - app.get("/", (req: express.Request, res: express.Response, next: (err?: Error) => void): any => { + // Basic routes + app.get("/", (req: express.Request, res: express.Response) => { res.send("Welcome to the CodePush REST API!"); }); + // Configure app settings app.set("etag", false); app.set("views", __dirname + "/views"); app.set("view engine", "ejs"); @@ -141,17 +139,9 @@ export function start(done: (err?: any, server?: express.Express, storage?: Stor if (process.env.DISABLE_MANAGEMENT !== "true") { if (process.env.DEBUG_DISABLE_AUTH === "true") { app.use((req, res, next) => { - let userId: string = "default"; - if (process.env.DEBUG_USER_ID) { - userId = process.env.DEBUG_USER_ID; - } else { - console.log("No DEBUG_USER_ID environment variable configured. Using 'default' as user id"); - } - req.user = { - id: userId, + id: process.env.DEBUG_USER_ID || "default" }; - next(); }); } else { @@ -162,26 +152,33 @@ export function start(done: (err?: any, server?: express.Express, storage?: Stor app.use(auth.legacyRouter()); } - // Error handler needs to be the last middleware so that it can catch all unhandled exceptions app.use(appInsights.errorHandler); - if (isKeyVaultConfigured) { - // Refresh credentials from the vault regularly as the key is rotated - setInterval(() => { - keyvaultClient - .getSecret(`storage-${process.env.AZURE_STORAGE_ACCOUNT}`) - .then((secret: any) => { - return (storage).reinitialize(process.env.AZURE_STORAGE_ACCOUNT, secret); - }) - .catch((error: Error) => { - console.error("Failed to reinitialize storage from Key Vault credentials"); - appInsights.errorHandler(error); - }) - .done(); + if (isSecretsManagerConfigured) { + // Refresh credentials from AWS Secrets Manager + setInterval(async () => { + try { + const command = new GetSecretValueCommand({ + SecretId: `storage-${process.env.AWS_STORAGE_NAME}` + }); + const response = await secretsClient.send(command); + const secret = JSON.parse(response.SecretString); + + await (storage).reinitialize( + process.env.AWS_REGION, + secret.accessKeyId, + secret.secretAccessKey + ); + } catch (error) { + console.error("Failed to reinitialize storage from AWS Secrets Manager"); + appInsights.errorHandler(error); + } }, Number(process.env.REFRESH_CREDENTIALS_INTERVAL) || 24 * 60 * 60 * 1000 /*daily*/); } done(null, app, storage); }) - .done(); + .catch(error => { + done(error); + }); } diff --git a/api/script/storage/aws-storage.ts b/api/script/storage/aws-storage.ts new file mode 100644 index 000000000..5c269ea97 --- /dev/null +++ b/api/script/storage/aws-storage.ts @@ -0,0 +1,1172 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import * as q from "q"; +import * as shortid from "shortid"; +import * as stream from "stream"; +import * as storage from "./storage"; +import * as utils from "../utils/common"; +import { Readable } from 'stream'; + +import { + DynamoDBClient, + PutItemCommand, + GetItemCommand, + DeleteItemCommand, + UpdateItemCommand, + QueryCommand, + TransactWriteItemsCommand, + BatchWriteItemCommand, + AttributeValue +} from "@aws-sdk/client-dynamodb"; +import { + S3Client, + PutObjectCommand, + GetObjectCommand, + DeleteObjectCommand, + CreateBucketCommand, + HeadBucketCommand +} from "@aws-sdk/client-s3"; +import { marshall, unmarshall } from "@aws-sdk/util-dynamodb"; +import { isPrototypePollutionKey } from "./storage"; + +interface Pointer { + partitionKey: string; + sortKey: string; +} + +interface DeploymentKeyPointer { + appId: string; + deploymentId: string; + accountId: string; // Add this line +} + +interface AccessKeyPointer { + accountId: string; + expires: number; +} + +module Keys { + const DELIMITER = "#"; // Changed from space for DynamoDB compatibility + const LEAF_MARKER = "*"; + + export function getAccountPartitionKey(accountId: string): string { + validateParameters(Array.prototype.slice.apply(arguments)); + return "accountId" + DELIMITER + accountId; + } + + export function getAccountAddress(accountId: string): Pointer { + validateParameters(Array.prototype.slice.apply(arguments)); + return { + partitionKey: getAccountPartitionKey(accountId), + sortKey: getHierarchicalAccountRowKey(accountId), + }; + } + + export function getAppPartitionKey(appId: string): string { + validateParameters(Array.prototype.slice.apply(arguments)); + return "appId" + DELIMITER + appId; + } + + export function getHierarchicalAppRowKey(appId?: string, deploymentId?: string): string { + validateParameters(Array.prototype.slice.apply(arguments)); + return generateHierarchicalAppKey(/*markLeaf=*/ true, appId, deploymentId); + } + + export function getHierarchicalAccountRowKey(accountId: string, appId?: string): string { + validateParameters(Array.prototype.slice.apply(arguments)); + return generateHierarchicalAccountKey(/*markLeaf=*/ true, accountId, appId); + } + + export function generateHierarchicalAppKey(markLeaf: boolean, appId: string, deploymentId?: string): string { + validateParameters(Array.prototype.slice.apply(arguments).slice(1)); + let key = delimit("appId", appId, /*prependDelimiter=*/ false); + + if (typeof deploymentId !== "undefined") { + key += delimit("deploymentId", deploymentId); + } + + if (markLeaf) { + const lastIdDelimiter: number = key.lastIndexOf(DELIMITER); + key = key.substring(0, lastIdDelimiter) + LEAF_MARKER + key.substring(lastIdDelimiter); + } + + return key; + } + + export function generateHierarchicalAccountKey(markLeaf: boolean, accountId: string, appId?: string): string { + validateParameters(Array.prototype.slice.apply(arguments).slice(1)); + let key = delimit("accountId", accountId, /*prependDelimiter=*/ false); + + if (typeof appId !== "undefined") { + key += delimit("appId", appId); + } + + if (markLeaf) { + const lastIdDelimiter: number = key.lastIndexOf(DELIMITER); + key = key.substring(0, lastIdDelimiter) + LEAF_MARKER + key.substring(lastIdDelimiter); + } + + return key; + } + + export function getAccessKeyRowKey(accountId: string, accessKeyId?: string): string { + validateParameters(Array.prototype.slice.apply(arguments)); + let key: string = "accountId_" + accountId + "_accessKeyId*_"; + + if (accessKeyId !== undefined) { + key += accessKeyId; + } + + return key; + } + + export function isDeployment(sortKey: string): boolean { + return sortKey.indexOf("deploymentId*") !== -1; + } + + export function getEmailShortcutAddress(email: string): Pointer { + validateParameters(Array.prototype.slice.apply(arguments)); + return { + partitionKey: "email" + DELIMITER + email.toLowerCase(), + sortKey: "", + }; + } + + export function getShortcutDeploymentKeyPartitionKey(deploymentKey: string): string { + validateParameters(Array.prototype.slice.apply(arguments)); + return delimit("deploymentKey", deploymentKey, /*prependDelimiter=*/ false); + } + + export function getShortcutDeploymentKeyRowKey(): string { + return ""; + } + + export function getShortcutAccessKeyPartitionKey(accessKeyName: string, hash: boolean = true): string { + validateParameters(Array.prototype.slice.apply(arguments)); + return delimit("accessKey", hash ? utils.hashWithSHA256(accessKeyName) : accessKeyName, /*prependDelimiter=*/ false); + } + + function validateParameters(parameters: string[]): void { + parameters.forEach((parameter: string): void => { + if (parameter && (parameter.indexOf(DELIMITER) >= 0 || parameter.indexOf(LEAF_MARKER) >= 0)) { + throw storage.storageError(storage.ErrorCode.Invalid, `The parameter '${parameter}' contained invalid characters.`); + } + }); + } + + function delimit(fieldName: string, value: string, prependDelimiter = true): string { + const prefix = prependDelimiter ? DELIMITER : ""; + return prefix + fieldName + DELIMITER + value; + } +} + +export class AWSStorage implements storage.Storage { + public static NO_ID_ERROR = "No id set"; + + private static HISTORY_BUCKET_NAME = "package-history-v1"; + private static MAX_PACKAGE_HISTORY_LENGTH = 50; + private static TABLE_NAME = "storage-v2"; + + private _dynamoClient: DynamoDBClient; + private _s3Client: S3Client; + private _setupPromise: q.Promise; + + public constructor(region?: string, accessKeyId?: string, secretAccessKey?: string) { + shortid.characters("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-"); + this._setupPromise = this.setup(region, accessKeyId, secretAccessKey); + } + + public reinitialize(region?: string, accessKeyId?: string, secretAccessKey?: string): q.Promise { + console.log("Re-initializing AWS storage"); + return this.setup(region, accessKeyId, secretAccessKey); + } + + private setup(region?: string, accessKeyId?: string, secretAccessKey?: string): q.Promise { + const config = { + region: region || process.env.AWS_REGION || 'us-east-1', + credentials: { + accessKeyId: accessKeyId || process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: secretAccessKey || process.env.AWS_SECRET_ACCESS_KEY + } + }; + + if (!config.credentials.accessKeyId || !config.credentials.secretAccessKey) { + throw new Error("AWS credentials not set"); + } + + this._dynamoClient = new DynamoDBClient(config); + this._s3Client = new S3Client(config); + + return q(this.createResources()); + } + + private async createResources(): Promise { + try { + // Create S3 buckets if they don't exist + await this._s3Client.send(new CreateBucketCommand({ + Bucket: AWSStorage.TABLE_NAME + })); + + await this._s3Client.send(new CreateBucketCommand({ + Bucket: AWSStorage.HISTORY_BUCKET_NAME + })); + + // Create health check items + await this._s3Client.send(new PutObjectCommand({ + Bucket: AWSStorage.TABLE_NAME, + Key: 'health', + Body: 'health' + })); + + await this._s3Client.send(new PutObjectCommand({ + Bucket: AWSStorage.HISTORY_BUCKET_NAME, + Key: 'health', + Body: 'health' + })); + + // Create DynamoDB health check item + await this._dynamoClient.send(new PutItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Item: marshall({ + partitionKey: 'health', + sortKey: 'health', + health: 'health' + }) + })); + + return q(); + } catch (error) { + if (error.name === 'ResourceInUseException' || error.name === 'BucketAlreadyOwnedByYou') { + return q(); + } + throw error; + } + } + + public checkHealth(): q.Promise { + return this._setupPromise + .then(() => { + const dynamoCheck = this.dynamoHealthCheck(); + const mainBucketCheck = this.s3HealthCheck(AWSStorage.TABLE_NAME); + const historyBucketCheck = this.s3HealthCheck(AWSStorage.HISTORY_BUCKET_NAME); + return q.all([dynamoCheck, mainBucketCheck, historyBucketCheck]); + }) + .then(() => { + return; + }); + } + + private dynamoHealthCheck(): q.Promise { + return q.Promise((resolve, reject) => { + this._dynamoClient.send(new GetItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Key: marshall({ + partitionKey: 'health', + sortKey: 'health' + }) + })) + .then(response => { + if (!response.Item || unmarshall(response.Item).health !== 'health') { + reject(storage.storageError( + storage.ErrorCode.ConnectionFailed, + "The DynamoDB service failed the health check" + )); + } else { + resolve(); + } + }) + .catch(reject); + }); + } + + private s3HealthCheck(bucket: string): q.Promise { + return q.Promise((resolve, reject) => { + this._s3Client.send(new GetObjectCommand({ + Bucket: bucket, + Key: 'health' + })) + .then(async response => { + const bodyContents = await response.Body.transformToString(); + if (bodyContents !== 'health') { + reject(storage.storageError( + storage.ErrorCode.ConnectionFailed, + `The S3 service failed the health check for ${bucket}` + )); + } else { + resolve(); + } + }) + .catch(reject); + }); + } + + private static awsErrorHandler(error: any, overrideMessage: boolean = false, overrideCondition?: string, overrideValue?: string): any { + let errorCode: storage.ErrorCode; + let errorMessage = error.message; + + if (overrideMessage && error.name === overrideCondition) { + errorMessage = overrideValue; + } + + switch (error.name) { + case 'ResourceNotFoundException': + case 'NoSuchKey': + errorCode = storage.ErrorCode.NotFound; + break; + case 'ResourceInUseException': + case 'ConditionalCheckFailedException': + errorCode = storage.ErrorCode.AlreadyExists; + break; + case 'EntityTooLarge': + case 'ItemSizeLimitExceededException': + errorCode = storage.ErrorCode.TooLarge; + break; + case 'RequestTimeout': + case 'TimeoutError': + errorCode = storage.ErrorCode.ConnectionFailed; + break; + default: + errorCode = storage.ErrorCode.Other; + break; + } + + throw storage.storageError(errorCode, errorMessage); + } + + public clearPackageHistory(accountId: string, appId: string, deploymentId: string): q.Promise { + return this._setupPromise + .then(async () => { + const partitionKey = Keys.getAppPartitionKey(appId); + const sortKey = Keys.getHierarchicalAppRowKey(appId, deploymentId); + + await this._dynamoClient.send(new UpdateItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Key: marshall({ + partitionKey, + sortKey + }), + UpdateExpression: 'REMOVE #package', + ExpressionAttributeNames: { + '#package': 'package' + } + })); + }); + } + + public addApp(accountId: string, app: storage.App): q.Promise { + app = storage.clone(app); + app.id = shortid.generate(); + + return this._setupPromise + .then(() => { + return this.getAccount(accountId); + }) + .then((account: storage.Account) => { + const collabMap: storage.CollaboratorMap = {}; + collabMap[account.email] = { accountId: accountId, permission: storage.Permissions.Owner }; + app.collaborators = collabMap; + + const flatApp = AWSStorage.flattenApp(app, /*updateCollaborator*/ true); + return this.insertByAppHierarchy(flatApp, app.id); + }) + .then(() => { + return this.addAppPointer(accountId, app.id); + }) + .then(() => app) + .catch(AWSStorage.awsErrorHandler); + } + + public getApps(accountId: string): q.Promise { + return this._setupPromise + .then(() => { + return this.getCollectionByHierarchy(accountId); + }) + .then((flatApps: any[]) => { + return flatApps.map(flatApp => AWSStorage.unflattenApp(flatApp, accountId)); + }) + .catch(AWSStorage.awsErrorHandler); + } + + public getApp(accountId: string, appId: string, keepCollaboratorIds: boolean = false): q.Promise { + return this._setupPromise + .then(() => { + return this.retrieveByAppHierarchy(appId); + }) + .then((flatApp: any) => { + return AWSStorage.unflattenApp(flatApp, accountId); + }) + .catch(AWSStorage.awsErrorHandler); + } + + public updateApp(accountId: string, app: storage.App): q.Promise { + if (!app.id) throw new Error(AWSStorage.NO_ID_ERROR); + + return this._setupPromise + .then(() => { + return this.updateAppWithPermission(accountId, app, /*updateCollaborator*/ false); + }) + .catch(AWSStorage.awsErrorHandler); + } + + public addDeployment(accountId: string, appId: string, deployment: storage.Deployment): q.Promise { + let deploymentId: string; + return this._setupPromise + .then(() => { + const flatDeployment = AWSStorage.flattenDeployment(deployment); + flatDeployment.id = shortid.generate(); + deploymentId = flatDeployment.id; + + return this.insertByAppHierarchy(flatDeployment, appId, deploymentId); + }) + .then(() => { + return this.uploadToHistoryBlob(deploymentId, JSON.stringify([])); + }) + .then(() => { + const params = { + TableName: AWSStorage.TABLE_NAME, + Item: marshall({ + partitionKey: Keys.getShortcutDeploymentKeyPartitionKey(deployment.key), + sortKey: Keys.getShortcutDeploymentKeyRowKey(), + appId: appId, + deploymentId: deploymentId + }) + }; + + return this._dynamoClient.send(new PutItemCommand(params)); + }) + .then(() => deploymentId) + .catch(AWSStorage.awsErrorHandler); + } + + public getDeployments(accountId: string, appId: string): q.Promise { + return this._setupPromise + .then(() => { + return this.getCollectionByHierarchy(accountId, appId); + }) + .then((flatDeployments: any[]) => { + return flatDeployments.map(flatDeployment => AWSStorage.unflattenDeployment(flatDeployment)); + }) + .catch(AWSStorage.awsErrorHandler); + } + + public commitPackage(accountId: string, appId: string, deploymentId: string, appPackage: storage.Package): q.Promise { + if (!deploymentId) throw new Error("No deployment id"); + if (!appPackage) throw new Error("No package specified"); + + appPackage = storage.clone(appPackage); + let packageHistory: storage.Package[]; + + return this._setupPromise + .then(() => { + return this.getPackageHistoryFromBlob(deploymentId); + }) + .then((history: storage.Package[]) => { + packageHistory = history; + appPackage.label = this.getNextLabel(packageHistory); + return this.getAccount(accountId); + }) + .then((account: storage.Account) => { + appPackage.releasedBy = account.email; + + const lastPackage = packageHistory && packageHistory.length ? + packageHistory[packageHistory.length - 1] : null; + if (lastPackage) { + lastPackage.rollout = null; + } + + packageHistory.push(appPackage); + + if (packageHistory.length > AWSStorage.MAX_PACKAGE_HISTORY_LENGTH) { + packageHistory.splice(0, packageHistory.length - AWSStorage.MAX_PACKAGE_HISTORY_LENGTH); + } + + const params = { + TableName: AWSStorage.TABLE_NAME, + Key: marshall({ + partitionKey: Keys.getAppPartitionKey(appId), + sortKey: Keys.getHierarchicalAppRowKey(appId, deploymentId) + }), + UpdateExpression: 'SET #package = :package', + ExpressionAttributeNames: { + '#package': 'package' + }, + ExpressionAttributeValues: marshall({ + ':package': JSON.stringify(appPackage) + }) + }; + + return this._dynamoClient.send(new UpdateItemCommand(params)); + }) + .then(() => { + return this.uploadToHistoryBlob(deploymentId, JSON.stringify(packageHistory)); + }) + .then(() => appPackage) + .catch(AWSStorage.awsErrorHandler); + } + + private async uploadToHistoryBlob(blobId: string, content: string): Promise { + try { + await this._s3Client.send(new PutObjectCommand({ + Bucket: AWSStorage.HISTORY_BUCKET_NAME, + Key: blobId, + Body: content + })); + } catch (error) { + throw AWSStorage.awsErrorHandler(error); + } + } + + private async getPackageHistoryFromBlob(blobId: string): Promise { + try { + const response = await this._s3Client.send(new GetObjectCommand({ + Bucket: AWSStorage.HISTORY_BUCKET_NAME, + Key: blobId + })); + + const bodyContents = await response.Body.transformToString(); + return JSON.parse(bodyContents); + } catch (error) { + if (error.name === 'NoSuchKey') { + return []; + } + throw AWSStorage.awsErrorHandler(error); + } + } + + private async insertByAppHierarchy(jsObject: Object, appId: string, deploymentId?: string): Promise { + const leafId = arguments[arguments.length - 1]; + const partitionKey = Keys.getAppPartitionKey(appId); + const sortKey = Keys.getHierarchicalAppRowKey(appId, deploymentId); + + try { + await this._dynamoClient.send(new PutItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Item: marshall(this.wrap(jsObject, partitionKey, sortKey)) + })); + + return leafId; + } catch (error) { + throw AWSStorage.awsErrorHandler(error); + } + } + + private static flattenApp(app: storage.App, updateCollaborator: boolean = false): any { + if (!app) return app; + + const flatApp: any = { ...app }; + if (updateCollaborator) { + AWSStorage.deleteIsCurrentAccountProperty(app.collaborators); + flatApp.collaborators = JSON.stringify(app.collaborators); + } + + return flatApp; + } + + private static unflattenApp(flatApp: any, currentAccountId: string): storage.App { + if (!flatApp) return flatApp; + + const app = { ...flatApp }; + app.collaborators = app.collaborators ? JSON.parse(app.collaborators) : {}; + + const currentUserEmail = AWSStorage.getEmailForAccountId(app.collaborators, currentAccountId); + if (currentUserEmail && app.collaborators[currentUserEmail]) { + app.collaborators[currentUserEmail].isCurrentAccount = true; + } + + return app; + } + + private static flattenDeployment(deployment: storage.Deployment): any { + if (!deployment) return deployment; + + const flatDeployment: any = { ...deployment }; + delete flatDeployment.package; + return flatDeployment; + } + + private static unflattenDeployment(flatDeployment: any): storage.Deployment { + if (!flatDeployment) return flatDeployment; + + const deployment = { ...flatDeployment }; + delete deployment.packageHistory; + deployment.package = deployment.package ? JSON.parse(deployment.package) : null; + + return deployment; + } + + private getNextLabel(packageHistory: storage.Package[]): string { + if (packageHistory.length === 0) { + return "v1"; + } + + const lastLabel = packageHistory[packageHistory.length - 1].label; + const lastVersion = parseInt(lastLabel.substring(1)); + return "v" + (lastVersion + 1); + } + + public getAccessKeyFromId(accountId: string, accessKeyId: string): q.Promise { + const partitionKey = Keys.getAccountPartitionKey(accountId); + const sortKey = Keys.getAccessKeyRowKey(accountId, accessKeyId); + + return this._setupPromise + .then(async () => { + const response = await this._dynamoClient.send(new GetItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Key: marshall({ partitionKey, sortKey }) + })); + + if (!response.Item) { + throw storage.storageError(storage.ErrorCode.NotFound); + } + + return this.unwrap(response.Item) as storage.AccessKey; + }) + .catch(AWSStorage.awsErrorHandler); + } + + public getAccountIdFromAccessKey(accessKey: string): q.Promise { + const partitionKey = Keys.getShortcutAccessKeyPartitionKey(accessKey); + const sortKey = ""; + + return this._setupPromise + .then(async () => { + const response = await this._dynamoClient.send(new GetItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Key: marshall({ partitionKey, sortKey }) + })); + + if (!response.Item) { + throw storage.storageError(storage.ErrorCode.NotFound); + } + + const accountIdObject = unmarshall(response.Item) as AccessKeyPointer; + if (new Date().getTime() >= accountIdObject.expires) { + throw storage.storageError(storage.ErrorCode.Expired, "The access key has expired."); + } + + return accountIdObject.accountId; + }) + .catch(AWSStorage.awsErrorHandler); + } + + public addCollaborator(accountId: string, appId: string, email: string): q.Promise { + return this._setupPromise + .then(() => { + const getAppPromise = this.getApp(accountId, appId, /*keepCollaboratorIds*/ true); + const accountPromise = this.getAccountByEmail(email); + return q.all([getAppPromise, accountPromise]); + }) + .spread((app: storage.App, account: storage.Account) => { + email = account.email; // Use original email for consistent casing + return this.addCollaboratorWithPermissions(accountId, app, email, { + accountId: account.id, + permission: storage.Permissions.Collaborator + }); + }) + .catch(AWSStorage.awsErrorHandler); + } + + public getCollaborators(accountId: string, appId: string): q.Promise { + return this._setupPromise + .then(() => { + return this.getApp(accountId, appId, /*keepCollaboratorIds*/ false); + }) + .then((app: storage.App) => { + return q(app.collaborators); + }) + .catch(AWSStorage.awsErrorHandler); + } + + public removeCollaborator(accountId: string, appId: string, email: string): q.Promise { + return this._setupPromise + .then(() => { + return this.getApp(accountId, appId, /*keepCollaboratorIds*/ true); + }) + .then((app: storage.App) => { + const removedCollabProperties = app.collaborators[email]; + + if (!removedCollabProperties) { + throw storage.storageError(storage.ErrorCode.NotFound, "The given email is not a collaborator for this app."); + } + + if (!AWSStorage.isOwner(app.collaborators, email)) { + delete app.collaborators[email]; + } else { + throw storage.storageError(storage.ErrorCode.AlreadyExists, "Cannot remove the owner of the app from collaborator list."); + } + + return this.updateAppWithPermission(accountId, app, /*updateCollaborator*/ true) + .then(() => { + return this.removeAppPointer(removedCollabProperties.accountId, app.id); + }); + }) + .catch(AWSStorage.awsErrorHandler); + } + + private async getCollectionByHierarchy(accountId: string, appId?: string): Promise { + let partitionKey: string; + let sortKeyPrefix: string; + + if (appId) { + partitionKey = Keys.getAppPartitionKey(appId); + sortKeyPrefix = Keys.generateHierarchicalAppKey(false, appId); + } else { + partitionKey = Keys.getAccountPartitionKey(accountId); + sortKeyPrefix = Keys.generateHierarchicalAccountKey(false, accountId); + } + + try { + const response = await this._dynamoClient.send(new QueryCommand({ + TableName: AWSStorage.TABLE_NAME, + KeyConditionExpression: 'partitionKey = :pk AND begins_with(sortKey, :sk)', + ExpressionAttributeValues: marshall({ + ':pk': partitionKey, + ':sk': sortKeyPrefix + }) + })); + + if (!response.Items || response.Items.length === 0) { + throw storage.storageError(storage.ErrorCode.NotFound); + } + + return response.Items.map(item => this.unwrap(item)); + } catch (error) { + throw AWSStorage.awsErrorHandler(error); + } + } + + private async retrieveByAppHierarchy(appId: string, deploymentId?: string): Promise { + const partitionKey = Keys.getAppPartitionKey(appId); + const sortKey = Keys.getHierarchicalAppRowKey(appId, deploymentId); + + try { + const response = await this._dynamoClient.send(new GetItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Key: marshall({ partitionKey, sortKey }) + })); + + if (!response.Item) { + throw storage.storageError(storage.ErrorCode.NotFound); + } + + return this.unwrap(response.Item); + } catch (error) { + throw AWSStorage.awsErrorHandler(error); + } + } + + private static isOwner(collaboratorsMap: storage.CollaboratorMap, email: string): boolean { + return collaboratorsMap?.[email]?.permission === storage.Permissions.Owner; + } + + private static isCollaborator(collaboratorsMap: storage.CollaboratorMap, email: string): boolean { + return collaboratorsMap?.[email]?.permission === storage.Permissions.Collaborator; + } + + private static deleteIsCurrentAccountProperty(map: storage.CollaboratorMap): void { + if (map) { + Object.keys(map).forEach((key: string) => { + delete map[key].isCurrentAccount; + }); + } + } + + private static getEmailForAccountId(collaboratorsMap: storage.CollaboratorMap, accountId: string): string { + if (collaboratorsMap) { + for (const [email, props] of Object.entries(collaboratorsMap)) { + if (props.accountId === accountId) { + return email; + } + } + } + return null; + } + + public getAccountByEmail(email: string): q.Promise { + const address = Keys.getEmailShortcutAddress(email); + + return this._setupPromise + .then(async () => { + const response = await this._dynamoClient.send(new GetItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Key: marshall({ + partitionKey: address.partitionKey, + sortKey: address.sortKey + }) + })); + + if (!response.Item) { + throw storage.storageError( + storage.ErrorCode.NotFound, + "The specified e-mail address doesn't represent a registered user" + ); + } + + return this.unwrap(response.Item) as storage.Account; + }) + .catch(AWSStorage.awsErrorHandler); + } + + private addCollaboratorWithPermissions(accountId: string, app: storage.App, email: string, collabProps: storage.CollaboratorProperties): q.Promise { + AWSStorage.addToCollaborators(app.collaborators, email, collabProps); + return this.updateAppWithPermission(accountId, app, /*updateCollaborator*/ true) + .then(() => { + return this.addAppPointer(collabProps.accountId, app.id); + }); + } + + private async addAppPointer(accountId: string, appId: string): Promise { + const partitionKey = Keys.getAccountPartitionKey(accountId); + const sortKey = Keys.getHierarchicalAccountRowKey(accountId, appId); + + await this._dynamoClient.send(new PutItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Item: marshall({ + partitionKey, + sortKey, + appId + }) + })); + } + + private updateAppWithPermission(accountId: string, app: storage.App, updateCollaborator: boolean): q.Promise { + const flatApp = AWSStorage.flattenApp(app, updateCollaborator); + const partitionKey = Keys.getAppPartitionKey(app.id); + const sortKey = Keys.getHierarchicalAppRowKey(app.id); + + return this._setupPromise + .then(async () => { + await this._dynamoClient.send(new PutItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Item: marshall(this.wrap(flatApp, partitionKey, sortKey)) + })); + }); + } + + private static addToCollaborators(collaboratorsMap: storage.CollaboratorMap, email: string, collabProps: storage.CollaboratorProperties): void { + if (collaboratorsMap && email && !isPrototypePollutionKey(email) && !collaboratorsMap[email]) { + collaboratorsMap[email] = collabProps; + } + } + + private unwrap(item: { [key: string]: AttributeValue }): any { + const unwrapped = unmarshall(item); + const { partitionKey, sortKey, ...rest } = unwrapped; + return rest; + } + + private wrap(jsObject: any, partitionKey: string, sortKey: string): any { + return { + partitionKey, + sortKey, + ...jsObject + }; + } + + public getAccount(accountId: string): q.Promise { + const address = Keys.getAccountAddress(accountId); + + return this._setupPromise + .then(async () => { + const response = await this._dynamoClient.send(new GetItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Key: marshall({ + partitionKey: address.partitionKey, + sortKey: address.sortKey + }) + })); + + if (!response.Item) { + throw storage.storageError(storage.ErrorCode.NotFound); + } + + return this.unwrap(response.Item) as storage.Account; + }) + .catch(AWSStorage.awsErrorHandler); + } + + private async removeAppPointer(accountId: string, appId: string): Promise { + const partitionKey = Keys.getAccountPartitionKey(accountId); + const sortKey = Keys.getHierarchicalAccountRowKey(accountId, appId); + + await this._dynamoClient.send(new DeleteItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Key: marshall({ + partitionKey, + sortKey + }) + })); + } + + public addAccount(account: storage.Account): q.Promise { + account = storage.clone(account); + account.id = shortid.generate(); + + const emailAddress = Keys.getEmailShortcutAddress(account.email); + const accountAddress = Keys.getAccountAddress(account.id); + + return this._setupPromise + .then(async () => { + await this._dynamoClient.send(new PutItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Item: marshall(this.wrap(account, emailAddress.partitionKey, emailAddress.sortKey)), + ConditionExpression: 'attribute_not_exists(partitionKey)' + })); + + await this._dynamoClient.send(new PutItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Item: marshall(this.wrap(account, accountAddress.partitionKey, accountAddress.sortKey)) + })); + + return account.id; + }) + .catch(AWSStorage.awsErrorHandler); + } + + public updateAccount(accountId: string, updateProperties: storage.UpdateAccountProperties): q.Promise { + return this._setupPromise + .then(async () => { + const account = await this.getAccount(accountId); + const emailAddress = Keys.getEmailShortcutAddress(account.email); + + await this._dynamoClient.send(new UpdateItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Key: marshall({ + partitionKey: emailAddress.partitionKey, + sortKey: emailAddress.sortKey + }), + UpdateExpression: 'SET azureAdId = :azureAdId, gitHubId = :gitHubId, microsoftId = :microsoftId', + ExpressionAttributeValues: marshall({ + ':azureAdId': updateProperties.azureAdId, + ':gitHubId': updateProperties.gitHubId, + ':microsoftId': updateProperties.microsoftId + }) + })); + }) + .catch(AWSStorage.awsErrorHandler); + } + + public removeApp(accountId: string, appId: string): q.Promise { + return this._setupPromise + .then(() => this.getApp(accountId, appId)) + .then(async (app) => { + const partitionKey = Keys.getAppPartitionKey(appId); + const sortKey = Keys.getHierarchicalAppRowKey(appId); + + await this._dynamoClient.send(new DeleteItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Key: marshall({ partitionKey, sortKey }) + })); + + // Remove app pointers for all collaborators + const promises = Object.values(app.collaborators).map(collab => + this.removeAppPointer(collab.accountId, appId) + ); + + return q.all(promises); + }) + .catch(AWSStorage.awsErrorHandler); + } + + public transferApp(fromAccountId: string, toAccountId: string, appId: string): q.Promise { + return this._setupPromise + .then(() => { + const getFromAccountPromise = this.getAccount(fromAccountId); + const getToAccountPromise = this.getAccount(toAccountId); + const getAppPromise = this.getApp(fromAccountId, appId, true); + return q.all([getFromAccountPromise, getToAccountPromise, getAppPromise]); + }) + .spread((fromAccount: storage.Account, toAccount: storage.Account, app: storage.App) => { + const fromEmail = fromAccount.email; + const toEmail = toAccount.email; + + if (!app.collaborators[fromEmail] || !AWSStorage.isOwner(app.collaborators, fromEmail)) { + throw storage.storageError(storage.ErrorCode.NotFound); + } + + app.collaborators[toEmail] = { + accountId: toAccountId, + permission: storage.Permissions.Owner + }; + + app.collaborators[fromEmail] = { + accountId: fromAccountId, + permission: storage.Permissions.Collaborator + }; + + return this.updateAppWithPermission(fromAccountId, app, true); + }) + .catch(AWSStorage.awsErrorHandler); + } + + public getDeploymentIdFromKey(deploymentKey: string): q.Promise { + const partitionKey = Keys.getShortcutDeploymentKeyPartitionKey(deploymentKey); + const sortKey = Keys.getShortcutDeploymentKeyRowKey(); + + return this._setupPromise + .then(async () => { + const response = await this._dynamoClient.send(new GetItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Key: marshall({ partitionKey, sortKey }) + })); + + if (!response.Item) { + throw storage.storageError(storage.ErrorCode.NotFound); + } + + return this.unwrap(response.Item) as DeploymentKeyPointer; + }) + .catch(AWSStorage.awsErrorHandler); + } + + public getDeployment(accountId: string, appId: string, deploymentId: string): q.Promise { + return this._setupPromise + .then(() => { + return this.retrieveByAppHierarchy(appId, deploymentId); + }) + .then((flatDeployment: any) => { + return AWSStorage.unflattenDeployment(flatDeployment); + }) + .catch(AWSStorage.awsErrorHandler); + } + + + public getDeploymentInfo(deploymentKey: string): q.Promise { + return this.getDeploymentIdFromKey(deploymentKey) + .then((pointer) => { + return this.getDeployment(pointer.accountId, pointer.appId, pointer.deploymentId) + .then((deployment) => ({ + id: deployment.id, + key: deployment.key, + name: deployment.name, + package: deployment.package, + appId: pointer.appId, + deploymentId: pointer.deploymentId + })); + }); + } + + public removeDeployment(accountId: string, appId: string, deploymentId: string): q.Promise { + return this._setupPromise + .then(async () => { + const partitionKey = Keys.getAppPartitionKey(appId); + const sortKey = Keys.getHierarchicalAppRowKey(appId, deploymentId); + + await this._dynamoClient.send(new DeleteItemCommand({ + TableName: AWSStorage.TABLE_NAME, + Key: marshall({ partitionKey, sortKey }) + })); + }) + .catch(AWSStorage.awsErrorHandler); + } + + public updateDeployment(accountId: string, appId: string, deployment: storage.Deployment): q.Promise { + if (!deployment.id) throw new Error(AWSStorage.NO_ID_ERROR); + + const flatDeployment = AWSStorage.flattenDeployment(deployment); + return q(this.insertByAppHierarchy(flatDeployment, appId, deployment.id)) + .then(() => { + return; + }); + } + + public getPackageHistoryFromDeploymentKey(deploymentKey: string): q.Promise { + return this.getDeploymentIdFromKey(deploymentKey) + .then((pointer) => { + return this.getPackageHistoryFromBlob(pointer.deploymentId); + }); + } + + public getPackageHistory(accountId: string, appId: string, deploymentId: string): q.Promise { + return q(this.getPackageHistoryFromBlob(deploymentId)); + } + + public updatePackageHistory(accountId: string, appId: string, deploymentId: string, packages: storage.Package[]): q.Promise { + return q(this.uploadToHistoryBlob(deploymentId, JSON.stringify(packages))); + } + + public addBlob(blobId: string, addstream: Readable, streamLength: number): q.Promise { + return q.Promise((resolve, reject) => { + const chunks: Buffer[] = []; + addstream.on('data', chunk => chunks.push(chunk)); + addstream.on('end', () => { + const content = Buffer.concat(chunks); + const hash = utils.hashWithSHA256(content.toString()); + this.uploadPackage(hash, content) + .then(() => resolve(hash)) + .catch(reject); + }); + addstream.on('error', reject); + }); + } + + private uploadPackage(packageHash: string, content: Buffer): q.Promise { + return this._setupPromise + .then(async () => { + await this._s3Client.send(new PutObjectCommand({ + Bucket: AWSStorage.TABLE_NAME, + Key: `packages/${packageHash}`, + Body: content + })); + }) + .catch(AWSStorage.awsErrorHandler); + } + + public getBlob(blobId: string): q.Promise { + return this.downloadPackage(blobId) + .then((stream) => { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + stream.on('data', (chunk) => chunks.push(chunk)); + stream.on('end', () => resolve(Buffer.concat(chunks))); + stream.on('error', reject); + }); + }); + } + + private downloadPackage(packageHash: string): q.Promise { + return this._setupPromise + .then(async () => { + const response = await this._s3Client.send(new GetObjectCommand({ + Bucket: AWSStorage.TABLE_NAME, + Key: `packages/${packageHash}` + })); + + return response.Body as stream.Readable; + }) + .catch(AWSStorage.awsErrorHandler); + } + + public getBlobUrl(blobId: string): q.Promise { + throw new Error("Not implemented"); + } + + public removeBlob(blobId: string): q.Promise { + throw new Error("Not implemented"); + } + + public addAccessKey(accountId: string, accessKey: storage.AccessKey): q.Promise { + throw new Error("Not implemented"); + } + + public getAccessKey(accountId: string, accessKeyId: string): q.Promise { + throw new Error("Not implemented"); + } + + public removeAccessKey(accountId: string, accessKeyId: string): q.Promise { + throw new Error("Not implemented"); + } + + public getAccessKeys(accountId: string): q.Promise { + throw new Error("Not implemented"); + } + + public updateAccessKey(accountId: string, accessKey: storage.AccessKey): q.Promise { + throw new Error("Not implemented"); + } + + public dropAll(): q.Promise { + throw new Error("Not implemented"); + } +} \ No newline at end of file diff --git a/api/script/storage/azure-storage-backup.ts b/api/script/storage/azure-storage-backup.ts new file mode 100644 index 000000000..5d67653e9 --- /dev/null +++ b/api/script/storage/azure-storage-backup.ts @@ -0,0 +1,1477 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import * as q from "q"; +import * as shortid from "shortid"; +import * as stream from "stream"; +import * as storage from "./storage"; +import * as utils from "../utils/common"; + +import { BlobServiceClient, StorageSharedKeyCredential } from "@azure/storage-blob"; +import { + TableServiceClient, + TableClient, + AzureNamedKeyCredential, + GetTableEntityResponse, + TableEntity, + odata, + TransactionAction, + CreateDeleteEntityAction, +} from "@azure/data-tables"; +import { isPrototypePollutionKey } from "./storage"; + +module Keys { + // Can these symbols break us? + const DELIMITER = " "; + const LEAF_MARKER = "*"; + + export function getAccountPartitionKey(accountId: string): string { + validateParameters(Array.prototype.slice.apply(arguments)); + return "accountId" + DELIMITER + accountId; + } + + export function getAccountAddress(accountId: string): Pointer { + validateParameters(Array.prototype.slice.apply(arguments)); + return { + partitionKeyPointer: getAccountPartitionKey(accountId), + rowKeyPointer: getHierarchicalAccountRowKey(accountId), + }; + } + + export function getAppPartitionKey(appId: string): string { + validateParameters(Array.prototype.slice.apply(arguments)); + return "appId" + DELIMITER + appId; + } + + export function getHierarchicalAppRowKey(appId?: string, deploymentId?: string): string { + validateParameters(Array.prototype.slice.apply(arguments)); + return generateHierarchicalAppKey(/*markLeaf=*/ true, appId, deploymentId); + } + + export function getHierarchicalAccountRowKey(accountId: string, appId?: string): string { + validateParameters(Array.prototype.slice.apply(arguments)); + return generateHierarchicalAccountKey(/*markLeaf=*/ true, accountId, appId); + } + + export function generateHierarchicalAppKey(markLeaf: boolean, appId: string, deploymentId?: string): string { + validateParameters(Array.prototype.slice.apply(arguments).slice(1)); + let key = delimit("appId", appId, /*prependDelimiter=*/ false); + + if (typeof deploymentId !== "undefined") { + key += delimit("deploymentId", deploymentId); + } + + // Mark leaf key with a '*', e.g. 'appId 123 deploymentId 456' -> 'appId 123 deploymentId* 456' + if (markLeaf) { + const lastIdDelimiter: number = key.lastIndexOf(DELIMITER); + key = key.substring(0, lastIdDelimiter) + LEAF_MARKER + key.substring(lastIdDelimiter); + } + + return key; + } + + export function generateHierarchicalAccountKey(markLeaf: boolean, accountId: string, appId?: string): string { + validateParameters(Array.prototype.slice.apply(arguments).slice(1)); + let key = delimit("accountId", accountId, /*prependDelimiter=*/ false); + + if (typeof appId !== "undefined") { + key += delimit("appId", appId); + } + + // Mark leaf key with a '*', e.g. 'accountId 123 appId 456' -> 'accountId 123 appId* 456' + if (markLeaf) { + const lastIdDelimiter: number = key.lastIndexOf(DELIMITER); + key = key.substring(0, lastIdDelimiter) + LEAF_MARKER + key.substring(lastIdDelimiter); + } + + return key; + } + + export function getAccessKeyRowKey(accountId: string, accessKeyId?: string): string { + validateParameters(Array.prototype.slice.apply(arguments)); + let key: string = "accountId_" + accountId + "_accessKeyId*_"; + + if (accessKeyId !== undefined) { + key += accessKeyId; + } + + return key; + } + + export function isDeployment(rowKey: string): boolean { + return rowKey.indexOf("deploymentId*") !== -1; + } + + // To prevent a table scan when querying by properties for which we don't have partition information, we create shortcut + // partitions which hold single entries + export function getEmailShortcutAddress(email: string): Pointer { + validateParameters(Array.prototype.slice.apply(arguments)); + // We lower-case the email in our storage lookup because Partition/RowKeys are case-sensitive, but in all other cases we leave + // the email as-is (as a new account with a different casing would be rejected as a duplicate at creation time) + return { + partitionKeyPointer: "email" + DELIMITER + email.toLowerCase(), + rowKeyPointer: "", + }; + } + + export function getShortcutDeploymentKeyPartitionKey(deploymentKey: string): string { + validateParameters(Array.prototype.slice.apply(arguments)); + return delimit("deploymentKey", deploymentKey, /*prependDelimiter=*/ false); + } + + export function getShortcutDeploymentKeyRowKey(): string { + return ""; + } + + export function getShortcutAccessKeyPartitionKey(accessKeyName: string, hash: boolean = true): string { + validateParameters(Array.prototype.slice.apply(arguments)); + return delimit("accessKey", hash ? utils.hashWithSHA256(accessKeyName) : accessKeyName, /*prependDelimiter=*/ false); + } + + // Last layer of defense against uncaught injection attacks - raise an uncaught exception + function validateParameters(parameters: string[]): void { + parameters.forEach((parameter: string): void => { + if (parameter && (parameter.indexOf(DELIMITER) >= 0 || parameter.indexOf(LEAF_MARKER) >= 0)) { + throw storage.storageError(storage.ErrorCode.Invalid, `The parameter '${parameter}' contained invalid characters.`); + } + }); + } + + function delimit(fieldName: string, value: string, prependDelimiter = true): string { + const prefix = prependDelimiter ? DELIMITER : ""; + return prefix + fieldName + DELIMITER + value; + } +} + +interface Pointer { + partitionKeyPointer: string; + rowKeyPointer: string; +} + +interface DeploymentKeyPointer { + appId: string; + deploymentId: string; +} + +interface AccessKeyPointer { + accountId: string; + expires: number; +} + +export class AzureStorage implements storage.Storage { + public static NO_ID_ERROR = "No id set"; + + private static HISTORY_BLOB_CONTAINER_NAME = "packagehistoryv1"; + private static MAX_PACKAGE_HISTORY_LENGTH = 50; + private static TABLE_NAME = "storagev2"; + + private _tableClient: TableClient; + private _blobService: BlobServiceClient; + private _setupPromise: q.Promise; + + public constructor(accountName?: string, accountKey?: string) { + shortid.characters("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-"); + + this._setupPromise = this.setup(accountName, accountKey); + } + + public reinitialize(accountName?: string, accountKey?: string): q.Promise { + console.log("Re-initializing Azure storage"); + return this.setup(accountName, accountKey); + } + + public checkHealth(): q.Promise { + return q.Promise((resolve, reject) => { + this._setupPromise + .then(() => { + const tableCheck: q.Promise = q.Promise((tableResolve, tableReject) => { + this._tableClient + .getEntity(/*partitionKey=*/ "health", /*rowKey=*/ "health") + .then((entity: any) => { + if ((entity).health !== "health") { + tableReject( + storage.storageError(storage.ErrorCode.ConnectionFailed, "The Azure Tables service failed the health check") + ); + } else { + tableResolve(); + } + }) + .catch(tableReject); + }); + + const acquisitionBlobCheck: q.Promise = this.blobHealthCheck(AzureStorage.TABLE_NAME); + const historyBlobCheck: q.Promise = this.blobHealthCheck(AzureStorage.HISTORY_BLOB_CONTAINER_NAME); + + return q.all([tableCheck, acquisitionBlobCheck, historyBlobCheck]); + }) + .then(() => { + resolve(); + }) + .catch(reject); + }); + } + + public addAccount(account: storage.Account): q.Promise { + account = storage.clone(account); // pass by value + account.id = shortid.generate(); + + const hierarchicalAddress: Pointer = Keys.getAccountAddress(account.id); + const emailShortcutAddress: Pointer = Keys.getEmailShortcutAddress(account.email); + + // Store the actual Account in the email partition, and a Pointer in the other partitions + const accountPointer: Pointer = Keys.getEmailShortcutAddress(account.email); + + return this._setupPromise + .then(() => { + const entity: any = this.wrap(account, emailShortcutAddress.partitionKeyPointer, emailShortcutAddress.rowKeyPointer); + return this._tableClient.createEntity(entity); // Successfully fails if duplicate email + }) + .then(() => { + const entity: any = this.wrap(accountPointer, hierarchicalAddress.partitionKeyPointer, hierarchicalAddress.rowKeyPointer); + return this._tableClient.createEntity(entity); + }) + .then(() => { + return account.id; + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getAccount(accountId: string): q.Promise { + const address: Pointer = Keys.getAccountAddress(accountId); + + return this._setupPromise + .then(() => { + return this.retrieveByKey(address.partitionKeyPointer, address.rowKeyPointer); + }) + .then((pointer: Pointer) => { + return this.retrieveByKey(pointer.partitionKeyPointer, pointer.rowKeyPointer); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getAccountByEmail(email: string): q.Promise { + const address: Pointer = Keys.getEmailShortcutAddress(email); + return this._setupPromise + .then(() => { + return this.retrieveByKey(address.partitionKeyPointer, address.rowKeyPointer); + }) + .catch((azureError: any): any => { + AzureStorage.azureErrorHandler( + azureError, + true, + "ResourceNotFound", + "The specified e-mail address doesn't represent a registered user" + ); + }); + } + + public updateAccount(email: string, updateProperties: storage.Account): q.Promise { + if (!email) throw new Error("No account email"); + const address: Pointer = Keys.getEmailShortcutAddress(email); + const updates: any = { + azureAdId: updateProperties.azureAdId, + gitHubId: updateProperties.gitHubId, + microsoftId: updateProperties.microsoftId, + }; + + return this._setupPromise + .then(() => { + const entity: any = this.wrap(updates, address.partitionKeyPointer, address.rowKeyPointer); + return this._tableClient.updateEntity(entity); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getAccountIdFromAccessKey(accessKey: string): q.Promise { + const partitionKey: string = Keys.getShortcutAccessKeyPartitionKey(accessKey); + const rowKey: string = ""; + + return this._setupPromise + .then(() => { + return this.retrieveByKey(partitionKey, rowKey); + }) + .then((accountIdObject: AccessKeyPointer) => { + if (new Date().getTime() >= accountIdObject.expires) { + throw storage.storageError(storage.ErrorCode.Expired, "The access key has expired."); + } + + return accountIdObject.accountId; + }) + .catch(AzureStorage.azureErrorHandler); + } + + public addApp(accountId: string, app: storage.App): q.Promise { + app = storage.clone(app); // pass by value + app.id = shortid.generate(); + + return this._setupPromise + .then(() => { + return this.getAccount(accountId); + }) + .then((account: storage.Account) => { + const collabMap: storage.CollaboratorMap = {}; + collabMap[account.email] = { accountId: accountId, permission: storage.Permissions.Owner }; + + app.collaborators = collabMap; + + const flatApp: any = AzureStorage.flattenApp(app, /*updateCollaborator*/ true); + return this.insertByAppHierarchy(flatApp, app.id); + }) + .then(() => { + return this.addAppPointer(accountId, app.id); + }) + .then(() => { + return app; + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getApps(accountId: string): q.Promise { + return this._setupPromise + .then(() => { + return this.getCollectionByHierarchy(accountId); + }) + .then((flatApps: any[]) => { + const apps: storage.App[] = flatApps.map((flatApp: any) => { + return AzureStorage.unflattenApp(flatApp, accountId); + }); + + return apps; + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getApp(accountId: string, appId: string, keepCollaboratorIds: boolean = false): q.Promise { + return this._setupPromise + .then(() => { + return this.retrieveByAppHierarchy(appId); + }) + .then((flatApp: any) => { + return AzureStorage.unflattenApp(flatApp, accountId); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public removeApp(accountId: string, appId: string): q.Promise { + // remove entries for all collaborators account before removing the app + return this._setupPromise + .then(() => { + return this.removeAllCollaboratorsAppPointers(accountId, appId); + }) + .then(() => { + return this.cleanUpByAppHierarchy(appId); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public updateApp(accountId: string, app: storage.App): q.Promise { + const appId: string = app.id; + if (!appId) throw new Error("No app id"); + + return this._setupPromise + .then(() => { + return this.updateAppWithPermission(accountId, app, /*updateCollaborator*/ false); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public transferApp(accountId: string, appId: string, email: string): q.Promise { + let app: storage.App; + let targetCollaboratorAccountId: string; + let requestingCollaboratorEmail: string; + let isTargetAlreadyCollaborator: boolean; + + return this._setupPromise + .then(() => { + const getAppPromise: q.Promise = this.getApp(accountId, appId, /*keepCollaboratorIds*/ true); + const accountPromise: q.Promise = this.getAccountByEmail(email); + return q.all([getAppPromise, accountPromise]); + }) + .spread((appPromiseResult: storage.App, accountPromiseResult: storage.Account) => { + targetCollaboratorAccountId = accountPromiseResult.id; + email = accountPromiseResult.email; // Use the original email stored on the account to ensure casing is consistent + app = appPromiseResult; + requestingCollaboratorEmail = AzureStorage.getEmailForAccountId(app.collaborators, accountId); + + if (requestingCollaboratorEmail === email) { + throw storage.storageError(storage.ErrorCode.AlreadyExists, "The given account already owns the app."); + } + + return this.getApps(targetCollaboratorAccountId); + }) + .then((appsForCollaborator: storage.App[]) => { + if (storage.NameResolver.isDuplicate(appsForCollaborator, app.name)) { + throw storage.storageError( + storage.ErrorCode.AlreadyExists, + 'Cannot transfer ownership. An app with name "' + app.name + '" already exists for the given collaborator.' + ); + } + + isTargetAlreadyCollaborator = AzureStorage.isCollaborator(app.collaborators, email); + + // Update the current owner to be a collaborator + AzureStorage.setCollaboratorPermission(app.collaborators, requestingCollaboratorEmail, storage.Permissions.Collaborator); + + // set target collaborator as an owner. + if (isTargetAlreadyCollaborator) { + AzureStorage.setCollaboratorPermission(app.collaborators, email, storage.Permissions.Owner); + } else { + const targetOwnerProperties: storage.CollaboratorProperties = { + accountId: targetCollaboratorAccountId, + permission: storage.Permissions.Owner, + }; + AzureStorage.addToCollaborators(app.collaborators, email, targetOwnerProperties); + } + + return this.updateAppWithPermission(accountId, app, /*updateCollaborator*/ true); + }) + .then(() => { + if (!isTargetAlreadyCollaborator) { + // Added a new collaborator as owner to the app, create a corresponding entry for app in target collaborator's account. + return this.addAppPointer(targetCollaboratorAccountId, app.id); + } + }) + .catch(AzureStorage.azureErrorHandler); + } + + public addCollaborator(accountId: string, appId: string, email: string): q.Promise { + return this._setupPromise + .then(() => { + const getAppPromise: q.Promise = this.getApp(accountId, appId, /*keepCollaboratorIds*/ true); + const accountPromise: q.Promise = this.getAccountByEmail(email); + return q.all([getAppPromise, accountPromise]); + }) + .spread((app: storage.App, account: storage.Account) => { + // Use the original email stored on the account to ensure casing is consistent + email = account.email; + return this.addCollaboratorWithPermissions(accountId, app, email, { + accountId: account.id, + permission: storage.Permissions.Collaborator, + }); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getCollaborators(accountId: string, appId: string): q.Promise { + return this._setupPromise + .then(() => { + return this.getApp(accountId, appId, /*keepCollaboratorIds*/ false); + }) + .then((app: storage.App) => { + return q(app.collaborators); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public removeCollaborator(accountId: string, appId: string, email: string): q.Promise { + return this._setupPromise + .then(() => { + return this.getApp(accountId, appId, /*keepCollaboratorIds*/ true); + }) + .then((app: storage.App) => { + const removedCollabProperties: storage.CollaboratorProperties = app.collaborators[email]; + + if (!removedCollabProperties) { + throw storage.storageError(storage.ErrorCode.NotFound, "The given email is not a collaborator for this app."); + } + + if (!AzureStorage.isOwner(app.collaborators, email)) { + delete app.collaborators[email]; + } else { + throw storage.storageError(storage.ErrorCode.AlreadyExists, "Cannot remove the owner of the app from collaborator list."); + } + + return this.updateAppWithPermission(accountId, app, /*updateCollaborator*/ true).then(() => { + return this.removeAppPointer(removedCollabProperties.accountId, app.id); + }); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public addDeployment(accountId: string, appId: string, deployment: storage.Deployment): q.Promise { + let deploymentId: string; + return this._setupPromise + .then(() => { + const flatDeployment: any = AzureStorage.flattenDeployment(deployment); + flatDeployment.id = shortid.generate(); + + return this.insertByAppHierarchy(flatDeployment, appId, flatDeployment.id); + }) + .then((returnedId: string) => { + deploymentId = returnedId; + return this.uploadToHistoryBlob(deploymentId, JSON.stringify([])); + }) + .then(() => { + const shortcutPartitionKey: string = Keys.getShortcutDeploymentKeyPartitionKey(deployment.key); + const shortcutRowKey: string = Keys.getShortcutDeploymentKeyRowKey(); + const pointer: DeploymentKeyPointer = { + appId: appId, + deploymentId: deploymentId, + }; + + const entity: any = this.wrap(pointer, shortcutPartitionKey, shortcutRowKey); + return this._tableClient.createEntity(entity); + }) + .then(() => { + return deploymentId; + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getDeploymentInfo(deploymentKey: string): q.Promise { + const partitionKey: string = Keys.getShortcutDeploymentKeyPartitionKey(deploymentKey); + const rowKey: string = Keys.getShortcutDeploymentKeyRowKey(); + + return this._setupPromise + .then(() => { + return this.retrieveByKey(partitionKey, rowKey); + }) + .then((pointer: DeploymentKeyPointer): storage.DeploymentInfo => { + if (!pointer) { + return null; + } + + return { appId: pointer.appId, deploymentId: pointer.deploymentId }; + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getPackageHistoryFromDeploymentKey(deploymentKey: string): q.Promise { + const pointerPartitionKey: string = Keys.getShortcutDeploymentKeyPartitionKey(deploymentKey); + const pointerRowKey: string = Keys.getShortcutDeploymentKeyRowKey(); + + return this._setupPromise + .then(() => { + return this.retrieveByKey(pointerPartitionKey, pointerRowKey); + }) + .then((pointer: DeploymentKeyPointer) => { + if (!pointer) return null; + + return this.getPackageHistoryFromBlob(pointer.deploymentId); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getDeployment(accountId: string, appId: string, deploymentId: string): q.Promise { + return this._setupPromise + .then(() => { + return this.retrieveByAppHierarchy(appId, deploymentId); + }) + .then((flatDeployment: any) => { + return AzureStorage.unflattenDeployment(flatDeployment); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getDeployments(accountId: string, appId: string): q.Promise { + return this._setupPromise + .then(() => { + return this.getCollectionByHierarchy(accountId, appId); + }) + .then((flatDeployments: any[]) => { + const deployments: storage.Deployment[] = []; + flatDeployments.forEach((flatDeployment: any) => { + deployments.push(AzureStorage.unflattenDeployment(flatDeployment)); + }); + + return deployments; + }) + .catch(AzureStorage.azureErrorHandler); + } + + public removeDeployment(accountId: string, appId: string, deploymentId: string): q.Promise { + return this._setupPromise + .then(() => { + return this.cleanUpByAppHierarchy(appId, deploymentId); + }) + .then(() => { + return this.deleteHistoryBlob(deploymentId); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public updateDeployment(accountId: string, appId: string, deployment: storage.Deployment): q.Promise { + const deploymentId: string = deployment.id; + if (!deploymentId) throw new Error("No deployment id"); + + return this._setupPromise + .then(() => { + const flatDeployment: any = AzureStorage.flattenDeployment(deployment); + return this.mergeByAppHierarchy(flatDeployment, appId, deploymentId); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public commitPackage( + accountId: string, + appId: string, + deploymentId: string, + appPackage: storage.Package + ): q.Promise { + if (!deploymentId) throw new Error("No deployment id"); + if (!appPackage) throw new Error("No package specified"); + + appPackage = storage.clone(appPackage); // pass by value + + let packageHistory: storage.Package[]; + return this._setupPromise + .then(() => { + return this.getPackageHistoryFromBlob(deploymentId); + }) + .then((history: storage.Package[]) => { + packageHistory = history; + appPackage.label = this.getNextLabel(packageHistory); + return this.getAccount(accountId); + }) + .then((account: storage.Account) => { + appPackage.releasedBy = account.email; + + // Remove the rollout value for the last package. + const lastPackage: storage.Package = + packageHistory && packageHistory.length ? packageHistory[packageHistory.length - 1] : null; + if (lastPackage) { + lastPackage.rollout = null; + } + + packageHistory.push(appPackage); + + if (packageHistory.length > AzureStorage.MAX_PACKAGE_HISTORY_LENGTH) { + packageHistory.splice(0, packageHistory.length - AzureStorage.MAX_PACKAGE_HISTORY_LENGTH); + } + + const flatPackage: any = { id: deploymentId, package: JSON.stringify(appPackage) }; + return this.mergeByAppHierarchy(flatPackage, appId, deploymentId); + }) + .then(() => { + return this.uploadToHistoryBlob(deploymentId, JSON.stringify(packageHistory)); + }) + .then((): storage.Package => { + return appPackage; + }) + .catch(AzureStorage.azureErrorHandler); + } + + public clearPackageHistory(accountId: string, appId: string, deploymentId: string): q.Promise { + return this._setupPromise + .then(() => { + return this.retrieveByAppHierarchy(appId, deploymentId); + }) + .then((flatDeployment: any) => { + delete flatDeployment.package; + return this.updateByAppHierarchy(flatDeployment, appId, deploymentId); + }) + .then(() => { + return this.uploadToHistoryBlob(deploymentId, JSON.stringify([])); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getPackageHistory(accountId: string, appId: string, deploymentId: string): q.Promise { + return this._setupPromise + .then(() => { + return this.getPackageHistoryFromBlob(deploymentId); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public updatePackageHistory(accountId: string, appId: string, deploymentId: string, history: storage.Package[]): q.Promise { + // If history is null or empty array we do not update the package history, use clearPackageHistory for that. + if (!history || !history.length) { + throw storage.storageError(storage.ErrorCode.Invalid, "Cannot clear package history from an update operation"); + } + + return this._setupPromise + .then(() => { + const flatDeployment: any = { id: deploymentId, package: JSON.stringify(history[history.length - 1]) }; + return this.mergeByAppHierarchy(flatDeployment, appId, deploymentId); + }) + .then(() => { + return this.uploadToHistoryBlob(deploymentId, JSON.stringify(history)); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public addBlob(blobId: string, stream: stream.Readable, streamLength: number): q.Promise { + return this._setupPromise + .then(() => { + return utils.streamToBuffer(stream); + }) + .then((buffer) => { + return this._blobService.getContainerClient(AzureStorage.TABLE_NAME).uploadBlockBlob(blobId, buffer, buffer.byteLength); + }) + .then(() => { + return blobId; + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getBlobUrl(blobId: string): q.Promise { + return this._setupPromise + .then(() => { + return this._blobService.getContainerClient(AzureStorage.TABLE_NAME).getBlobClient(blobId).url; + }) + .catch(AzureStorage.azureErrorHandler); + } + + public removeBlob(blobId: string): q.Promise { + return this._setupPromise + .then(() => { + return this._blobService.getContainerClient(AzureStorage.TABLE_NAME).deleteBlob(blobId); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public addAccessKey(accountId: string, accessKey: storage.AccessKey): q.Promise { + accessKey = storage.clone(accessKey); // pass by value + accessKey.id = shortid.generate(); + + return this._setupPromise + .then(() => { + const partitionKey: string = Keys.getShortcutAccessKeyPartitionKey(accessKey.name); + const rowKey: string = ""; + const accessKeyPointer: AccessKeyPointer = { accountId, expires: accessKey.expires }; + const accessKeyPointerEntity: any = this.wrap(accessKeyPointer, partitionKey, rowKey); + return this._tableClient.createEntity(accessKeyPointerEntity); + }) + .then(() => { + return this.insertAccessKey(accessKey, accountId); + }) + .then((): string => { + return accessKey.id; + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getAccessKey(accountId: string, accessKeyId: string): q.Promise { + const partitionKey: string = Keys.getAccountPartitionKey(accountId); + const rowKey: string = Keys.getAccessKeyRowKey(accountId, accessKeyId); + return this._setupPromise + .then(() => { + return this.retrieveByKey(partitionKey, rowKey); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public getAccessKeys(accountId: string): q.Promise { + const deferred = q.defer(); + + const partitionKey: string = Keys.getAccountPartitionKey(accountId); + const rowKey: string = Keys.getHierarchicalAccountRowKey(accountId); + const searchKey: string = Keys.getAccessKeyRowKey(accountId); + + // Fetch both the parent account (for error-checking purposes) and the access tokens + const query = `PartitionKey eq '${partitionKey}' and (RowKey eq '${rowKey}' or (RowKey gt '${searchKey}' and RowKey lt '${searchKey}~'))`; + const options = { queryOptions: { filter: query } }; + + this._setupPromise.then(() => { + this._tableClient + .listEntities(options) + .byPage() + .next() + .then((response) => { + const entities: TableEntity[] = response.value; + if (entities.length === 0) { + // Reject as 'not found' if we can't even find the parent entity + throw storage.storageError(storage.ErrorCode.NotFound); + } + + const objects: storage.AccessKey[] = []; + + entities.forEach((entity: any) => { + // Don't include the account + if (entity.rowKey !== rowKey) { + objects.push(this.unwrap(entity)); + } + }); + + deferred.resolve(objects); + }) + .catch((error: any) => { + deferred.reject(error); + }); + }); + + return deferred.promise; + } + + public removeAccessKey(accountId: string, accessKeyId: string): q.Promise { + return this._setupPromise + .then(() => { + return this.getAccessKey(accountId, accessKeyId); + }) + .then((accessKey) => { + const partitionKey: string = Keys.getAccountPartitionKey(accountId); + const rowKey: string = Keys.getAccessKeyRowKey(accountId, accessKeyId); + const shortcutAccessKeyPartitionKey: string = Keys.getShortcutAccessKeyPartitionKey(accessKey.name, false); + + return q.all([ + this._tableClient.deleteEntity(partitionKey, rowKey), + this._tableClient.deleteEntity(shortcutAccessKeyPartitionKey, ""), + ]); + }) + .catch(AzureStorage.azureErrorHandler); + } + + public updateAccessKey(accountId: string, accessKey: storage.AccessKey): q.Promise { + if (!accessKey) { + throw new Error("No access key"); + } + + if (!accessKey.id) { + throw new Error("No access key id"); + } + + const partitionKey: string = Keys.getAccountPartitionKey(accountId); + const rowKey: string = Keys.getAccessKeyRowKey(accountId, accessKey.id); + + return this._setupPromise + .then(() => { + const entity: any = this.wrap(accessKey, partitionKey, rowKey); + return this._tableClient.updateEntity(entity); + }) + .then(() => { + const newAccessKeyPointer: AccessKeyPointer = { + accountId, + expires: accessKey.expires, + }; + + const accessKeyPointerEntity: any = this.wrap( + newAccessKeyPointer, + Keys.getShortcutAccessKeyPartitionKey(accessKey.name, false), + "" + ); + return this._tableClient.updateEntity(accessKeyPointerEntity); + }) + .catch(AzureStorage.azureErrorHandler); + } + + // No-op for safety, so that we don't drop the wrong db, pending a cleaner solution for removing test data. + public dropAll(): q.Promise { + return q(null); + } + + private setup(accountName?: string, accountKey?: string): q.Promise { + let tableServiceClient: TableServiceClient; + let tableClient: TableClient; + let blobServiceClient: BlobServiceClient; + + if (process.env.EMULATED) { + const devConnectionString = "UseDevelopmentStorage=true"; + + tableServiceClient = TableServiceClient.fromConnectionString(devConnectionString); + tableClient = TableClient.fromConnectionString(devConnectionString, AzureStorage.TABLE_NAME); + blobServiceClient = BlobServiceClient.fromConnectionString(devConnectionString); + } else { + if ((!accountName && !process.env.AZURE_STORAGE_ACCOUNT) || (!accountKey && !process.env.AZURE_STORAGE_ACCESS_KEY)) { + throw new Error("Azure credentials not set"); + } + + const _accountName = accountName ?? process.env.AZURE_STORAGE_ACCOUNT; + const _accountKey = accountKey ?? process.env.AZURE_STORAGE_ACCESS_KEY; + + const tableStorageCredential = new AzureNamedKeyCredential(_accountName, _accountKey); + const blobStorageCredential = new StorageSharedKeyCredential(_accountName, _accountKey); + + const tableServiceUrl = `https://${_accountName}.table.core.windows.net`; + const blobServiceUrl = `https://${_accountName}.blob.core.windows.net`; + + tableServiceClient = new TableServiceClient(tableServiceUrl, tableStorageCredential, { + retryOptions: { + maxRetries: 3, + maxRetryDelayInMs: 2000, + retryDelayInMs: 500, + }, + }); + tableClient = new TableClient(tableServiceUrl, AzureStorage.TABLE_NAME, tableStorageCredential); + blobServiceClient = new BlobServiceClient(blobServiceUrl, blobStorageCredential, { + retryOptions: { + maxTries: 4, + maxRetryDelayInMs: 2000, + retryDelayInMs: 500, + }, + }); + } + + const tableHealthEntity: any = this.wrap({ health: "health" }, /*partitionKey=*/ "health", /*rowKey=*/ "health"); + + return q + .all([ + tableServiceClient.createTable(AzureStorage.TABLE_NAME), + blobServiceClient.createContainer(AzureStorage.TABLE_NAME, { access: "blob" }), + blobServiceClient.createContainer(AzureStorage.HISTORY_BLOB_CONTAINER_NAME), + ]) + .then(() => { + return q.all([ + tableClient.createEntity(tableHealthEntity), + blobServiceClient.getContainerClient(AzureStorage.TABLE_NAME).uploadBlockBlob("health", "health", "health".length), + blobServiceClient + .getContainerClient(AzureStorage.HISTORY_BLOB_CONTAINER_NAME) + .uploadBlockBlob("health", "health", "health".length), + ]); + }) + .then(() => { + // Do not assign these unless everything completes successfully, as this will cause in-flight promise chains to start using + // the initialized services + this._tableClient = tableClient; + this._blobService = blobServiceClient; + }) + .catch((error) => { + if (error.code == "ContainerAlreadyExists") { + this._tableClient = tableClient; + this._blobService = blobServiceClient; + } else { + throw error; + } + }); + } + + private blobHealthCheck(container: string): q.Promise { + const deferred = q.defer(); + + this._blobService + .getContainerClient(container) + .getBlobClient("health") + .downloadToBuffer() + .then((blobContents: Buffer) => { + if (blobContents.toString() !== "health") { + deferred.reject( + storage.storageError( + storage.ErrorCode.ConnectionFailed, + "The Azure Blobs service failed the health check for " + container + ) + ); + } else { + deferred.resolve(); + } + }) + .catch((error: any) => { + deferred.reject(error); + }); + + return deferred.promise; + } + + private getPackageHistoryFromBlob(blobId: string): q.Promise { + const deferred = q.defer(); + + this._blobService + .getContainerClient(AzureStorage.HISTORY_BLOB_CONTAINER_NAME) + .getBlobClient(blobId) + .downloadToBuffer() + .then((blobContents: Buffer) => { + const parsedContents = JSON.parse(blobContents.toString()); + deferred.resolve(parsedContents); + }) + .catch((error: any) => { + deferred.reject(error); + }); + + return deferred.promise; + } + + private uploadToHistoryBlob(blobId: string, content: string): q.Promise { + const deferred = q.defer(); + + this._blobService + .getContainerClient(AzureStorage.HISTORY_BLOB_CONTAINER_NAME) + .uploadBlockBlob(blobId, content, content.length) + .then(() => { + deferred.resolve(); + }) + .catch((error: any) => { + deferred.reject(error); + }); + + return deferred.promise; + } + + private deleteHistoryBlob(blobId: string): q.Promise { + const deferred = q.defer(); + + this._blobService + .getContainerClient(AzureStorage.HISTORY_BLOB_CONTAINER_NAME) + .deleteBlob(blobId) + .then(() => { + deferred.resolve(); + }) + .catch((error: any) => { + deferred.reject(error); + }); + + return deferred.promise; + } + + private wrap(jsObject: any, partitionKey: string, rowKey: string): any { + return { + partitionKey, + rowKey, + ...jsObject, + }; + } + + private unwrap(entity: any, includeKey?: boolean): any { + const { partitionKey, rowKey, etag, timestamp, createdTime, ...rest } = entity; + + let unwrapped = includeKey ? { partitionKey, rowKey, ...rest } : rest; + + if (typeof createdTime === "bigint") { + unwrapped = { ...unwrapped, createdTime: Number(createdTime) }; + } + + return unwrapped; + } + + private addCollaboratorWithPermissions( + accountId: string, + app: storage.App, + email: string, + collabProperties: storage.CollaboratorProperties + ): q.Promise { + if (app && app.collaborators && !app.collaborators[email]) { + app.collaborators[email] = collabProperties; + return this.updateAppWithPermission(accountId, app, /*updateCollaborator*/ true).then(() => { + return this.addAppPointer(collabProperties.accountId, app.id); + }); + } else { + throw storage.storageError(storage.ErrorCode.AlreadyExists, "The given account is already a collaborator for this app."); + } + } + + private addAppPointer(accountId: string, appId: string): q.Promise { + const deferred = q.defer(); + + const appPartitionKey: string = Keys.getAppPartitionKey(appId); + const appRowKey: string = Keys.getHierarchicalAppRowKey(appId); + const pointer: Pointer = { partitionKeyPointer: appPartitionKey, rowKeyPointer: appRowKey }; + + const accountPartitionKey: string = Keys.getAccountPartitionKey(accountId); + const accountRowKey: string = Keys.getHierarchicalAccountRowKey(accountId, appId); + + const entity: any = this.wrap(pointer, accountPartitionKey, accountRowKey); + this._tableClient + .createEntity(entity) + .then(() => { + deferred.resolve(); + }) + .catch((error: any) => { + deferred.reject(error); + }); + + return deferred.promise; + } + + private removeAppPointer(accountId: string, appId: string): q.Promise { + const deferred = q.defer(); + + const accountPartitionKey: string = Keys.getAccountPartitionKey(accountId); + const accountRowKey: string = Keys.getHierarchicalAccountRowKey(accountId, appId); + + this._tableClient + .deleteEntity(accountPartitionKey, accountRowKey) + .then(() => { + deferred.resolve(); + }) + .catch((error: any) => { + deferred.reject(error); + }); + + return deferred.promise; + } + + private removeAllCollaboratorsAppPointers(accountId: string, appId: string): q.Promise { + return this.getApp(accountId, appId, /*keepCollaboratorIds*/ true) + .then((app: storage.App) => { + const collaboratorMap: storage.CollaboratorMap = app.collaborators; + const requesterEmail: string = AzureStorage.getEmailForAccountId(collaboratorMap, accountId); + + const removalPromises: q.Promise[] = []; + + Object.keys(collaboratorMap).forEach((key: string) => { + const collabProperties: storage.CollaboratorProperties = collaboratorMap[key]; + removalPromises.push(this.removeAppPointer(collabProperties.accountId, app.id)); + }); + + return q.allSettled(removalPromises); + }) + .then(() => { }); + } + + private updateAppWithPermission(accountId: string, app: storage.App, updateCollaborator: boolean = false): q.Promise { + const appId: string = app.id; + if (!appId) throw new Error("No app id"); + + const flatApp: any = AzureStorage.flattenApp(app, updateCollaborator); + return this.mergeByAppHierarchy(flatApp, appId); + } + + private insertByAppHierarchy(jsObject: Object, appId: string, deploymentId?: string): Promise { + const leafId: string = arguments[arguments.length - 1]; + const appPartitionKey: string = Keys.getAppPartitionKey(appId); + + const args = Array.prototype.slice.call(arguments); + args.shift(); // Remove 'jsObject' argument + args.pop(); // Remove the leaf id + + // Check for existence of the parent before inserting + let fetchParentPromise: Promise> = Promise.resolve(null); + if (args.length > 0) { + const parentRowKey: string = Keys.getHierarchicalAppRowKey.apply(null, args); + fetchParentPromise = this._tableClient.getEntity(appPartitionKey, parentRowKey); + } + + return fetchParentPromise + .then(() => { + // We need Pointer object to create partitionKeyPointer and rowKeyPointer fields in our table + const appRowKey: string = Keys.getHierarchicalAppRowKey(appId, deploymentId); + const pointer: Pointer = { partitionKeyPointer: appPartitionKey, rowKeyPointer: appRowKey }; + const entity: any = this.wrap(jsObject, pointer.partitionKeyPointer, pointer.rowKeyPointer); + return this._tableClient.createEntity(entity); + }) + .then(() => { + return leafId; + }); + } + + private insertAccessKey(accessKey: storage.AccessKey, accountId: string): q.Promise { + accessKey = storage.clone(accessKey); + accessKey.name = utils.hashWithSHA256(accessKey.name); + + const deferred = q.defer(); + + const partitionKey: string = Keys.getAccountPartitionKey(accountId); + const rowKey: string = Keys.getAccessKeyRowKey(accountId, accessKey.id); + + const entity: any = this.wrap(accessKey, partitionKey, rowKey); + + this._tableClient + .createEntity(entity) + .then(() => { + deferred.resolve(accessKey.id); + }) + .catch((error: any) => { + deferred.reject(error); + }); + + return deferred.promise; + } + + private retrieveByKey(partitionKey: string, rowKey: string): any { + return this._tableClient.getEntity(partitionKey, rowKey).then((entity: any) => { + return this.unwrap(entity); + }); + } + + private retrieveByAppHierarchy(appId: string, deploymentId?: string): q.Promise { + const partitionKey: string = Keys.getAppPartitionKey(appId); + const rowKey: string = Keys.getHierarchicalAppRowKey(appId, deploymentId); + return this.retrieveByKey(partitionKey, rowKey); + } + + private async getLeafEntities(query: string, childrenSearchKey: string): Promise { + const finalEntries: any[] = []; + const promises: Promise[] = []; + + for await (const entity of this._tableClient.listEntities({ + queryOptions: { filter: query }, + })) { + if (entity.partitionKeyPointer && entity.partitionKeyPointer !== "" && entity.rowKeyPointer && entity.rowKeyPointer !== "") { + const childQuery = odata`PartitionKey eq ${entity.partitionKeyPointer} and (RowKey eq ${entity.rowKeyPointer + } or (RowKey gt ${childrenSearchKey} and RowKey lt ${childrenSearchKey + "~"}))`; + + promises.push(this.getLeafEntities(childQuery, childrenSearchKey)); + } else { + finalEntries.push(entity); + } + } + + if (promises.length > 0) { + const results = await Promise.all(promises); + results.forEach((value: TableEntity[]) => { + if (value.length > 0) { + finalEntries.push(...value); + } + }); + + return finalEntries; + } else { + return finalEntries; + } + } + + private async getCollectionByHierarchy(accountId: string, appId?: string, deploymentId?: string): Promise { + let partitionKey: string; + let rowKey: string; + let childrenSearchKey: string; + + // Construct a search key that fetches only the direct children at the given hierarchical location + const searchKeyArgs: any[] = Array.prototype.slice.call(arguments); + searchKeyArgs.unshift(/*markLeaf=*/ true); + searchKeyArgs.push(/*leafId=*/ ""); + + if (appId) { + searchKeyArgs.splice(1, 1); // remove accountId + partitionKey = Keys.getAppPartitionKey(appId); + rowKey = Keys.getHierarchicalAppRowKey(appId, deploymentId); + childrenSearchKey = Keys.generateHierarchicalAppKey.apply(null, searchKeyArgs); + } else { + partitionKey = Keys.getAccountPartitionKey(accountId); + rowKey = Keys.getHierarchicalAccountRowKey(accountId); + childrenSearchKey = Keys.generateHierarchicalAccountKey.apply(null, searchKeyArgs); + } + + // Fetch both the parent (for error-checking purposes) and the direct children + const query = odata`PartitionKey eq ${partitionKey} and (RowKey eq ${rowKey} or (RowKey gt ${childrenSearchKey} and RowKey lt ${childrenSearchKey + "~" + }))`; + + const entities: TableEntity[] = await this.getLeafEntities(query, childrenSearchKey); + + if (entities.length === 0) { + // Reject as 'not found' if we can't even find the parent entity + throw new Error("Entity not found"); + } + + const objects: any[] = []; + entities.forEach((entity: TableEntity) => { + // Don't include the parent + if (entity.rowKey !== rowKey) { + objects.push(this.unwrap(entity)); + } + }); + + return objects; + } + + private async cleanUpByAppHierarchy(appId: string, deploymentId?: string): Promise { + const partitionKey: string = Keys.getAppPartitionKey(appId); + const rowKey: string = Keys.getHierarchicalAppRowKey(appId, deploymentId); + const descendantsSearchKey: string = Keys.generateHierarchicalAppKey(/*markLeaf=*/ false, appId, deploymentId); + + const tableBatch: TransactionAction[] = []; + + const query = odata`PartitionKey eq '${partitionKey}' and (RowKey eq '${rowKey}' or (RowKey ge '${descendantsSearchKey}' and RowKey lt '${descendantsSearchKey}~'))`; + for await (const entity of this._tableClient.listEntities({ + queryOptions: { filter: query }, + })) { + tableBatch.push(["delete", entity] as CreateDeleteEntityAction); + } + + if (tableBatch.length > 0) { + this._tableClient.submitTransaction(tableBatch); + } + } + + private getEntityByAppHierarchy(jsObject: Object, appId: string, deploymentId?: string): any { + const partitionKey: string = Keys.getAppPartitionKey(appId); + const rowKey: string = Keys.getHierarchicalAppRowKey(appId, deploymentId); + return this.wrap(jsObject, partitionKey, rowKey); + } + + private mergeByAppHierarchy(jsObject: Object, appId: string, deploymentId?: string): q.Promise { + const deferred = q.defer(); + + const entity: any = this.getEntityByAppHierarchy(jsObject, appId, deploymentId); + this._tableClient + .updateEntity(entity) + .then(() => { + deferred.resolve(); + }) + .catch((error: any) => { + deferred.reject(error); + }); + + return deferred.promise; + } + + private updateByAppHierarchy(jsObject: Object, appId: string, deploymentId?: string): q.Promise { + const deferred = q.defer(); + + const entity: any = this.getEntityByAppHierarchy(jsObject, appId, deploymentId); + this._tableClient + .updateEntity(entity) + .then(() => { + deferred.resolve(); + }) + .catch((error: any) => { + deferred.reject(error); + }); + + return deferred.promise; + } + + private getNextLabel(packageHistory: storage.Package[]): string { + if (packageHistory.length === 0) { + return "v1"; + } + + const lastLabel: string = packageHistory[packageHistory.length - 1].label; + const lastVersion: number = parseInt(lastLabel.substring(1)); // Trim 'v' from the front + return "v" + (lastVersion + 1); + } + + private static azureErrorHandler( + azureError: any, + overrideMessage: boolean = false, + overrideCondition?: string, + overrideValue?: string + ): any { + let errorCodeRaw: number | string; + let errorMessage: string; + + try { + const parsedMessage = JSON.parse(azureError.message); + errorCodeRaw = parsedMessage["odata.error"].code; + errorMessage = parsedMessage["odata.error"].message.value; + } catch (error) { + errorCodeRaw = azureError.code; + errorMessage = azureError.message; + } + + if (overrideMessage && overrideCondition == errorCodeRaw) { + errorMessage = overrideValue; + } + + if (typeof errorCodeRaw === "number") { + // This is a storage.Error that we previously threw; just re-throw it + throw azureError; + } + + let errorCode: storage.ErrorCode; + switch (errorCodeRaw) { + case "BlobNotFound": + case "ResourceNotFound": + case "TableNotFound": + errorCode = storage.ErrorCode.NotFound; + break; + case "EntityAlreadyExists": + case "TableAlreadyExists": + errorCode = storage.ErrorCode.AlreadyExists; + break; + case "EntityTooLarge": + case "PropertyValueTooLarge": + errorCode = storage.ErrorCode.TooLarge; + break; + case "ETIMEDOUT": + case "ESOCKETTIMEDOUT": + case "ECONNRESET": + // This is an error emitted from the 'request' module, which is a + // dependency of 'azure-storage', and indicates failure after multiple + // retries. + errorCode = storage.ErrorCode.ConnectionFailed; + break; + default: + errorCode = storage.ErrorCode.Other; + break; + } + + throw storage.storageError(errorCode, errorMessage); + } + + private static deleteIsCurrentAccountProperty(map: storage.CollaboratorMap): void { + if (map) { + Object.keys(map).forEach((key: string) => { + delete (map[key]).isCurrentAccount; + }); + } + } + + private static flattenApp(app: storage.App, updateCollaborator: boolean = false): any { + if (!app) { + return app; + } + + const flatApp: any = {}; + for (const property in app) { + if (property === "collaborators" && updateCollaborator) { + AzureStorage.deleteIsCurrentAccountProperty(app.collaborators); + flatApp[property] = JSON.stringify((app)[property]); + } else if (property !== "collaborators") { + // No-op updates on these properties + flatApp[property] = (app)[property]; + } + } + + return flatApp; + } + + // Note: This does not copy the object before unflattening it + private static unflattenApp(flatApp: any, currentAccountId: string): storage.App { + flatApp.collaborators = flatApp.collaborators ? JSON.parse(flatApp.collaborators) : {}; + + const currentUserEmail: string = AzureStorage.getEmailForAccountId(flatApp.collaborators, currentAccountId); + if (currentUserEmail && flatApp.collaborators[currentUserEmail]) { + flatApp.collaborators[currentUserEmail].isCurrentAccount = true; + } + + return flatApp; + } + + private static flattenDeployment(deployment: storage.Deployment): any { + if (!deployment) { + return deployment; + } + + const flatDeployment: any = {}; + for (const property in deployment) { + if (property !== "package") { + // No-op updates on these properties + flatDeployment[property] = (deployment)[property]; + } + } + + return flatDeployment; + } + + // Note: This does not copy the object before unflattening it + private static unflattenDeployment(flatDeployment: any): storage.Deployment { + delete flatDeployment.packageHistory; + flatDeployment.package = flatDeployment.package ? JSON.parse(flatDeployment.package) : null; + + return flatDeployment; + } + + private static isOwner(collaboratorsMap: storage.CollaboratorMap, email: string): boolean { + return ( + collaboratorsMap && + email && + collaboratorsMap[email] && + (collaboratorsMap[email]).permission === storage.Permissions.Owner + ); + } + + private static isCollaborator(collaboratorsMap: storage.CollaboratorMap, email: string): boolean { + return ( + collaboratorsMap && + email && + collaboratorsMap[email] && + (collaboratorsMap[email]).permission === storage.Permissions.Collaborator + ); + } + + private static setCollaboratorPermission(collaboratorsMap: storage.CollaboratorMap, email: string, permission: string): void { + if (collaboratorsMap && email && !isPrototypePollutionKey(email) && collaboratorsMap[email]) { + (collaboratorsMap[email]).permission = permission; + } + } + + private static addToCollaborators( + collaboratorsMap: storage.CollaboratorMap, + email: string, + collabProps: storage.CollaboratorProperties + ): void { + if (collaboratorsMap && email && !isPrototypePollutionKey(email) && !collaboratorsMap[email]) { + collaboratorsMap[email] = collabProps; + } + } + + private static getEmailForAccountId(collaboratorsMap: storage.CollaboratorMap, accountId: string): string { + if (collaboratorsMap) { + for (const email of Object.keys(collaboratorsMap)) { + if ((collaboratorsMap[email]).accountId === accountId) { + return email; + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/api/script/storage/storage.ts b/api/script/storage/storage.ts index 74fe619fb..cf14d0c04 100644 --- a/api/script/storage/storage.ts +++ b/api/script/storage/storage.ts @@ -348,3 +348,9 @@ export class NameResolver { .catch(NameResolver.errorMessageOverride(ErrorCode.NotFound, `Deployment "${name}" does not exist.`)); } } + +export interface UpdateAccountProperties { + azureAdId?: string; + gitHubId?: string; + microsoftId?: string; +}