From ffc3b4212ff1f3ee7eaf7d0569a83b85d26366e1 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Tue, 17 Sep 2024 08:58:02 -0700 Subject: [PATCH 001/199] update data construct (#2013) --- .changeset/eight-cars-train.md | 5 + package-lock.json | 4235 +++++++++++++++++---- packages/backend-data/package.json | 2 +- packages/backend-data/src/factory.test.ts | 89 +- 4 files changed, 3583 insertions(+), 748 deletions(-) create mode 100644 .changeset/eight-cars-train.md diff --git a/.changeset/eight-cars-train.md b/.changeset/eight-cars-train.md new file mode 100644 index 00000000000..8b546c7a98a --- /dev/null +++ b/.changeset/eight-cars-train.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-data': patch +--- + +update data construct diff --git a/package-lock.json b/package-lock.json index 73015bf27cb..35127fe7aa4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -700,16 +700,12 @@ } }, "node_modules/@aws-amplify/data-construct": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@aws-amplify/data-construct/-/data-construct-1.10.0.tgz", - "integrity": "sha512-2w/SSsaqj0DeHJYKo1rQbNX+lvS9ja7wqqoYvRCJ/VKbSPVrNrYZORjBCQ4WIB9x3ElDVCogMboI7mgmfWeE7w==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@aws-amplify/data-construct/-/data-construct-1.10.1.tgz", + "integrity": "sha512-fG8EHT+LYpBGIOwXx2uw4IKTyZv5IWTRtnSSVBM6AYT76FsRe2qhvNnaDUWP7S5SEROQrfLxMnuWiozfeknTgg==", "bundleDependencies": [ "@aws-amplify/backend-output-schemas", "@aws-amplify/backend-output-storage", - "@aws-amplify/graphql-transformer", - "@aws-amplify/graphql-transformer-core", - "@aws-amplify/graphql-transformer-interfaces", - "zod", "@aws-amplify/graphql-auth-transformer", "@aws-amplify/graphql-conversation-transformer", "@aws-amplify/graphql-default-value-transformer", @@ -724,72 +720,237 @@ "@aws-amplify/graphql-searchable-transformer", "@aws-amplify/graphql-sql-transformer", "@aws-amplify/graphql-generation-transformer", + "@aws-amplify/ai-constructs", + "@aws-amplify/graphql-transformer", + "@aws-amplify/graphql-transformer-core", + "@aws-amplify/graphql-transformer-interfaces", "@aws-amplify/platform-core", "@aws-amplify/plugin-types", - "@aws-amplify/ai-constructs", - "fs-extra", - "graphql", - "graphql-transformer-common", - "hjson", - "lodash", - "md5", - "object-hash", - "ts-dedent", + "@aws-sdk/client-bedrock-runtime", + "@smithy/eventstream-serde-browser", + "@smithy/eventstream-serde-config-resolver", + "@smithy/eventstream-serde-node", + "@smithy/eventstream-serde-universal", + "@smithy/eventstream-codec", + "@aws-crypto/crc32", "charenc", "crypt", + "fs-extra", "graceful-fs", + "graphql", "graphql-mapping-template", + "graphql-transformer-common", + "hjson", "immer", "is-buffer", "jsonfile", "libphonenumber-js", + "lodash", + "md5", + "object-hash", "pluralize", - "universalify" + "ts-dedent", + "universalify", + "zod", + "@aws-sdk/client-sts", + "is-ci", + "lodash.mergewith", + "uuid", + "@aws-crypto/sha256-browser", + "@aws-crypto/sha256-js", + "@aws-sdk/client-sso-oidc", + "@aws-sdk/core", + "@aws-sdk/credential-provider-node", + "@aws-sdk/middleware-host-header", + "@aws-sdk/middleware-logger", + "@aws-sdk/middleware-recursion-detection", + "@aws-sdk/middleware-user-agent", + "@aws-sdk/region-config-resolver", + "@aws-sdk/types", + "@aws-sdk/util-endpoints", + "@aws-sdk/util-user-agent-browser", + "@aws-sdk/util-user-agent-node", + "@smithy/config-resolver", + "@smithy/core", + "@smithy/fetch-http-handler", + "@smithy/hash-node", + "@smithy/invalid-dependency", + "@smithy/middleware-content-length", + "@smithy/middleware-endpoint", + "@smithy/middleware-retry", + "@smithy/middleware-serde", + "@smithy/middleware-stack", + "@smithy/node-config-provider", + "@smithy/node-http-handler", + "@smithy/protocol-http", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/url-parser", + "@smithy/util-base64", + "@smithy/util-body-length-browser", + "@smithy/util-body-length-node", + "@smithy/util-defaults-mode-browser", + "@smithy/util-defaults-mode-node", + "@smithy/util-endpoints", + "@smithy/util-middleware", + "@smithy/util-retry", + "@smithy/util-utf8", + "tslib", + "ci-info", + "@aws-crypto/supports-web-crypto", + "@aws-crypto/util", + "@aws-sdk/util-locate-window", + "@smithy/signature-v4", + "fast-xml-parser", + "@aws-sdk/credential-provider-env", + "@aws-sdk/credential-provider-http", + "@aws-sdk/credential-provider-ini", + "@aws-sdk/credential-provider-process", + "@aws-sdk/credential-provider-sso", + "@aws-sdk/credential-provider-web-identity", + "@smithy/credential-provider-imds", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/util-config-provider", + "bowser", + "@smithy/querystring-builder", + "@smithy/util-buffer-from", + "@smithy/service-error-classification", + "@smithy/abort-controller", + "@smithy/util-stream", + "@smithy/querystring-parser", + "@smithy/is-array-buffer", + "@smithy/util-hex-encoding", + "@smithy/util-uri-escape", + "strnum", + "@aws-sdk/token-providers", + "@aws-sdk/client-sso", + "semver" ], - "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.1.2", - "@aws-amplify/backend-output-schemas": "^0.4.0", - "@aws-amplify/backend-output-storage": "^0.2.2", - "@aws-amplify/graphql-api-construct": "1.12.0", - "@aws-amplify/graphql-auth-transformer": "4.1.0", - "@aws-amplify/graphql-conversation-transformer": "0.2.0", - "@aws-amplify/graphql-default-value-transformer": "3.0.2", - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-function-transformer": "3.0.2", - "@aws-amplify/graphql-generation-transformer": "0.2.0", - "@aws-amplify/graphql-http-transformer": "3.0.2", - "@aws-amplify/graphql-index-transformer": "3.0.2", - "@aws-amplify/graphql-maps-to-transformer": "4.0.2", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-predictions-transformer": "3.0.2", - "@aws-amplify/graphql-relational-transformer": "3.0.2", - "@aws-amplify/graphql-searchable-transformer": "3.0.2", - "@aws-amplify/graphql-sql-transformer": "0.4.2", - "@aws-amplify/graphql-transformer": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/ai-constructs": "^0.1.4", + "@aws-amplify/backend-output-schemas": "^1.0.0", + "@aws-amplify/backend-output-storage": "^1.0.0", + "@aws-amplify/graphql-api-construct": "1.13.0", + "@aws-amplify/graphql-auth-transformer": "4.1.1", + "@aws-amplify/graphql-conversation-transformer": "0.2.1", + "@aws-amplify/graphql-default-value-transformer": "3.0.3", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-function-transformer": "3.1.0", + "@aws-amplify/graphql-generation-transformer": "0.2.1", + "@aws-amplify/graphql-http-transformer": "3.0.3", + "@aws-amplify/graphql-index-transformer": "3.0.3", + "@aws-amplify/graphql-maps-to-transformer": "4.0.3", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-predictions-transformer": "3.0.3", + "@aws-amplify/graphql-relational-transformer": "3.0.3", + "@aws-amplify/graphql-searchable-transformer": "3.0.3", + "@aws-amplify/graphql-sql-transformer": "0.4.3", + "@aws-amplify/graphql-transformer": "2.1.1", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "@aws-amplify/platform-core": "^0.2.0", - "@aws-amplify/plugin-types": "^0.4.1", + "@aws-amplify/platform-core": "^1.0.0", + "@aws-amplify/plugin-types": "^1.0.0", + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/client-bedrock-runtime": "^3.622.0", + "@aws-sdk/client-sso": "3.637.0", + "@aws-sdk/client-sso-oidc": "3.637.0", + "@aws-sdk/client-sts": "^3.624.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.637.0", + "@aws-sdk/credential-provider-node": "3.637.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.637.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/abort-controller": "^3.1.1", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/eventstream-codec": "^3.1.2", + "@smithy/eventstream-serde-browser": "^3.0.6", + "@smithy/eventstream-serde-config-resolver": "^3.0.3", + "@smithy/eventstream-serde-node": "^3.0.5", + "@smithy/eventstream-serde-universal": "^3.0.5", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/querystring-parser": "^3.0.3", + "@smithy/service-error-classification": "^3.0.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "bowser": "^2.11.0", "charenc": "^0.0.2", + "ci-info": "^3.2.0", "crypt": "^0.0.2", + "fast-xml-parser": "4.4.1", "fs-extra": "^8.1.0", - "graceful-fs": "^4.2.11", + "graceful-fs": "^4.2.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", "graphql-transformer-common": "5.0.1", "hjson": "^3.2.2", "immer": "^9.0.12", - "is-buffer": "^2.0.5", - "jsonfile": "^6.1.0", + "is-buffer": "~1.1.6", + "is-ci": "^3.0.1", + "jsonfile": "^4.0.0", "libphonenumber-js": "1.9.47", "lodash": "^4.17.21", - "md5": "^2.3.0", + "lodash.mergewith": "^4.6.2", + "md5": "^2.2.1", "object-hash": "^3.0.0", - "pluralize": "^8.0.0", + "pluralize": "8.0.0", + "semver": "^7.6.3", + "strnum": "^1.0.5", "ts-dedent": "^2.0.0", - "universalify": "^2.0.0", - "zod": "^3.22.3" + "tslib": "^2.6.2", + "universalify": "^0.1.0", + "uuid": "^9.0.1", + "zod": "^3.22.2" }, "peerDependencies": { "aws-cdk-lib": "^2.152.0", @@ -797,7 +958,7 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/ai-constructs": { - "version": "0.1.2", + "version": "0.1.4", "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -810,45 +971,49 @@ "constructs": "^10.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/ai-constructs/node_modules/@aws-amplify/plugin-types": { - "version": "1.2.1", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/backend-output-schemas": { + "version": "1.2.0", "inBundle": true, "license": "Apache-2.0", "peerDependencies": { - "@aws-sdk/types": "^3.609.0", - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.0.0" + "zod": "^3.22.2" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/backend-output-schemas": { - "version": "0.4.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/backend-output-storage": { + "version": "1.1.1", "inBundle": true, "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/backend-output-schemas": "^1.2.0", + "@aws-amplify/platform-core": "^1.0.6" + }, "peerDependencies": { - "zod": "^3.21.4" + "aws-cdk-lib": "^2.152.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/backend-output-storage": { - "version": "0.2.2", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/backend-output-storage/node_modules/@aws-amplify/platform-core": { + "version": "1.0.7", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^0.4.0", - "@aws-amplify/platform-core": "^0.2.0" - }, - "peerDependencies": { - "aws-cdk-lib": "^2.103.0" + "@aws-amplify/plugin-types": "^1.2.1", + "@aws-sdk/client-sts": "^3.624.0", + "is-ci": "^3.0.1", + "lodash.mergewith": "^4.6.2", + "semver": "^7.6.3", + "uuid": "^9.0.1", + "zod": "^3.22.2" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-auth-transformer": { - "version": "4.1.0", + "version": "4.1.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-relational-transformer": "3.0.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-relational-transformer": "3.0.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", @@ -862,16 +1027,16 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-conversation-transformer": { - "version": "0.2.0", + "version": "0.2.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.1.2", - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-index-transformer": "3.0.2", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-relational-transformer": "3.0.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/ai-constructs": "^0.1.4", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-index-transformer": "3.0.3", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-relational-transformer": "3.0.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", @@ -884,12 +1049,12 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-default-value-transformer": { - "version": "3.0.2", + "version": "3.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", @@ -898,17 +1063,17 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-directives": { - "version": "2.1.0", + "version": "2.2.0", "inBundle": true, "license": "Apache-2.0" }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-function-transformer": { - "version": "3.0.2", + "version": "3.1.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", @@ -920,12 +1085,12 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-generation-transformer": { - "version": "0.2.0", + "version": "0.2.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", @@ -938,12 +1103,12 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-http-transformer": { - "version": "3.0.2", + "version": "3.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", @@ -955,13 +1120,13 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-index-transformer": { - "version": "3.0.2", + "version": "3.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", @@ -973,12 +1138,12 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-maps-to-transformer": { - "version": "4.0.2", + "version": "4.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql-mapping-template": "5.0.1", "graphql-transformer-common": "5.0.1" @@ -989,12 +1154,12 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-model-transformer": { - "version": "3.0.2", + "version": "3.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", @@ -1006,12 +1171,12 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-predictions-transformer": { - "version": "3.0.2", + "version": "3.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", @@ -1023,14 +1188,14 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-relational-transformer": { - "version": "3.0.2", + "version": "3.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-index-transformer": "3.0.2", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-index-transformer": "3.0.3", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", @@ -1043,13 +1208,13 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-searchable-transformer": { - "version": "3.0.2", + "version": "3.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", @@ -1061,13 +1226,13 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-sql-transformer": { - "version": "0.4.2", + "version": "0.4.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", @@ -1079,24 +1244,24 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-transformer": { - "version": "2.1.0", + "version": "2.1.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-auth-transformer": "4.1.0", - "@aws-amplify/graphql-conversation-transformer": "0.2.0", - "@aws-amplify/graphql-default-value-transformer": "3.0.2", - "@aws-amplify/graphql-function-transformer": "3.0.2", - "@aws-amplify/graphql-generation-transformer": "0.2.0", - "@aws-amplify/graphql-http-transformer": "3.0.2", - "@aws-amplify/graphql-index-transformer": "3.0.2", - "@aws-amplify/graphql-maps-to-transformer": "4.0.2", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-predictions-transformer": "3.0.2", - "@aws-amplify/graphql-relational-transformer": "3.0.2", - "@aws-amplify/graphql-searchable-transformer": "3.0.2", - "@aws-amplify/graphql-sql-transformer": "0.4.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", + "@aws-amplify/graphql-auth-transformer": "4.1.1", + "@aws-amplify/graphql-conversation-transformer": "0.2.1", + "@aws-amplify/graphql-default-value-transformer": "3.0.3", + "@aws-amplify/graphql-function-transformer": "3.1.0", + "@aws-amplify/graphql-generation-transformer": "0.2.1", + "@aws-amplify/graphql-http-transformer": "3.0.3", + "@aws-amplify/graphql-index-transformer": "3.0.3", + "@aws-amplify/graphql-maps-to-transformer": "4.0.3", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-predictions-transformer": "3.0.3", + "@aws-amplify/graphql-relational-transformer": "3.0.3", + "@aws-amplify/graphql-searchable-transformer": "3.0.3", + "@aws-amplify/graphql-sql-transformer": "0.4.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", "@aws-amplify/graphql-transformer-interfaces": "4.1.0" }, "peerDependencies": { @@ -1105,11 +1270,11 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-transformer-core": { - "version": "3.1.0", + "version": "3.1.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", + "@aws-amplify/graphql-directives": "2.2.0", "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "fs-extra": "^8.1.0", "graphql": "^15.5.0", @@ -1139,724 +1304,3390 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/platform-core": { - "version": "0.2.0", + "version": "1.1.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^0.4.0" + "@aws-amplify/plugin-types": "^1.2.1", + "@aws-sdk/client-sts": "^3.624.0", + "is-ci": "^3.0.1", + "lodash.mergewith": "^4.6.2", + "semver": "^7.6.3", + "uuid": "^9.0.1", + "zod": "^3.22.2" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/plugin-types": { - "version": "0.4.1", + "version": "1.2.1", "inBundle": true, "license": "Apache-2.0", "peerDependencies": { - "aws-cdk-lib": "^2.103.0", + "@aws-sdk/types": "^3.609.0", + "aws-cdk-lib": "^2.152.0", "constructs": "^10.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/charenc": { - "version": "0.0.2", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/crc32": { + "version": "5.2.0", "inBundle": true, - "license": "BSD-3-Clause", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, "engines": { - "node": "*" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/crypt": { - "version": "0.0.2", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", "inBundle": true, - "license": "BSD-3-Clause", - "engines": { - "node": "*" + "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-amplify/data-construct/node_modules/fs-extra": { - "version": "8.1.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=14.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/fs-extra/node_modules/jsonfile": { - "version": "4.0.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", "inBundle": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/fs-extra/node_modules/universalify": { - "version": "0.1.2", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 4.0.0" + "node": ">=14.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/graceful-fs": { - "version": "4.2.11", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@aws-amplify/data-construct/node_modules/graphql": { - "version": "15.8.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 10.x" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/graphql-mapping-template": { - "version": "5.0.1", - "inBundle": true, - "license": "Apache-2.0" - }, - "node_modules/@aws-amplify/data-construct/node_modules/graphql-transformer-common": { - "version": "5.0.1", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime": { + "version": "3.642.0", + "inBundle": true, + "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.637.0", + "@aws-sdk/client-sts": "3.637.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.637.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/eventstream-serde-browser": "^3.0.6", + "@smithy/eventstream-serde-config-resolver": "^3.0.3", + "@smithy/eventstream-serde-node": "^3.0.5", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@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.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sso": { + "version": "3.637.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@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.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.637.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.637.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@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.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.637.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts": { + "version": "3.637.0", + "inBundle": true, + "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.637.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.637.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@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.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/core": { + "version": "3.635.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.4.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.620.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.635.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.637.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.637.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.637.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.637.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.637.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.637.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.620.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.637.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.637.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.621.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.620.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/middleware-logger": { + "version": "3.609.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.620.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.637.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.614.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/token-providers": { + "version": "3.614.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.614.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/util-endpoints": { + "version": "3.637.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "@smithy/util-endpoints": "^2.0.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/util-locate-window": { + "version": "3.568.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.609.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.614.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/abort-controller": { + "version": "3.1.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/config-resolver": { + "version": "3.0.5", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/core": { + "version": "2.4.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/credential-provider-imds": { + "version": "3.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-codec": { + "version": "3.1.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-serde-browser": { + "version": "3.0.6", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.5", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-serde-node": { + "version": "3.0.5", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.5", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-serde-universal": { + "version": "3.0.5", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^3.1.2", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/hash-node": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/invalid-dependency": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-content-length": { + "version": "3.0.5", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-endpoint": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-retry": { + "version": "3.0.15", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/service-error-classification": "^3.0.3", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-serde": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-stack": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/node-config-provider": { + "version": "3.1.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/node-http-handler": { + "version": "3.1.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.1", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/property-provider": { + "version": "3.1.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/protocol-http": { + "version": "4.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/querystring-builder": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/querystring-parser": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/service-error-classification": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/signature-v4": { + "version": "4.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/smithy-client": { + "version": "3.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/types": { + "version": "3.3.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/url-parser": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-base64": { + "version": "3.0.0", + "inBundle": true, + "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/@aws-amplify/data-construct/node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.15", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.15", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^3.0.5", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-endpoints": { + "version": "2.0.5", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-middleware": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-retry": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-stream": { + "version": "3.1.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/types": "^3.3.0", + "@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": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/bowser": { + "version": "2.11.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-amplify/data-construct/node_modules/charenc": { + "version": "0.0.2", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/ci-info": { + "version": "3.9.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/crypt": { + "version": "0.0.2", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/fast-xml-parser": { + "version": "4.4.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/fs-extra": { + "version": "8.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@aws-amplify/data-construct/node_modules/graphql": { + "version": "15.9.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/graphql-mapping-template": { + "version": "5.0.1", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/@aws-amplify/data-construct/node_modules/graphql-transformer-common": { + "version": "5.0.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "md5": "^2.2.1", + "pluralize": "8.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/hjson": { + "version": "3.2.2", + "inBundle": true, + "license": "MIT", + "bin": { + "hjson": "bin/hjson" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/immer": { + "version": "9.0.21", + "inBundle": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/is-buffer": { + "version": "1.1.6", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-amplify/data-construct/node_modules/is-ci": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/jsonfile": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/libphonenumber-js": { + "version": "1.9.47", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-amplify/data-construct/node_modules/lodash": { + "version": "4.17.21", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-amplify/data-construct/node_modules/lodash.mergewith": { + "version": "4.6.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-amplify/data-construct/node_modules/md5": { + "version": "2.3.0", + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/object-hash": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/pluralize": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/semver": { + "version": "7.6.3", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/strnum": { + "version": "1.0.5", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-amplify/data-construct/node_modules/ts-dedent": { + "version": "2.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/tslib": { + "version": "2.7.0", + "inBundle": true, + "license": "0BSD" + }, + "node_modules/@aws-amplify/data-construct/node_modules/universalify": { + "version": "0.1.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "inBundle": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/zod": { + "version": "3.23.8", + "inBundle": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@aws-amplify/data-schema": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema/-/data-schema-1.5.1.tgz", + "integrity": "sha512-hFDqqwHqdoFazmvGOApCX8kqrdoum9YJikmAQN5tP2sgnCT++lqznFw2F4PPqDJRxhQP1AYuwhbbRBvGLMbs/w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/data-schema-types": "*", + "@smithy/util-base64": "^3.0.0", + "@types/aws-lambda": "^8.10.134", + "@types/json-schema": "^7.0.15", + "rxjs": "^7.8.1" + } + }, + "node_modules/@aws-amplify/data-schema-types": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema-types/-/data-schema-types-1.1.1.tgz", + "integrity": "sha512-WhWEEsztpSSxIY0lJ3Ge5iA4g3PBm66SQmy1fBH1FBq0T+cxUBijifOU8MNwf+tf6lGpArMX0RS54HRVF5fUSA==", + "license": "Apache-2.0", + "dependencies": { + "graphql": "15.8.0", + "rxjs": "^7.8.1" + } + }, + "node_modules/@aws-amplify/data-schema-types/node_modules/graphql": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz", + "integrity": "sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==", + "license": "MIT", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/@aws-amplify/datastore": { + "version": "5.0.49", + "resolved": "https://registry.npmjs.org/@aws-amplify/datastore/-/datastore-5.0.49.tgz", + "integrity": "sha512-7ESzc1/5rOth3gPi65IJyx4dQbzRTvRxpefcA132C3vAm5BFqKEcDXyZX5sHbSf5OOMmQDsa68GfzhAM10rWPg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/api": "6.0.49", + "buffer": "4.9.2", + "idb": "5.0.6", + "immer": "9.0.6", + "rxjs": "^7.8.1", + "ulid": "^2.3.0" + }, + "peerDependencies": { + "@aws-amplify/core": "^6.1.0" + } + }, + "node_modules/@aws-amplify/deployed-backend-client": { + "resolved": "packages/deployed-backend-client", + "link": true + }, + "node_modules/@aws-amplify/form-generator": { + "resolved": "packages/form-generator", + "link": true + }, + "node_modules/@aws-amplify/graphql-api-construct": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@aws-amplify/graphql-api-construct/-/graphql-api-construct-1.13.0.tgz", + "integrity": "sha512-4cv4Ko7OP7lZTDhojM/ibKglP1Ospy+/iGMnKguuYoISqhmL2sgnyVHWw+1XnRVc8eUf6gnip2KXMQKnaPU0Lw==", + "bundleDependencies": [ + "@aws-amplify/backend-output-schemas", + "@aws-amplify/backend-output-storage", + "@aws-amplify/graphql-auth-transformer", + "@aws-amplify/graphql-conversation-transformer", + "@aws-amplify/graphql-default-value-transformer", + "@aws-amplify/graphql-directives", + "@aws-amplify/graphql-function-transformer", + "@aws-amplify/graphql-generation-transformer", + "@aws-amplify/graphql-http-transformer", + "@aws-amplify/graphql-index-transformer", + "@aws-amplify/graphql-maps-to-transformer", + "@aws-amplify/graphql-model-transformer", + "@aws-amplify/graphql-predictions-transformer", + "@aws-amplify/graphql-relational-transformer", + "@aws-amplify/graphql-searchable-transformer", + "@aws-amplify/graphql-sql-transformer", + "@aws-amplify/graphql-transformer", + "@aws-amplify/graphql-transformer-core", + "@aws-amplify/graphql-transformer-interfaces", + "@aws-amplify/platform-core", + "@aws-amplify/plugin-types", + "@aws-amplify/ai-constructs", + "@aws-sdk/client-bedrock-runtime", + "@smithy/eventstream-serde-browser", + "@smithy/eventstream-serde-config-resolver", + "@smithy/eventstream-serde-node", + "@smithy/eventstream-serde-universal", + "@smithy/eventstream-codec", + "@aws-crypto/crc32", + "charenc", + "crypt", + "fs-extra", + "graceful-fs", + "graphql", + "graphql-mapping-template", + "graphql-transformer-common", + "hjson", + "immer", + "is-buffer", + "jsonfile", + "libphonenumber-js", + "lodash", + "md5", + "object-hash", + "pluralize", + "ts-dedent", + "universalify", + "zod", + "@aws-sdk/client-sts", + "is-ci", + "lodash.mergewith", + "uuid", + "@aws-crypto/sha256-browser", + "@aws-crypto/sha256-js", + "@aws-sdk/client-sso-oidc", + "@aws-sdk/core", + "@aws-sdk/credential-provider-node", + "@aws-sdk/middleware-host-header", + "@aws-sdk/middleware-logger", + "@aws-sdk/middleware-recursion-detection", + "@aws-sdk/middleware-user-agent", + "@aws-sdk/region-config-resolver", + "@aws-sdk/types", + "@aws-sdk/util-endpoints", + "@aws-sdk/util-user-agent-browser", + "@aws-sdk/util-user-agent-node", + "@smithy/config-resolver", + "@smithy/core", + "@smithy/fetch-http-handler", + "@smithy/hash-node", + "@smithy/invalid-dependency", + "@smithy/middleware-content-length", + "@smithy/middleware-endpoint", + "@smithy/middleware-retry", + "@smithy/middleware-serde", + "@smithy/middleware-stack", + "@smithy/node-config-provider", + "@smithy/node-http-handler", + "@smithy/protocol-http", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/url-parser", + "@smithy/util-base64", + "@smithy/util-body-length-browser", + "@smithy/util-body-length-node", + "@smithy/util-defaults-mode-browser", + "@smithy/util-defaults-mode-node", + "@smithy/util-endpoints", + "@smithy/util-middleware", + "@smithy/util-retry", + "@smithy/util-utf8", + "tslib", + "ci-info", + "@aws-crypto/supports-web-crypto", + "@aws-crypto/util", + "@aws-sdk/util-locate-window", + "@smithy/signature-v4", + "fast-xml-parser", + "@aws-sdk/credential-provider-env", + "@aws-sdk/credential-provider-http", + "@aws-sdk/credential-provider-ini", + "@aws-sdk/credential-provider-process", + "@aws-sdk/credential-provider-sso", + "@aws-sdk/credential-provider-web-identity", + "@smithy/credential-provider-imds", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/util-config-provider", + "bowser", + "@smithy/querystring-builder", + "@smithy/util-buffer-from", + "@smithy/service-error-classification", + "@smithy/abort-controller", + "@smithy/util-stream", + "@smithy/querystring-parser", + "@smithy/is-array-buffer", + "@smithy/util-hex-encoding", + "@smithy/util-uri-escape", + "strnum", + "@aws-sdk/token-providers", + "@aws-sdk/client-sso", + "semver" + ], + "dependencies": { + "@aws-amplify/ai-constructs": "^0.1.4", + "@aws-amplify/backend-output-schemas": "^1.0.0", + "@aws-amplify/backend-output-storage": "^1.0.0", + "@aws-amplify/graphql-auth-transformer": "4.1.1", + "@aws-amplify/graphql-conversation-transformer": "0.2.1", + "@aws-amplify/graphql-default-value-transformer": "3.0.3", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-function-transformer": "3.1.0", + "@aws-amplify/graphql-generation-transformer": "0.2.1", + "@aws-amplify/graphql-http-transformer": "3.0.3", + "@aws-amplify/graphql-index-transformer": "3.0.3", + "@aws-amplify/graphql-maps-to-transformer": "4.0.3", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-predictions-transformer": "3.0.3", + "@aws-amplify/graphql-relational-transformer": "3.0.3", + "@aws-amplify/graphql-searchable-transformer": "3.0.3", + "@aws-amplify/graphql-sql-transformer": "0.4.3", + "@aws-amplify/graphql-transformer": "2.1.1", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/platform-core": "^1.0.0", + "@aws-amplify/plugin-types": "^1.0.0", + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/client-bedrock-runtime": "^3.622.0", + "@aws-sdk/client-sso": "3.637.0", + "@aws-sdk/client-sso-oidc": "3.637.0", + "@aws-sdk/client-sts": "^3.624.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.637.0", + "@aws-sdk/credential-provider-node": "3.637.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.637.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/abort-controller": "^3.1.1", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/eventstream-codec": "^3.1.2", + "@smithy/eventstream-serde-browser": "^3.0.6", + "@smithy/eventstream-serde-config-resolver": "^3.0.3", + "@smithy/eventstream-serde-node": "^3.0.5", + "@smithy/eventstream-serde-universal": "^3.0.5", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/querystring-parser": "^3.0.3", + "@smithy/service-error-classification": "^3.0.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "bowser": "^2.11.0", + "charenc": "^0.0.2", + "ci-info": "^3.2.0", + "crypt": "^0.0.2", + "fast-xml-parser": "4.4.1", + "fs-extra": "^8.1.0", + "graceful-fs": "^4.2.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1", + "hjson": "^3.2.2", + "immer": "^9.0.12", + "is-buffer": "~1.1.6", + "is-ci": "^3.0.1", + "jsonfile": "^4.0.0", + "libphonenumber-js": "1.9.47", + "lodash": "^4.17.21", + "lodash.mergewith": "^4.6.2", + "md5": "^2.2.1", + "object-hash": "^3.0.0", + "pluralize": "8.0.0", + "semver": "^7.6.3", + "strnum": "^1.0.5", + "ts-dedent": "^2.0.0", + "tslib": "^2.6.2", + "universalify": "^0.1.0", + "uuid": "^9.0.1", + "zod": "^3.22.2" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/ai-constructs": { + "version": "0.1.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/plugin-types": "^1.0.1", + "@aws-sdk/client-bedrock-runtime": "^3.622.0", + "@smithy/types": "^3.3.0" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/backend-output-schemas": { + "version": "1.2.0", + "inBundle": true, + "license": "Apache-2.0", + "peerDependencies": { + "zod": "^3.22.2" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/backend-output-storage": { + "version": "1.1.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/backend-output-schemas": "^1.2.0", + "@aws-amplify/platform-core": "^1.0.6" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/backend-output-storage/node_modules/@aws-amplify/platform-core": { + "version": "1.0.7", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/plugin-types": "^1.2.1", + "@aws-sdk/client-sts": "^3.624.0", + "is-ci": "^3.0.1", + "lodash.mergewith": "^4.6.2", + "semver": "^7.6.3", + "uuid": "^9.0.1", + "zod": "^3.22.2" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-auth-transformer": { + "version": "4.1.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-relational-transformer": "3.0.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1", + "lodash": "^4.17.21", + "md5": "^2.3.0" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-conversation-transformer": { + "version": "0.2.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/ai-constructs": "^0.1.4", + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-index-transformer": "3.0.3", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-relational-transformer": "3.0.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1", + "immer": "^9.0.12" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-default-value-transformer": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1", + "libphonenumber-js": "1.9.47" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-directives": { + "version": "2.2.0", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-function-transformer": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-generation-transformer": { + "version": "0.2.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1", + "immer": "^9.0.12" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-http-transformer": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", "graphql": "^15.5.0", "graphql-mapping-template": "5.0.1", - "md5": "^2.2.1", - "pluralize": "8.0.0" + "graphql-transformer-common": "5.0.1" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-index-transformer": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-maps-to-transformer": { + "version": "4.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-model-transformer": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-predictions-transformer": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-relational-transformer": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-index-transformer": "3.0.3", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1", + "immer": "^9.0.12" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-searchable-transformer": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-sql-transformer": { + "version": "0.4.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-transformer": { + "version": "2.1.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-auth-transformer": "4.1.1", + "@aws-amplify/graphql-conversation-transformer": "0.2.1", + "@aws-amplify/graphql-default-value-transformer": "3.0.3", + "@aws-amplify/graphql-function-transformer": "3.1.0", + "@aws-amplify/graphql-generation-transformer": "0.2.1", + "@aws-amplify/graphql-http-transformer": "3.0.3", + "@aws-amplify/graphql-index-transformer": "3.0.3", + "@aws-amplify/graphql-maps-to-transformer": "4.0.3", + "@aws-amplify/graphql-model-transformer": "3.0.3", + "@aws-amplify/graphql-predictions-transformer": "3.0.3", + "@aws-amplify/graphql-relational-transformer": "3.0.3", + "@aws-amplify/graphql-searchable-transformer": "3.0.3", + "@aws-amplify/graphql-sql-transformer": "0.4.3", + "@aws-amplify/graphql-transformer-core": "3.1.1", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-transformer-core": { + "version": "3.1.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.2.0", + "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "fs-extra": "^8.1.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.0.1", + "hjson": "^3.2.2", + "lodash": "^4.17.21", + "md5": "^2.3.0", + "object-hash": "^3.0.0", + "ts-dedent": "^2.0.0" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-transformer-interfaces": { + "version": "4.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "graphql": "^15.5.0" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/platform-core": { + "version": "1.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/plugin-types": "^1.2.1", + "@aws-sdk/client-sts": "^3.624.0", + "is-ci": "^3.0.1", + "lodash.mergewith": "^4.6.2", + "semver": "^7.6.3", + "uuid": "^9.0.1", + "zod": "^3.22.2" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/plugin-types": { + "version": "1.2.1", + "inBundle": true, + "license": "Apache-2.0", + "peerDependencies": { + "@aws-sdk/types": "^3.609.0", + "aws-cdk-lib": "^2.152.0", + "constructs": "^10.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "inBundle": true, + "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-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime": { + "version": "3.642.0", + "inBundle": true, + "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.637.0", + "@aws-sdk/client-sts": "3.637.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.637.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/eventstream-serde-browser": "^3.0.6", + "@smithy/eventstream-serde-config-resolver": "^3.0.3", + "@smithy/eventstream-serde-node": "^3.0.5", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@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.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sso": { + "version": "3.637.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@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.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.637.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.637.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@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.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.637.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts": { + "version": "3.637.0", + "inBundle": true, + "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.637.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.637.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@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.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/core": { + "version": "3.635.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.4.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.620.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.635.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.637.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.637.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.637.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/hjson": { - "version": "3.2.2", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.637.0", "inBundle": true, - "license": "MIT", - "bin": { - "hjson": "bin/hjson" + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.637.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.637.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/immer": { - "version": "9.0.21", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.620.1", "inBundle": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/is-buffer": { - "version": "2.0.5", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.637.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.637.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.621.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.620.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/middleware-logger": { + "version": "3.609.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.620.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.637.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.614.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/token-providers": { + "version": "3.614.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.614.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/util-endpoints": { + "version": "3.637.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "@smithy/util-endpoints": "^2.0.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/util-locate-window": { + "version": "3.568.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.609.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.614.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true } - ], + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/abort-controller": { + "version": "3.1.1", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4" + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/config-resolver": { + "version": "3.0.5", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/jsonfile": { - "version": "6.1.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/core": { + "version": "2.4.0", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "universalify": "^2.0.0" + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/libphonenumber-js": { - "version": "1.9.47", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/credential-provider-imds": { + "version": "3.2.0", "inBundle": true, - "license": "MIT" + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@aws-amplify/data-construct/node_modules/lodash": { - "version": "4.17.21", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/eventstream-codec": { + "version": "3.1.2", "inBundle": true, - "license": "MIT" + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "tslib": "^2.6.2" + } }, - "node_modules/@aws-amplify/data-construct/node_modules/md5": { - "version": "2.3.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/eventstream-serde-browser": { + "version": "3.0.6", "inBundle": true, - "license": "BSD-3-Clause", + "license": "Apache-2.0", "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" + "@smithy/eventstream-serde-universal": "^3.0.5", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/md5/node_modules/is-buffer": { - "version": "1.1.6", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "3.0.3", "inBundle": true, - "license": "MIT" + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@aws-amplify/data-construct/node_modules/object-hash": { - "version": "3.0.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/eventstream-serde-node": { + "version": "3.0.5", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.5", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 6" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/pluralize": { - "version": "8.0.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/eventstream-serde-universal": { + "version": "3.0.5", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^3.1.2", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/ts-dedent": { - "version": "2.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6.10" + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@aws-amplify/data-construct/node_modules/universalify": { - "version": "2.0.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/hash-node": { + "version": "3.0.3", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/zod": { - "version": "3.22.4", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/invalid-dependency": { + "version": "3.0.3", "inBundle": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" } }, - "node_modules/@aws-amplify/data-schema": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema/-/data-schema-1.5.1.tgz", - "integrity": "sha512-hFDqqwHqdoFazmvGOApCX8kqrdoum9YJikmAQN5tP2sgnCT++lqznFw2F4PPqDJRxhQP1AYuwhbbRBvGLMbs/w==", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/data-schema-types": "*", - "@smithy/util-base64": "^3.0.0", - "@types/aws-lambda": "^8.10.134", - "@types/json-schema": "^7.0.15", - "rxjs": "^7.8.1" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-schema-types": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema-types/-/data-schema-types-1.1.1.tgz", - "integrity": "sha512-WhWEEsztpSSxIY0lJ3Ge5iA4g3PBm66SQmy1fBH1FBq0T+cxUBijifOU8MNwf+tf6lGpArMX0RS54HRVF5fUSA==", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/middleware-content-length": { + "version": "3.0.5", + "inBundle": true, "license": "Apache-2.0", "dependencies": { - "graphql": "15.8.0", - "rxjs": "^7.8.1" + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-schema-types/node_modules/graphql": { - "version": "15.8.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz", - "integrity": "sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==", - "license": "MIT", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/middleware-endpoint": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 10.x" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/datastore": { - "version": "5.0.49", - "resolved": "https://registry.npmjs.org/@aws-amplify/datastore/-/datastore-5.0.49.tgz", - "integrity": "sha512-7ESzc1/5rOth3gPi65IJyx4dQbzRTvRxpefcA132C3vAm5BFqKEcDXyZX5sHbSf5OOMmQDsa68GfzhAM10rWPg==", - "dev": true, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/middleware-retry": { + "version": "3.0.15", + "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/api": "6.0.49", - "buffer": "4.9.2", - "idb": "5.0.6", - "immer": "9.0.6", - "rxjs": "^7.8.1", - "ulid": "^2.3.0" + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/service-error-classification": "^3.0.3", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "tslib": "^2.6.2", + "uuid": "^9.0.1" }, - "peerDependencies": { - "@aws-amplify/core": "^6.1.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/deployed-backend-client": { - "resolved": "packages/deployed-backend-client", - "link": true - }, - "node_modules/@aws-amplify/form-generator": { - "resolved": "packages/form-generator", - "link": true + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/middleware-serde": { + "version": "3.0.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@aws-amplify/graphql-api-construct": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@aws-amplify/graphql-api-construct/-/graphql-api-construct-1.12.0.tgz", - "integrity": "sha512-h/FWriGl39zise+EofwHh2FmHmmEINefN/DEdXgQLT0/Cw5WUKCMKaIS/30Nk27N8V0anFWBVVbVUDMOjARLTQ==", - "bundleDependencies": [ - "@aws-amplify/backend-output-schemas", - "@aws-amplify/backend-output-storage", - "@aws-amplify/graphql-auth-transformer", - "@aws-amplify/graphql-conversation-transformer", - "@aws-amplify/graphql-default-value-transformer", - "@aws-amplify/graphql-directives", - "@aws-amplify/graphql-function-transformer", - "@aws-amplify/graphql-generation-transformer", - "@aws-amplify/graphql-http-transformer", - "@aws-amplify/graphql-index-transformer", - "@aws-amplify/graphql-maps-to-transformer", - "@aws-amplify/graphql-model-transformer", - "@aws-amplify/graphql-predictions-transformer", - "@aws-amplify/graphql-relational-transformer", - "@aws-amplify/graphql-searchable-transformer", - "@aws-amplify/graphql-sql-transformer", - "@aws-amplify/graphql-transformer", - "@aws-amplify/graphql-transformer-core", - "@aws-amplify/graphql-transformer-interfaces", - "@aws-amplify/platform-core", - "@aws-amplify/plugin-types", - "@aws-amplify/ai-constructs", - "charenc", - "crypt", - "fs-extra", - "graceful-fs", - "graphql", - "graphql-mapping-template", - "graphql-transformer-common", - "hjson", - "immer", - "is-buffer", - "jsonfile", - "libphonenumber-js", - "lodash", - "md5", - "object-hash", - "pluralize", - "ts-dedent", - "universalify", - "zod" - ], + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/middleware-stack": { + "version": "3.0.3", + "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.1.2", - "@aws-amplify/backend-output-schemas": "^0.4.0", - "@aws-amplify/backend-output-storage": "^0.2.2", - "@aws-amplify/graphql-auth-transformer": "4.1.0", - "@aws-amplify/graphql-conversation-transformer": "0.2.0", - "@aws-amplify/graphql-default-value-transformer": "3.0.2", - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-function-transformer": "3.0.2", - "@aws-amplify/graphql-generation-transformer": "0.2.0", - "@aws-amplify/graphql-http-transformer": "3.0.2", - "@aws-amplify/graphql-index-transformer": "3.0.2", - "@aws-amplify/graphql-maps-to-transformer": "4.0.2", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-predictions-transformer": "3.0.2", - "@aws-amplify/graphql-relational-transformer": "3.0.2", - "@aws-amplify/graphql-searchable-transformer": "3.0.2", - "@aws-amplify/graphql-sql-transformer": "0.4.2", - "@aws-amplify/graphql-transformer": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "@aws-amplify/platform-core": "^0.2.0", - "@aws-amplify/plugin-types": "^0.4.1", - "charenc": "^0.0.2", - "crypt": "^0.0.2", - "fs-extra": "^8.1.0", - "graceful-fs": "^4.2.11", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "hjson": "^3.2.2", - "immer": "^9.0.12", - "is-buffer": "^2.0.5", - "jsonfile": "^6.1.0", - "libphonenumber-js": "1.9.47", - "lodash": "^4.17.21", - "md5": "^2.3.0", - "object-hash": "^3.0.0", - "pluralize": "^8.0.0", - "ts-dedent": "^2.0.0", - "universalify": "^2.0.0", - "zod": "^3.22.3" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/ai-constructs": { - "version": "0.1.2", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/node-config-provider": { + "version": "3.1.4", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.0.1", - "@aws-sdk/client-bedrock-runtime": "^3.622.0", - "@smithy/types": "^3.3.0" + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.0.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/ai-constructs/node_modules/@aws-amplify/plugin-types": { - "version": "1.2.1", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/node-http-handler": { + "version": "3.1.4", "inBundle": true, "license": "Apache-2.0", - "peerDependencies": { - "@aws-sdk/types": "^3.609.0", - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.0.0" + "dependencies": { + "@smithy/abort-controller": "^3.1.1", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/backend-output-schemas": { - "version": "0.4.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/property-provider": { + "version": "3.1.3", "inBundle": true, "license": "Apache-2.0", - "peerDependencies": { - "zod": "^3.21.4" + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/backend-output-storage": { - "version": "0.2.2", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/protocol-http": { + "version": "4.1.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^0.4.0", - "@aws-amplify/platform-core": "^0.2.0" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.103.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-auth-transformer": { - "version": "4.1.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/querystring-builder": { + "version": "3.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-relational-transformer": "3.0.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "lodash": "^4.17.21", - "md5": "^2.3.0" + "@smithy/types": "^3.3.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-conversation-transformer": { - "version": "0.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/querystring-parser": { + "version": "3.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.1.2", - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-index-transformer": "3.0.2", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-relational-transformer": "3.0.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "immer": "^9.0.12" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-default-value-transformer": { - "version": "3.0.2", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/service-error-classification": { + "version": "3.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "libphonenumber-js": "1.9.47" + "@smithy/types": "^3.3.0" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-directives": { - "version": "2.1.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.4", "inBundle": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-function-transformer": { - "version": "3.0.2", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/signature-v4": { + "version": "4.1.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-generation-transformer": { - "version": "0.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/smithy-client": { + "version": "3.2.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "immer": "^9.0.12" + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-http-transformer": { - "version": "3.0.2", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/types": { + "version": "3.3.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-index-transformer": { - "version": "3.0.2", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/url-parser": { + "version": "3.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "@smithy/querystring-parser": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-base64": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-maps-to-transformer": { - "version": "4.0.2", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-model-transformer": { - "version": "3.0.2", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-predictions-transformer": { - "version": "3.0.2", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-config-provider": { + "version": "3.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.15", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-relational-transformer": { - "version": "3.0.2", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.15", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-index-transformer": "3.0.2", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "immer": "^9.0.12" + "@smithy/config-resolver": "^3.0.5", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-searchable-transformer": { - "version": "3.0.2", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-endpoints": { + "version": "2.0.5", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-sql-transformer": { - "version": "0.4.2", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-transformer": { - "version": "2.1.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-middleware": { + "version": "3.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-auth-transformer": "4.1.0", - "@aws-amplify/graphql-conversation-transformer": "0.2.0", - "@aws-amplify/graphql-default-value-transformer": "3.0.2", - "@aws-amplify/graphql-function-transformer": "3.0.2", - "@aws-amplify/graphql-generation-transformer": "0.2.0", - "@aws-amplify/graphql-http-transformer": "3.0.2", - "@aws-amplify/graphql-index-transformer": "3.0.2", - "@aws-amplify/graphql-maps-to-transformer": "4.0.2", - "@aws-amplify/graphql-model-transformer": "3.0.2", - "@aws-amplify/graphql-predictions-transformer": "3.0.2", - "@aws-amplify/graphql-relational-transformer": "3.0.2", - "@aws-amplify/graphql-searchable-transformer": "3.0.2", - "@aws-amplify/graphql-sql-transformer": "0.4.2", - "@aws-amplify/graphql-transformer-core": "3.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-transformer-core": { - "version": "3.1.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-retry": { + "version": "3.0.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.1.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "fs-extra": "^8.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "hjson": "^3.2.2", - "lodash": "^4.17.21", - "md5": "^2.3.0", - "object-hash": "^3.0.0", - "ts-dedent": "^2.0.0" + "@smithy/service-error-classification": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-transformer-interfaces": { - "version": "4.1.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-stream": { + "version": "3.1.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "graphql": "^15.5.0" + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/types": "^3.3.0", + "@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" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/platform-core": { - "version": "0.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^0.4.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/plugin-types": { - "version": "0.4.1", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-utf8": { + "version": "3.0.0", "inBundle": true, "license": "Apache-2.0", - "peerDependencies": { - "aws-cdk-lib": "^2.103.0", - "constructs": "^10.0.0" + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/bowser": { + "version": "2.11.0", + "inBundle": true, + "license": "MIT" + }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/charenc": { "version": "0.0.2", "inBundle": true, @@ -1865,6 +4696,20 @@ "node": "*" } }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/ci-info": { + "version": "3.9.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/crypt": { "version": "0.0.2", "inBundle": true, @@ -1873,6 +4718,27 @@ "node": "*" } }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/fast-xml-parser": { + "version": "4.4.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/fs-extra": { "version": "8.1.0", "inBundle": true, @@ -1886,29 +4752,13 @@ "node": ">=6 <7 || >=8" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/fs-extra/node_modules/jsonfile": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/fs-extra/node_modules/universalify": { - "version": "0.1.2", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/graceful-fs": { "version": "4.2.11", "inBundle": true, "license": "ISC" }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/graphql": { - "version": "15.8.0", + "version": "15.9.0", "inBundle": true, "license": "MIT", "engines": { @@ -1949,34 +4799,25 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/is-buffer": { - "version": "2.0.5", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "version": "1.1.6", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/is-ci": { + "version": "3.0.1", "inBundle": true, "license": "MIT", - "engines": { - "node": ">=4" + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/jsonfile": { - "version": "6.1.0", + "version": "4.0.0", "inBundle": true, "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -1991,6 +4832,11 @@ "inBundle": true, "license": "MIT" }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/lodash.mergewith": { + "version": "4.6.2", + "inBundle": true, + "license": "MIT" + }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/md5": { "version": "2.3.0", "inBundle": true, @@ -2001,11 +4847,6 @@ "is-buffer": "~1.1.6" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/md5/node_modules/is-buffer": { - "version": "1.1.6", - "inBundle": true, - "license": "MIT" - }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/object-hash": { "version": "3.0.0", "inBundle": true, @@ -2022,6 +4863,22 @@ "node": ">=4" } }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/semver": { + "version": "7.6.3", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/strnum": { + "version": "1.0.5", + "inBundle": true, + "license": "MIT" + }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/ts-dedent": { "version": "2.2.0", "inBundle": true, @@ -2030,16 +4887,33 @@ "node": ">=6.10" } }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/tslib": { + "version": "2.7.0", + "inBundle": true, + "license": "0BSD" + }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/universalify": { - "version": "2.0.0", + "version": "0.1.2", "inBundle": true, "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": ">= 4.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "inBundle": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/zod": { - "version": "3.22.4", + "version": "3.23.8", "inBundle": true, "license": "MIT", "funding": { @@ -23026,6 +25900,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -23038,6 +25913,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -27270,7 +30146,6 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, "license": "MIT", "bin": { "uuid": "dist/bin/uuid" @@ -27903,6 +30778,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "dev": true, "license": "ISC", "engines": { "node": ">= 14" @@ -28039,6 +30915,7 @@ } }, "packages/ai-constructs": { + "name": "@aws-amplify/ai-constructs", "version": "0.1.4", "license": "Apache-2.0", "dependencies": { @@ -28056,6 +30933,7 @@ "license": "Apache-2.0" }, "packages/auth-construct": { + "name": "@aws-amplify/auth-construct", "version": "1.3.0", "license": "Apache-2.0", "dependencies": { @@ -28070,6 +30948,7 @@ } }, "packages/backend": { + "name": "@aws-amplify/backend", "version": "1.2.1", "license": "Apache-2.0", "dependencies": { @@ -28098,6 +30977,7 @@ } }, "packages/backend-ai": { + "name": "@aws-amplify/backend-ai", "version": "0.1.1", "license": "Apache-2.0", "dependencies": { @@ -28114,6 +30994,7 @@ } }, "packages/backend-auth": { + "name": "@aws-amplify/backend-auth", "version": "1.1.4", "license": "Apache-2.0", "dependencies": { @@ -28131,12 +31012,13 @@ } }, "packages/backend-data": { + "name": "@aws-amplify/backend-data", "version": "1.1.3", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", "@aws-amplify/backend-output-storage": "^1.1.1", - "@aws-amplify/data-construct": "^1.9.6", + "@aws-amplify/data-construct": "^1.10.1", "@aws-amplify/data-schema-types": "^1.1.1", "@aws-amplify/plugin-types": "^1.2.1" }, @@ -28151,6 +31033,7 @@ } }, "packages/backend-deployer": { + "name": "@aws-amplify/backend-deployer", "version": "1.1.2", "license": "Apache-2.0", "dependencies": { @@ -28165,6 +31048,7 @@ } }, "packages/backend-function": { + "name": "@aws-amplify/backend-function", "version": "1.4.0", "license": "Apache-2.0", "dependencies": { @@ -28200,6 +31084,7 @@ } }, "packages/backend-output-schemas": { + "name": "@aws-amplify/backend-output-schemas", "version": "1.2.0", "license": "Apache-2.0", "devDependencies": { @@ -28210,25 +31095,30 @@ } }, "packages/backend-output-storage": { + "name": "@aws-amplify/backend-output-storage", "version": "1.1.1", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/platform-core": "^1.0.6" + "@aws-amplify/platform-core": "^1.0.6", + "@aws-amplify/plugin-types": "^1.2.1" }, "peerDependencies": { "aws-cdk-lib": "^2.152.0" } }, "packages/backend-platform-test-stubs": { + "name": "@aws-amplify/backend-platform-test-stubs", "version": "0.3.4", "license": "Apache-2.0", "dependencies": { + "@aws-amplify/plugin-types": "^1.2.1", "aws-cdk-lib": "^2.152.0", "constructs": "^10.0.0" } }, "packages/backend-secret": { + "name": "@aws-amplify/backend-secret", "version": "1.1.1", "license": "Apache-2.0", "dependencies": { @@ -28241,6 +31131,7 @@ } }, "packages/backend-storage": { + "name": "@aws-amplify/backend-storage", "version": "1.1.2", "license": "Apache-2.0", "dependencies": { @@ -28258,6 +31149,7 @@ } }, "packages/cli": { + "name": "@aws-amplify/backend-cli", "version": "1.2.6", "license": "Apache-2.0", "dependencies": { @@ -28270,6 +31162,7 @@ "@aws-amplify/form-generator": "^1.0.1", "@aws-amplify/model-generator": "^1.0.5", "@aws-amplify/platform-core": "^1.0.5", + "@aws-amplify/plugin-types": "^1.2.1", "@aws-amplify/sandbox": "^1.2.0", "@aws-amplify/schema-generator": "^1.2.1", "@aws-sdk/client-amplify": "^3.624.0", @@ -28303,6 +31196,7 @@ } }, "packages/cli-core": { + "name": "@aws-amplify/cli-core", "version": "1.1.2", "license": "Apache-2.0", "dependencies": { @@ -28409,6 +31303,7 @@ } }, "packages/client-config": { + "name": "@aws-amplify/client-config", "version": "1.3.0", "license": "Apache-2.0", "dependencies": { @@ -28416,6 +31311,7 @@ "@aws-amplify/deployed-backend-client": "^1.4.0", "@aws-amplify/model-generator": "^1.0.5", "@aws-amplify/platform-core": "^1.0.7", + "@aws-amplify/plugin-types": "^1.2.1", "zod": "^3.22.2" }, "devDependencies": { @@ -28544,11 +31440,13 @@ } }, "packages/deployed-backend-client": { + "name": "@aws-amplify/deployed-backend-client", "version": "1.4.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", "@aws-amplify/platform-core": "^1.0.5", + "@aws-amplify/plugin-types": "^1.2.1", "zod": "^3.22.2" }, "peerDependencies": { @@ -28559,6 +31457,7 @@ } }, "packages/eslint-rules": { + "name": "eslint-plugin-amplify-backend-rules", "version": "0.0.1", "license": "Apache-2.0", "dependencies": { @@ -28569,6 +31468,7 @@ } }, "packages/form-generator": { + "name": "@aws-amplify/form-generator", "version": "1.0.1", "license": "Apache-2.0", "dependencies": { @@ -28588,6 +31488,7 @@ } }, "packages/integration-tests": { + "name": "@aws-amplify/integration-tests", "version": "0.5.8", "license": "Apache-2.0", "devDependencies": { @@ -28601,6 +31502,7 @@ "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/deployed-backend-client": "^1.3.0", "@aws-amplify/platform-core": "^1.1.0", + "@aws-amplify/plugin-types": "^1.2.1", "@aws-sdk/client-accessanalyzer": "^3.624.0", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-bedrock-runtime": "^3.622.0", @@ -28645,6 +31547,7 @@ } }, "packages/model-generator": { + "name": "@aws-amplify/model-generator", "version": "1.0.6", "license": "Apache-2.0", "dependencies": { @@ -28653,6 +31556,7 @@ "@aws-amplify/graphql-generator": "^0.4.0", "@aws-amplify/graphql-types-generator": "^3.6.0", "@aws-amplify/platform-core": "^1.0.5", + "@aws-amplify/plugin-types": "^1.2.1", "@aws-sdk/client-appsync": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/credential-providers": "^3.624.0", @@ -28665,6 +31569,7 @@ } }, "packages/platform-core": { + "name": "@aws-amplify/platform-core", "version": "1.1.0", "license": "Apache-2.0", "dependencies": { @@ -28696,6 +31601,7 @@ } }, "packages/plugin-types": { + "name": "@aws-amplify/plugin-types", "version": "1.2.1", "license": "Apache-2.0", "devDependencies": { @@ -28824,6 +31730,7 @@ } }, "packages/sandbox": { + "name": "@aws-amplify/sandbox", "version": "1.2.1", "license": "Apache-2.0", "dependencies": { @@ -28833,6 +31740,7 @@ "@aws-amplify/client-config": "^1.1.3", "@aws-amplify/deployed-backend-client": "^1.3.0", "@aws-amplify/platform-core": "^1.0.6", + "@aws-amplify/plugin-types": "^1.2.1", "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", @@ -28855,6 +31763,7 @@ } }, "packages/schema-generator": { + "name": "@aws-amplify/schema-generator", "version": "1.2.2", "license": "Apache-2.0", "dependencies": { diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index 8d993abce73..a4cdf8912d3 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -30,7 +30,7 @@ "dependencies": { "@aws-amplify/backend-output-storage": "^1.1.1", "@aws-amplify/backend-output-schemas": "^1.1.0", - "@aws-amplify/data-construct": "^1.9.6", + "@aws-amplify/data-construct": "^1.10.1", "@aws-amplify/plugin-types": "^1.2.1", "@aws-amplify/data-schema-types": "^1.1.1" } diff --git a/packages/backend-data/src/factory.test.ts b/packages/backend-data/src/factory.test.ts index 526409bc898..c4cc45b9adb 100644 --- a/packages/backend-data/src/factory.test.ts +++ b/packages/backend-data/src/factory.test.ts @@ -535,25 +535,8 @@ void describe('DataFactory', () => { 'Fn::Join': [ '', [ - 'arn:', { - Ref: 'AWS::Partition', - }, - ':appsync:', - { - Ref: 'AWS::Region', - }, - ':', - { - Ref: 'AWS::AccountId', - }, - // eslint-disable-next-line spellcheck/spell-checker - ':apis/', - { - 'Fn::GetAtt': [ - 'amplifyDataGraphQLAPI42A6FA33', - 'ApiId', - ], + 'Fn::GetAtt': ['amplifyDataGraphQLAPI42A6FA33', 'Arn'], }, '/types/Query/*', ], @@ -563,25 +546,8 @@ void describe('DataFactory', () => { 'Fn::Join': [ '', [ - 'arn:', - { - Ref: 'AWS::Partition', - }, - ':appsync:', { - Ref: 'AWS::Region', - }, - ':', - { - Ref: 'AWS::AccountId', - }, - // eslint-disable-next-line spellcheck/spell-checker - ':apis/', - { - 'Fn::GetAtt': [ - 'amplifyDataGraphQLAPI42A6FA33', - 'ApiId', - ], + 'Fn::GetAtt': ['amplifyDataGraphQLAPI42A6FA33', 'Arn'], }, '/types/Mutation/*', ], @@ -591,25 +557,8 @@ void describe('DataFactory', () => { 'Fn::Join': [ '', [ - 'arn:', - { - Ref: 'AWS::Partition', - }, - ':appsync:', - { - Ref: 'AWS::Region', - }, - ':', - { - Ref: 'AWS::AccountId', - }, - // eslint-disable-next-line spellcheck/spell-checker - ':apis/', { - 'Fn::GetAtt': [ - 'amplifyDataGraphQLAPI42A6FA33', - 'ApiId', - ], + 'Fn::GetAtt': ['amplifyDataGraphQLAPI42A6FA33', 'Arn'], }, '/types/Subscription/*', ], @@ -717,22 +666,8 @@ void describe('DataFactory', () => { 'Fn::Join': [ '', [ - 'arn:', - { - Ref: 'AWS::Partition', - }, - ':appsync:', - { - Ref: 'AWS::Region', - }, - ':', { - Ref: 'AWS::AccountId', - }, - // eslint-disable-next-line spellcheck/spell-checker - ':apis/', - { - 'Fn::GetAtt': ['amplifyDataGraphQLAPI42A6FA33', 'ApiId'], + 'Fn::GetAtt': ['amplifyDataGraphQLAPI42A6FA33', 'Arn'], }, '/types/Mutation/*', ], @@ -757,22 +692,8 @@ void describe('DataFactory', () => { 'Fn::Join': [ '', [ - 'arn:', - { - Ref: 'AWS::Partition', - }, - ':appsync:', - { - Ref: 'AWS::Region', - }, - ':', - { - Ref: 'AWS::AccountId', - }, - // eslint-disable-next-line spellcheck/spell-checker - ':apis/', { - 'Fn::GetAtt': ['amplifyDataGraphQLAPI42A6FA33', 'ApiId'], + 'Fn::GetAtt': ['amplifyDataGraphQLAPI42A6FA33', 'Arn'], }, '/types/Query/*', ], From 5af7781d27f90518432d8140d3edae8a9136ade5 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Tue, 17 Sep 2024 22:05:57 +0200 Subject: [PATCH 002/199] add new e2e test for validating backwards compatibility of amplify outputs (#2009) * add new e2e test for validating backwards compatibility of amplify outputs * add changeset * try this * try this * try this * try this * try this * PR Updates --- .changeset/cool-bulldogs-accept.md | 2 + .../actions/setup_baseline_version/action.yml | 52 +++++ .github/workflows/health_checks.yml | 76 +++---- ...fy_outputs_backwards_compatibility.test.ts | 212 ++++++++++++++++++ 4 files changed, 302 insertions(+), 40 deletions(-) create mode 100644 .changeset/cool-bulldogs-accept.md create mode 100644 .github/actions/setup_baseline_version/action.yml create mode 100644 packages/integration-tests/src/test-e2e/amplify_outputs_backwards_compatibility.test.ts diff --git a/.changeset/cool-bulldogs-accept.md b/.changeset/cool-bulldogs-accept.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/cool-bulldogs-accept.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/actions/setup_baseline_version/action.yml b/.github/actions/setup_baseline_version/action.yml new file mode 100644 index 00000000000..1594ab6dde6 --- /dev/null +++ b/.github/actions/setup_baseline_version/action.yml @@ -0,0 +1,52 @@ +name: setup_baseline_version +description: Set up a baseline or "previous" version of the library for testing. Mostly useful for backwards compatibility +outputs: + baseline_dir: + description: 'Path where baseline project directory is setup' + value: ${{ steps.move_baseline_version.outputs.baseline_dir }} +runs: + using: composite + steps: + - name: Get baseline commit sha + id: get_baseline_commit_sha + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + if [[ ${{ github.event_name }} == 'push' ]]; then + # The SHA of the most recent commit on ref before the push. + baseline_commit_sha="${{ github.event.before }}" + elif [[ ${{ github.event_name }} == 'pull_request' ]]; then + # The SHA of the HEAD commit on base branch. + baseline_commit_sha="${{ github.event.pull_request.base.sha }}" + elif [[ ${{ github.event_name }} == 'schedule' ]] || [[ ${{ github.event_name }} == 'workflow_dispatch' ]]; then + # The SHA of the parent of HEAD commit on main branch. + # This assumes linear history of main branch, i.e. one parent. + # These events have only information about HEAD commit, hence the need for lookup. + baseline_commit_sha=$(gh api /repos/${{ github.repository }}/commits/${{ github.sha }} | jq -r '.parents[0].sha') + else + echo Unable to determine baseline commit sha; + exit 1; + fi + echo baseline commit sha is $baseline_commit_sha; + echo "baseline_commit_sha=$baseline_commit_sha" >> "$GITHUB_OUTPUT"; + - name: Checkout baseline version + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 + with: + ref: ${{ steps.get_baseline_commit_sha.outputs.baseline_commit_sha }} + - uses: ./.github/actions/setup_node + - name: Install and build baseline version + shell: bash + run: | + npm ci + npm run build + - name: Move baseline version + id: move_baseline_version + shell: bash + run: | + BASELINE_DIR=$(mktemp -d) + # Command below makes shell include .hidden files in file system commands (i.e. mv). + # This is to make sure that .git directory is moved with the repo content. + shopt -s dotglob + mv ./* $BASELINE_DIR + echo "baseline_dir=$BASELINE_DIR" >> "$GITHUB_OUTPUT"; diff --git a/.github/workflows/health_checks.yml b/.github/workflows/health_checks.yml index c950c12f453..36dd88d9346 100644 --- a/.github/workflows/health_checks.yml +++ b/.github/workflows/health_checks.yml @@ -160,46 +160,13 @@ jobs: id-token: write contents: read steps: - - name: Get baseline commit sha - id: get_baseline_commit_sha - env: - GH_TOKEN: ${{ github.token }} - run: | - if [[ ${{ github.event_name }} == 'push' ]]; then - # The SHA of the most recent commit on ref before the push. - baseline_commit_sha="${{ github.event.before }}" - elif [[ ${{ github.event_name }} == 'pull_request' ]]; then - # The SHA of the HEAD commit on base branch. - baseline_commit_sha="${{ github.event.pull_request.base.sha }}" - elif [[ ${{ github.event_name }} == 'schedule' ]] || [[ ${{ github.event_name }} == 'workflow_dispatch' ]]; then - # The SHA of the parent of HEAD commit on main branch. - # This assumes linear history of main branch, i.e. one parent. - # These events have only information about HEAD commit, hence the need for lookup. - baseline_commit_sha=$(gh api /repos/${{ github.repository }}/commits/${{ github.sha }} | jq -r '.parents[0].sha') - else - echo Unable to determine baseline commit sha; - exit 1; - fi - echo baseline commit sha is $baseline_commit_sha; - echo "baseline_commit_sha=$baseline_commit_sha" >> "$GITHUB_OUTPUT"; - - name: Checkout baseline version + # This checkout is needed for the setup_baseline_version action to run `checkout` inside + # See https://github.com/actions/checkout/issues/692 + - name: Checkout version for baseline uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - with: - ref: ${{ steps.get_baseline_commit_sha.outputs.baseline_commit_sha }} - - uses: ./.github/actions/setup_node - - name: Install and build baseline version - run: | - npm ci - npm run build - - name: Move baseline version - id: move_baseline_version - run: | - BASELINE_DIR=$(mktemp -d) - # Command below makes shell include .hidden files in file system commands (i.e. mv). - # This is to make sure that .git directory is moved with the repo content. - shopt -s dotglob - mv ./* $BASELINE_DIR - echo "baseline_dir=$BASELINE_DIR" >> "$GITHUB_OUTPUT"; + - name: Setup baseline version + uses: ./.github/actions/setup_baseline_version + id: setup_baseline_version - name: Checkout current version uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - name: Run e2e iam access drift test @@ -209,7 +176,36 @@ jobs: node_version: ${{ matrix.node-version }} run: npm run test:dir packages/integration-tests/lib/test-e2e/iam_access_drift.test.js env: - BASELINE_DIR: ${{ steps.move_baseline_version.outputs.baseline_dir }} + BASELINE_DIR: ${{ steps.setup_baseline_version.outputs.baseline_dir }} + e2e_amplify_outputs_backwards_compatibility: + if: needs.do_include_e2e.outputs.run_e2e == 'true' + runs-on: ubuntu-latest + timeout-minutes: 25 + needs: + - do_include_e2e + - build + permissions: + # these permissions are required for the configure-aws-credentials action to get a JWT from GitHub + id-token: write + contents: read + steps: + # This checkout is needed for the setup_baseline_version action to run `checkout` inside + # See https://github.com/actions/checkout/issues/692 + - name: Checkout version for baseline + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 + - name: Setup baseline version + uses: ./.github/actions/setup_baseline_version + id: setup_baseline_version + - name: Checkout current version + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 + - name: Run e2e amplify outputs backwards compatibility test + uses: ./.github/actions/run_with_e2e_account + with: + e2e_test_accounts: ${{ vars.E2E_TEST_ACCOUNTS }} + node_version: ${{ matrix.node-version }} + run: npm run test:dir packages/integration-tests/lib/test-e2e/amplify_outputs_backwards_compatibility.test.js + env: + BASELINE_DIR: ${{ steps.setup_baseline_version.outputs.baseline_dir }} e2e_deployment: if: needs.do_include_e2e.outputs.run_e2e == 'true' strategy: diff --git a/packages/integration-tests/src/test-e2e/amplify_outputs_backwards_compatibility.test.ts b/packages/integration-tests/src/test-e2e/amplify_outputs_backwards_compatibility.test.ts new file mode 100644 index 00000000000..bdbedc29887 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/amplify_outputs_backwards_compatibility.test.ts @@ -0,0 +1,212 @@ +import { after, before, describe, it } from 'node:test'; +import { execa } from 'execa'; +import path from 'path'; +import { TestBranch, amplifyAppPool } from '../amplify_app_pool.js'; +import { BackendIdentifier } from '@aws-amplify/plugin-types'; +import { + CloudFormationClient, + DeleteStackCommand, +} from '@aws-sdk/client-cloudformation'; +import fsp from 'fs/promises'; +import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; +import { NpmProxyController } from '../npm_proxy_controller.js'; +import assert from 'assert'; +import os from 'os'; +import { generateClientConfig } from '@aws-amplify/client-config'; +import { BackendIdentifierConversions } from '@aws-amplify/platform-core'; +import { amplifyAtTag } from '../constants.js'; + +void describe('client config backwards compatibility', () => { + let branchBackendIdentifier: BackendIdentifier; + let testBranch: TestBranch; + let cfnClient: CloudFormationClient; + let tempDir: string; + let baselineDir: string; + let baselineNpmProxyController: NpmProxyController; + let currentNpmProxyController: NpmProxyController; + + before(async () => { + assert.ok( + process.env.BASELINE_DIR, + 'BASELINE_DIR environment variable must be set and point to amplify-backend repo at baseline version' + ); + baselineDir = process.env.BASELINE_DIR; + + tempDir = await fsp.mkdtemp( + path.join(os.tmpdir(), 'test-amplify-outputs-backwards-compatibility') + ); + + console.log(`Temp dir is ${tempDir}`); + + cfnClient = new CloudFormationClient(e2eToolingClientConfig); + baselineNpmProxyController = new NpmProxyController(baselineDir); + currentNpmProxyController = new NpmProxyController(); + testBranch = await amplifyAppPool.createTestBranch(); + branchBackendIdentifier = { + namespace: testBranch.appId, + name: testBranch.branchName, + type: 'branch', + }; + }); + + after(async () => { + await cfnClient.send( + new DeleteStackCommand({ + StackName: BackendIdentifierConversions.toStackName( + branchBackendIdentifier + ), + }) + ); + await fsp.rm(tempDir, { recursive: true }); + + await baselineNpmProxyController.tearDown(); + await currentNpmProxyController.tearDown(); + }); + + const deploy = async (): Promise => { + await execa( + 'npx', + [ + 'ampx', + 'pipeline-deploy', + '--branch', + branchBackendIdentifier.name, + '--appId', + branchBackendIdentifier.namespace, + ], + { + cwd: tempDir, + stdio: 'inherit', + env: { + CI: 'true', + }, + } + ); + }; + + const reinstallDependencies = async (): Promise => { + await fsp.rm(path.join(tempDir, 'node_modules'), { + recursive: true, + force: true, + }); + await fsp.unlink(path.join(tempDir, 'package-lock.json')); + + await execa('npm', ['install'], { + cwd: tempDir, + stdio: 'inherit', + }); + }; + + const assertGenerateClientConfigAPI = async ( + type: 'baseline' | 'current' + ) => { + try { + assert.ok( + await generateClientConfig(branchBackendIdentifier, '1'), + `outputs v1 failed to be generated for an app created with ${type} library version` + ); + } catch (e) { + throw new Error( + `outputs v1 failed to be generated for an app created with ${type} library version. Error: ${JSON.stringify( + e + )}` + ); + } + try { + assert.ok( + await generateClientConfig(branchBackendIdentifier, '1.1'), + `outputs v1.1 failed to be generated for an app created with ${type} library version` + ); + } catch (e) { + throw new Error( + `outputs v1.1 failed to be generated for an app created with ${type} library version. Error: ${JSON.stringify( + e + )}` + ); + } + }; + + const assertGenerateClientConfigCommand = async ( + type: 'baseline' | 'current' + ) => { + await execa( + 'npx', + [ + 'ampx', + 'generate', + 'outputs', + '--stack', + BackendIdentifierConversions.toStackName(branchBackendIdentifier), + ], + { + cwd: tempDir, + stdio: 'inherit', + } + ); + + const fileSize = ( + await fsp.stat(path.join(tempDir, 'amplify_outputs.json')) + ).size; + assert.ok( + fileSize > 100, // Validate that it's not just a shim + `outputs file should not be empty when generating for a ${ + type === 'baseline' ? 'new' : 'old' + } new app with the ${type} version` + ); + }; + + void it('outputs generation should be backwards and forward compatible', async () => { + // build an app using previous (baseline) version + await baselineNpmProxyController.setUp(); + await execa('npm', ['create', amplifyAtTag, '--yes'], { + cwd: tempDir, + stdio: 'inherit', + }); + + // Replace backend.ts to add custom outputs without version as well. + await fsp.writeFile( + path.join(tempDir, 'amplify', 'backend.ts'), + `import { defineBackend } from '@aws-amplify/backend'; +import { auth } from './auth/resource'; +import { data } from './data/resource'; + +const backend = defineBackend({ + auth, + data, +}); + +backend.addOutput({ + custom: { + someCustomOutput: 'someCustomOutputValue', + }, +}); +` + ); + await deploy(); + await baselineNpmProxyController.tearDown(); + + // Generate the outputs using the current version for apps built with baseline version + + // 1. via CLI command + await currentNpmProxyController.setUp(); + await reinstallDependencies(); + await assertGenerateClientConfigCommand('current'); + + // 2. via API. + await assertGenerateClientConfigAPI('current'); + + // Re-deploy the app using the current version now + await deploy(); + + // Generate the outputs using the baseline version for apps built with current version + + // 1. via CLI command + await currentNpmProxyController.tearDown(); + await baselineNpmProxyController.setUp(); + await reinstallDependencies(); + await assertGenerateClientConfigCommand('baseline'); + + // 2. via API. + await assertGenerateClientConfigAPI('baseline'); + }); +}); From cbac1051df636a7848a57e17cfda7cea62907ea7 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 18 Sep 2024 06:44:36 -0700 Subject: [PATCH 003/199] Handle CDK version mismatch (#2018) --- .changeset/spicy-monkeys-guess.md | 5 +++++ .eslint_dictionary.json | 1 + .../backend-deployer/src/cdk_error_mapper.test.ts | 9 +++++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 11 +++++++++++ 4 files changed, 26 insertions(+) create mode 100644 .changeset/spicy-monkeys-guess.md diff --git a/.changeset/spicy-monkeys-guess.md b/.changeset/spicy-monkeys-guess.md new file mode 100644 index 00000000000..a24ea31be91 --- /dev/null +++ b/.changeset/spicy-monkeys-guess.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +Handle CDK version mismatch diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index ff69a3796a7..e249b3861f8 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -14,6 +14,7 @@ "argv", "arn", "arns", + "aws", "backends", "birthdate", "bundler", diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 7e6e1415de9..48630a0bd6c 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -259,6 +259,15 @@ const testErrorMappings = [ errorName: 'FilePermissionsError', expectedDownstreamErrorMessage: `EACCES: permission denied, unlink '.amplify/artifacts/cdk.out/synth.lock'`, }, + { + errorMessage: `This CDK CLI is not compatible with the CDK library used by your application. Please upgrade the CLI to the latest version. + (Cloud assembly schema version mismatch: Maximum schema version supported is 36.0.0, but found 36.1.1)`, + expectedTopLevelErrorMessage: + "Installed 'aws-cdk' is not compatible with installed 'aws-cdk-lib'.", + errorName: 'CDKVersionMismatchError', + expectedDownstreamErrorMessage: `This CDK CLI is not compatible with the CDK library used by your application. Please upgrade the CLI to the latest version. + (Cloud assembly schema version mismatch: Maximum schema version supported is 36.0.0, but found 36.1.1)`, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index a00715d08c8..635f9793a57 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -110,6 +110,16 @@ export class CdkErrorMapper { errorName: 'BootstrapNotDetectedError', classification: 'ERROR', }, + { + errorRegex: + /This CDK CLI is not compatible with the CDK library used by your application\. Please upgrade the CLI to the latest version\./, + humanReadableErrorMessage: + "Installed 'aws-cdk' is not compatible with installed 'aws-cdk-lib'.", + resolutionMessage: + "Make sure that version of 'aws-cdk' is greater or equal to version of 'aws-cdk-lib'", + errorName: 'CDKVersionMismatchError', + classification: 'ERROR', + }, { errorRegex: new RegExp( `(SyntaxError|ReferenceError|TypeError):((?:.|${this.multiLineEolRegex})*?at .*)` @@ -272,6 +282,7 @@ export type CDKDeploymentError = | 'BackendSynthError' | 'BootstrapNotDetectedError' | 'CDKResolveAWSAccountError' + | 'CDKVersionMismatchError' | 'CFNUpdateNotSupportedError' | 'CloudFormationDeploymentError' | 'FilePermissionsError' From c9c873c22843152cca129523ea4f96bd97a0a3ba Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Wed, 18 Sep 2024 18:15:45 +0200 Subject: [PATCH 004/199] throw ESBuild error with correct messages (#2020) --- .changeset/fuzzy-drinks-attend.md | 6 +++++ .../src/cdk_error_mapper.test.ts | 23 +++++++++++++++++++ .../backend-deployer/src/cdk_error_mapper.ts | 12 ++++++++++ packages/backend-function/src/factory.ts | 13 ++++++++++- 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 .changeset/fuzzy-drinks-attend.md diff --git a/.changeset/fuzzy-drinks-attend.md b/.changeset/fuzzy-drinks-attend.md new file mode 100644 index 00000000000..b9d5077af01 --- /dev/null +++ b/.changeset/fuzzy-drinks-attend.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/backend-deployer': patch +'@aws-amplify/backend-function': patch +--- + +throw ESBuild error with correct messages diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 48630a0bd6c..8d5f2e783fd 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -169,6 +169,29 @@ const testErrorMappings = [ EOL + ` at /Users/user/work-space/amplify-app/amplify/data/resource.ts:16:0`, }, + { + errorMessage: + `✘ [ERROR] Could not resolve "$amplify/env/defaultNodeFunctions"` + + EOL + + EOL + + ` amplify/func-src/handler.ts:1:20:` + + EOL + + ` 1 │ ...t { env } from '$amplify/env/defaultNodeFunctions';` + + EOL + + `1 error`, + expectedTopLevelErrorMessage: + 'Unable to build the Amplify backend definition.', + errorName: 'ESBuildError', + expectedDownstreamErrorMessage: + `✘ [ERROR] Could not resolve "$amplify/env/defaultNodeFunctions"` + + EOL + + EOL + + ` amplify/func-src/handler.ts:1:20:` + + EOL + + ` 1 │ ...t { env } from '$amplify/env/defaultNodeFunctions';` + + EOL + + `1 error`, + }, { errorMessage: `Error [TransformError]: Transform failed with 1 error:` + diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 635f9793a57..c5c67c958c0 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -180,6 +180,18 @@ export class CdkErrorMapper { errorName: 'ESBuildError', classification: 'ERROR', }, + { + // Also extracts the first line in the stack where the error happened + errorRegex: new RegExp( + `[✘X] \\[ERROR\\] ((?:.|${this.multiLineEolRegex})*error.*)` + ), + humanReadableErrorMessage: + 'Unable to build the Amplify backend definition.', + resolutionMessage: + 'Check your backend definition in the `amplify` folder for syntax and type errors.', + errorName: 'ESBuildError', + classification: 'ERROR', + }, { errorRegex: new RegExp( `\\[TransformError\\]: Transform failed with .* error:${this.multiLineEolRegex}(?.*)` diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index e32e72cfb69..0b184ddda27 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -373,11 +373,22 @@ class AmplifyFunction }, }); } catch (error) { + // If the error is from ES Bundler which is executed as a child process by CDK, + // then the error from CDK contains the command that was executed along with the exit status. + // Wrapping it here would cause the cdk_deployer to re-throw this wrapped exception + // instead of scraping the stderr for actual ESBuild error. + if ( + error instanceof Error && + error.message.match(/Failed to bundle asset.*exited with status/) + ) { + throw error; + } throw new AmplifyUserError( 'NodeJSFunctionConstructInitializationError', { message: 'Failed to instantiate nodejs function construct', - resolution: 'See the underlying error message for more details.', + resolution: + 'See the underlying error message for more details. Use `--debug` for additional debugging information.', }, error as Error ); From a00533eb999f47f79b501dee2bd0ed9fe43a78c5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 10:34:48 -0700 Subject: [PATCH 005/199] Version Packages (#1994) Co-authored-by: github-actions[bot] --- .changeset/cool-bulldogs-accept.md | 2 -- .changeset/eight-cars-train.md | 5 ---- .changeset/forty-buckets-visit.md | 21 ----------------- .changeset/fresh-dancers-peel.md | 6 ----- .changeset/fuzzy-drinks-attend.md | 6 ----- .changeset/spicy-monkeys-guess.md | 5 ---- .changeset/stale-worms-pretend.md | 2 -- .changeset/strange-jobs-refuse.md | 14 ----------- .changeset/yellow-jokes-kick.md | 12 ---------- packages/ampx/CHANGELOG.md | 6 +++++ packages/ampx/package.json | 2 +- packages/auth-construct/CHANGELOG.md | 9 ++++++++ packages/auth-construct/package.json | 6 ++--- packages/backend-auth/CHANGELOG.md | 11 +++++++++ packages/backend-auth/package.json | 10 ++++---- packages/backend-data/CHANGELOG.md | 10 ++++++++ packages/backend-data/package.json | 8 +++---- packages/backend-deployer/CHANGELOG.md | 11 +++++++++ packages/backend-deployer/package.json | 4 ++-- packages/backend-function/CHANGELOG.md | 10 ++++++++ packages/backend-function/package.json | 8 +++---- packages/backend-output-storage/CHANGELOG.md | 8 +++++++ packages/backend-output-storage/package.json | 4 ++-- .../backend-platform-test-stubs/CHANGELOG.md | 8 +++++++ .../backend-platform-test-stubs/package.json | 4 ++-- packages/backend-secret/CHANGELOG.md | 10 ++++++++ packages/backend-secret/package.json | 4 ++-- packages/backend-storage/CHANGELOG.md | 9 ++++++++ packages/backend-storage/package.json | 8 +++---- packages/backend/CHANGELOG.md | 20 ++++++++++++++++ packages/backend/package.json | 18 +++++++-------- packages/cli-core/CHANGELOG.md | 6 +++++ packages/cli-core/package.json | 2 +- packages/cli/CHANGELOG.md | 23 +++++++++++++++++++ packages/cli/package.json | 22 +++++++++--------- packages/client-config/CHANGELOG.md | 14 +++++++++++ packages/client-config/package.json | 8 +++---- packages/create-amplify/CHANGELOG.md | 9 ++++++++ packages/create-amplify/package.json | 6 ++--- packages/deployed-backend-client/CHANGELOG.md | 10 ++++++++ packages/deployed-backend-client/package.json | 4 ++-- packages/form-generator/CHANGELOG.md | 7 ++++++ packages/form-generator/package.json | 2 +- packages/integration-tests/CHANGELOG.md | 6 +++++ packages/integration-tests/package.json | 14 +++++------ packages/model-generator/CHANGELOG.md | 13 +++++++++++ packages/model-generator/package.json | 6 ++--- packages/plugin-types/CHANGELOG.md | 6 +++++ packages/plugin-types/package.json | 2 +- packages/sandbox/CHANGELOG.md | 21 +++++++++++++++++ packages/sandbox/package.json | 14 +++++------ packages/schema-generator/CHANGELOG.md | 7 ++++++ packages/schema-generator/package.json | 2 +- 53 files changed, 313 insertions(+), 152 deletions(-) delete mode 100644 .changeset/cool-bulldogs-accept.md delete mode 100644 .changeset/eight-cars-train.md delete mode 100644 .changeset/forty-buckets-visit.md delete mode 100644 .changeset/fresh-dancers-peel.md delete mode 100644 .changeset/fuzzy-drinks-attend.md delete mode 100644 .changeset/spicy-monkeys-guess.md delete mode 100644 .changeset/stale-worms-pretend.md delete mode 100644 .changeset/strange-jobs-refuse.md delete mode 100644 .changeset/yellow-jokes-kick.md diff --git a/.changeset/cool-bulldogs-accept.md b/.changeset/cool-bulldogs-accept.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/cool-bulldogs-accept.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/eight-cars-train.md b/.changeset/eight-cars-train.md deleted file mode 100644 index 8b546c7a98a..00000000000 --- a/.changeset/eight-cars-train.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-data': patch ---- - -update data construct diff --git a/.changeset/forty-buckets-visit.md b/.changeset/forty-buckets-visit.md deleted file mode 100644 index 4f19c5fee76..00000000000 --- a/.changeset/forty-buckets-visit.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -'@aws-amplify/deployed-backend-client': patch -'@aws-amplify/backend-deployer': patch -'@aws-amplify/backend-function': patch -'@aws-amplify/schema-generator': patch -'@aws-amplify/backend-storage': patch -'@aws-amplify/model-generator': patch -'@aws-amplify/auth-construct': patch -'@aws-amplify/backend-secret': patch -'create-amplify': patch -'@aws-amplify/form-generator': patch -'@aws-amplify/client-config': patch -'@aws-amplify/backend-auth': patch -'@aws-amplify/backend-data': patch -'@aws-amplify/backend': patch -'@aws-amplify/sandbox': patch -'ampx': patch -'@aws-amplify/backend-cli': patch ---- - -added main field to package.json so these packages are resolvable diff --git a/.changeset/fresh-dancers-peel.md b/.changeset/fresh-dancers-peel.md deleted file mode 100644 index 4b78eee2c57..00000000000 --- a/.changeset/fresh-dancers-peel.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/backend-secret': patch -'@aws-amplify/sandbox': patch ---- - -add ExpiredToken in the list of credentials error diff --git a/.changeset/fuzzy-drinks-attend.md b/.changeset/fuzzy-drinks-attend.md deleted file mode 100644 index b9d5077af01..00000000000 --- a/.changeset/fuzzy-drinks-attend.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch -'@aws-amplify/backend-function': patch ---- - -throw ESBuild error with correct messages diff --git a/.changeset/spicy-monkeys-guess.md b/.changeset/spicy-monkeys-guess.md deleted file mode 100644 index a24ea31be91..00000000000 --- a/.changeset/spicy-monkeys-guess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -Handle CDK version mismatch diff --git a/.changeset/stale-worms-pretend.md b/.changeset/stale-worms-pretend.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/stale-worms-pretend.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/strange-jobs-refuse.md b/.changeset/strange-jobs-refuse.md deleted file mode 100644 index e7ee1c9d472..00000000000 --- a/.changeset/strange-jobs-refuse.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@aws-amplify/backend-platform-test-stubs': patch -'@aws-amplify/deployed-backend-client': patch -'@aws-amplify/backend-output-storage': patch -'@aws-amplify/integration-tests': patch -'@aws-amplify/model-generator': patch -'@aws-amplify/client-config': patch -'@aws-amplify/plugin-types': patch -'@aws-amplify/cli-core': patch -'@aws-amplify/sandbox': patch -'@aws-amplify/backend-cli': patch ---- - -fixed errors in plugin-types and cli-core along with any extraneous dependencies in other packages diff --git a/.changeset/yellow-jokes-kick.md b/.changeset/yellow-jokes-kick.md deleted file mode 100644 index 83fd0a01b42..00000000000 --- a/.changeset/yellow-jokes-kick.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@aws-amplify/deployed-backend-client': patch -'@aws-amplify/backend-deployer': patch -'@aws-amplify/schema-generator': patch -'@aws-amplify/model-generator': patch -'@aws-amplify/backend-secret': patch -'@aws-amplify/form-generator': patch -'@aws-amplify/client-config': patch -'@aws-amplify/sandbox': patch ---- - -added main field to packages known to lack one diff --git a/packages/ampx/CHANGELOG.md b/packages/ampx/CHANGELOG.md index 4491409e236..038f571e2fa 100644 --- a/packages/ampx/CHANGELOG.md +++ b/packages/ampx/CHANGELOG.md @@ -1,5 +1,11 @@ # ampx +## 0.2.2 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable + ## 0.2.1 ### Patch Changes diff --git a/packages/ampx/package.json b/packages/ampx/package.json index d029196189e..be64fde07a9 100644 --- a/packages/ampx/package.json +++ b/packages/ampx/package.json @@ -1,6 +1,6 @@ { "name": "ampx", - "version": "0.2.1", + "version": "0.2.2", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/auth-construct/CHANGELOG.md b/packages/auth-construct/CHANGELOG.md index d41c584235c..3daa63b0973 100644 --- a/packages/auth-construct/CHANGELOG.md +++ b/packages/auth-construct/CHANGELOG.md @@ -1,5 +1,14 @@ # @aws-amplify/auth-construct +## 1.3.1 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- Updated dependencies [8dd7286] + - @aws-amplify/backend-output-storage@1.1.2 + - @aws-amplify/plugin-types@1.2.2 + ## 1.3.0 ### Minor Changes diff --git a/packages/auth-construct/package.json b/packages/auth-construct/package.json index cbd26233a5a..f69909104b9 100644 --- a/packages/auth-construct/package.json +++ b/packages/auth-construct/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/auth-construct", - "version": "1.3.0", + "version": "1.3.1", "type": "commonjs", "publishConfig": { "access": "public" @@ -20,8 +20,8 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", - "@aws-amplify/backend-output-storage": "^1.1.1", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/plugin-types": "^1.2.2", "@aws-sdk/util-arn-parser": "^3.568.0" }, "peerDependencies": { diff --git a/packages/backend-auth/CHANGELOG.md b/packages/backend-auth/CHANGELOG.md index eac5836eccd..0cee4996550 100644 --- a/packages/backend-auth/CHANGELOG.md +++ b/packages/backend-auth/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend-auth +## 1.1.5 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- Updated dependencies [e648e8e] +- Updated dependencies [8dd7286] + - @aws-amplify/auth-construct@1.3.1 + - @aws-amplify/backend-output-storage@1.1.2 + - @aws-amplify/plugin-types@1.2.2 + ## 1.1.4 ### Patch Changes diff --git a/packages/backend-auth/package.json b/packages/backend-auth/package.json index bee451734b5..68c0b590190 100644 --- a/packages/backend-auth/package.json +++ b/packages/backend-auth/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-auth", - "version": "1.1.4", + "version": "1.1.5", "type": "module", "publishConfig": { "access": "public" @@ -19,12 +19,12 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/auth-construct": "^1.3.0", - "@aws-amplify/backend-output-storage": "^1.1.1", - "@aws-amplify/plugin-types": "^1.2.1" + "@aws-amplify/auth-construct": "^1.3.1", + "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/plugin-types": "^1.2.2" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.4", + "@aws-amplify/backend-platform-test-stubs": "^0.3.5", "@aws-amplify/platform-core": "^1.0.6" }, "peerDependencies": { diff --git a/packages/backend-data/CHANGELOG.md b/packages/backend-data/CHANGELOG.md index 85ba09de848..84349755363 100644 --- a/packages/backend-data/CHANGELOG.md +++ b/packages/backend-data/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/backend-data +## 1.1.4 + +### Patch Changes + +- ffc3b42: update data construct +- e648e8e: added main field to package.json so these packages are resolvable +- Updated dependencies [8dd7286] + - @aws-amplify/backend-output-storage@1.1.2 + - @aws-amplify/plugin-types@1.2.2 + ## 1.1.3 ### Patch Changes diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index a4cdf8912d3..3f31e6e5077 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-data", - "version": "1.1.3", + "version": "1.1.4", "type": "module", "publishConfig": { "access": "public" @@ -20,7 +20,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-amplify/data-schema": "^1.0.0", - "@aws-amplify/backend-platform-test-stubs": "^0.3.4", + "@aws-amplify/backend-platform-test-stubs": "^0.3.5", "@aws-amplify/platform-core": "^1.0.7" }, "peerDependencies": { @@ -28,10 +28,10 @@ "constructs": "^10.0.0" }, "dependencies": { - "@aws-amplify/backend-output-storage": "^1.1.1", + "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/backend-output-schemas": "^1.1.0", "@aws-amplify/data-construct": "^1.10.1", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "@aws-amplify/data-schema-types": "^1.1.1" } } diff --git a/packages/backend-deployer/CHANGELOG.md b/packages/backend-deployer/CHANGELOG.md index 3055dcdd90e..a2427731f4e 100644 --- a/packages/backend-deployer/CHANGELOG.md +++ b/packages/backend-deployer/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend-deployer +## 1.1.3 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- c9c873c: throw ESBuild error with correct messages +- cbac105: Handle CDK version mismatch +- e648e8e: added main field to packages known to lack one +- Updated dependencies [8dd7286] + - @aws-amplify/plugin-types@1.2.2 + ## 1.1.2 ### Patch Changes diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index 6165fd7fa98..d329c432379 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-deployer", - "version": "1.1.2", + "version": "1.1.3", "type": "module", "publishConfig": { "access": "public" @@ -20,7 +20,7 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "execa": "^8.0.1", "tsx": "^4.6.1" }, diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index 68161b9d38e..a8f9cfe8cd8 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/backend-function +## 1.4.1 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- c9c873c: throw ESBuild error with correct messages +- Updated dependencies [8dd7286] + - @aws-amplify/backend-output-storage@1.1.2 + - @aws-amplify/plugin-types@1.2.2 + ## 1.4.0 ### Minor Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index bdb9e459826..c30cb8017a1 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.4.0", + "version": "1.4.1", "type": "module", "publishConfig": { "access": "public" @@ -20,12 +20,12 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", - "@aws-amplify/backend-output-storage": "^1.1.1", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/plugin-types": "^1.2.2", "execa": "^8.0.1" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.4", + "@aws-amplify/backend-platform-test-stubs": "^0.3.5", "@aws-amplify/platform-core": "^1.1.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", diff --git a/packages/backend-output-storage/CHANGELOG.md b/packages/backend-output-storage/CHANGELOG.md index 51e28f0faab..e8b0e9f9a58 100644 --- a/packages/backend-output-storage/CHANGELOG.md +++ b/packages/backend-output-storage/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-output-storage +## 1.1.2 + +### Patch Changes + +- 8dd7286: fixed errors in plugin-types and cli-core along with any extraneous dependencies in other packages +- Updated dependencies [8dd7286] + - @aws-amplify/plugin-types@1.2.2 + ## 1.1.1 ### Patch Changes diff --git a/packages/backend-output-storage/package.json b/packages/backend-output-storage/package.json index a854f673d9a..228a0f85b11 100644 --- a/packages/backend-output-storage/package.json +++ b/packages/backend-output-storage/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-output-storage", - "version": "1.1.1", + "version": "1.1.2", "type": "commonjs", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.2.1" + "@aws-amplify/plugin-types": "^1.2.2" }, "peerDependencies": { "aws-cdk-lib": "^2.152.0" diff --git a/packages/backend-platform-test-stubs/CHANGELOG.md b/packages/backend-platform-test-stubs/CHANGELOG.md index 6c1a63bd463..0b73fa2b05a 100644 --- a/packages/backend-platform-test-stubs/CHANGELOG.md +++ b/packages/backend-platform-test-stubs/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-platform-test-stubs +## 0.3.5 + +### Patch Changes + +- 8dd7286: fixed errors in plugin-types and cli-core along with any extraneous dependencies in other packages +- Updated dependencies [8dd7286] + - @aws-amplify/plugin-types@1.2.2 + ## 0.3.4 ### Patch Changes diff --git a/packages/backend-platform-test-stubs/package.json b/packages/backend-platform-test-stubs/package.json index 409cb649109..953c053355f 100644 --- a/packages/backend-platform-test-stubs/package.json +++ b/packages/backend-platform-test-stubs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-platform-test-stubs", - "version": "0.3.4", + "version": "0.3.5", "type": "module", "private": true, "exports": { @@ -16,7 +16,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "aws-cdk-lib": "^2.152.0", "constructs": "^10.0.0" } diff --git a/packages/backend-secret/CHANGELOG.md b/packages/backend-secret/CHANGELOG.md index ddf0cac6187..f044611a38a 100644 --- a/packages/backend-secret/CHANGELOG.md +++ b/packages/backend-secret/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/backend-secret +## 1.1.2 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- 0ff73ec: add ExpiredToken in the list of credentials error +- e648e8e: added main field to packages known to lack one +- Updated dependencies [8dd7286] + - @aws-amplify/plugin-types@1.2.2 + ## 1.1.1 ### Patch Changes diff --git a/packages/backend-secret/package.json b/packages/backend-secret/package.json index 56835c984dd..8972da637fe 100644 --- a/packages/backend-secret/package.json +++ b/packages/backend-secret/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-secret", - "version": "1.1.1", + "version": "1.1.2", "type": "module", "publishConfig": { "access": "public" @@ -19,7 +19,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.1.1", + "@aws-amplify/plugin-types": "^1.2.2", "@aws-amplify/platform-core": "^1.0.5", "@aws-sdk/client-ssm": "^3.624.0" }, diff --git a/packages/backend-storage/CHANGELOG.md b/packages/backend-storage/CHANGELOG.md index 2be32cc7492..3f1b5f28039 100644 --- a/packages/backend-storage/CHANGELOG.md +++ b/packages/backend-storage/CHANGELOG.md @@ -1,5 +1,14 @@ # @aws-amplify/backend-storage +## 1.1.3 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- Updated dependencies [8dd7286] + - @aws-amplify/backend-output-storage@1.1.2 + - @aws-amplify/plugin-types@1.2.2 + ## 1.1.2 ### Patch Changes diff --git a/packages/backend-storage/package.json b/packages/backend-storage/package.json index c1bcb05b335..7d4abbf78a5 100644 --- a/packages/backend-storage/package.json +++ b/packages/backend-storage/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-storage", - "version": "1.1.2", + "version": "1.1.3", "type": "module", "publishConfig": { "access": "public" @@ -20,11 +20,11 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/backend-output-storage": "^1.1.1", - "@aws-amplify/plugin-types": "^1.2.1" + "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/plugin-types": "^1.2.2" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.4", + "@aws-amplify/backend-platform-test-stubs": "^0.3.5", "@aws-amplify/platform-core": "^1.0.6" }, "peerDependencies": { diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index cc029c0cb07..2173352b44b 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,25 @@ # @aws-amplify/backend +## 1.2.2 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- Updated dependencies [ffc3b42] +- Updated dependencies [e648e8e] +- Updated dependencies [0ff73ec] +- Updated dependencies [c9c873c] +- Updated dependencies [8dd7286] +- Updated dependencies [e648e8e] + - @aws-amplify/backend-data@1.1.4 + - @aws-amplify/backend-function@1.4.1 + - @aws-amplify/backend-storage@1.1.3 + - @aws-amplify/backend-secret@1.1.2 + - @aws-amplify/client-config@1.3.1 + - @aws-amplify/backend-auth@1.1.5 + - @aws-amplify/backend-output-storage@1.1.2 + - @aws-amplify/plugin-types@1.2.2 + ## 1.2.1 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index a411301bfe4..d05dc6f45fd 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.2.1", + "version": "1.2.2", "type": "module", "publishConfig": { "access": "public" @@ -26,16 +26,16 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/data-schema": "^1.0.0", - "@aws-amplify/backend-auth": "^1.1.2", - "@aws-amplify/backend-function": "^1.4.0", - "@aws-amplify/backend-data": "^1.1.3", + "@aws-amplify/backend-auth": "^1.1.5", + "@aws-amplify/backend-function": "^1.4.1", + "@aws-amplify/backend-data": "^1.1.4", "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/backend-output-storage": "^1.1.1", - "@aws-amplify/backend-secret": "^1.0.1", - "@aws-amplify/backend-storage": "^1.1.1", - "@aws-amplify/client-config": "^1.3.0", + "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/backend-secret": "^1.1.2", + "@aws-amplify/backend-storage": "^1.1.3", + "@aws-amplify/client-config": "^1.3.1", "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "@aws-sdk/client-amplify": "^3.624.0", "lodash.snakecase": "^4.1.1" }, diff --git a/packages/cli-core/CHANGELOG.md b/packages/cli-core/CHANGELOG.md index a99721d8219..6aefdbed094 100644 --- a/packages/cli-core/CHANGELOG.md +++ b/packages/cli-core/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/cli-core +## 1.1.3 + +### Patch Changes + +- 8dd7286: fixed errors in plugin-types and cli-core along with any extraneous dependencies in other packages + ## 1.1.2 ### Patch Changes diff --git a/packages/cli-core/package.json b/packages/cli-core/package.json index b752e107634..61dd48e7121 100644 --- a/packages/cli-core/package.json +++ b/packages/cli-core/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/cli-core", - "version": "1.1.2", + "version": "1.1.3", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index ad5515725e5..231b451546b 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,28 @@ # @aws-amplify/backend-cli +## 1.2.7 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- 8dd7286: fixed errors in plugin-types and cli-core along with any extraneous dependencies in other packages +- Updated dependencies [e648e8e] +- Updated dependencies [0ff73ec] +- Updated dependencies [c9c873c] +- Updated dependencies [cbac105] +- Updated dependencies [8dd7286] +- Updated dependencies [e648e8e] + - @aws-amplify/deployed-backend-client@1.4.1 + - @aws-amplify/backend-deployer@1.1.3 + - @aws-amplify/schema-generator@1.2.3 + - @aws-amplify/model-generator@1.0.7 + - @aws-amplify/backend-secret@1.1.2 + - @aws-amplify/form-generator@1.0.2 + - @aws-amplify/client-config@1.3.1 + - @aws-amplify/sandbox@1.2.2 + - @aws-amplify/plugin-types@1.2.2 + - @aws-amplify/cli-core@1.1.3 + ## 1.2.6 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index bd40712e75e..373c854cd51 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-cli", - "version": "1.2.6", + "version": "1.2.7", "description": "Command line interface for various Amplify tools", "bin": { "ampx": "lib/ampx.js", @@ -31,18 +31,18 @@ }, "homepage": "https://github.com/aws-amplify/amplify-backend#readme", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.1", + "@aws-amplify/backend-deployer": "^1.1.3", "@aws-amplify/backend-output-schemas": "^1.1.0", - "@aws-amplify/backend-secret": "^1.1.0", - "@aws-amplify/cli-core": "^1.1.2", - "@aws-amplify/client-config": "^1.2.1", - "@aws-amplify/deployed-backend-client": "^1.3.0", - "@aws-amplify/form-generator": "^1.0.1", - "@aws-amplify/model-generator": "^1.0.5", + "@aws-amplify/backend-secret": "^1.1.2", + "@aws-amplify/cli-core": "^1.1.3", + "@aws-amplify/client-config": "^1.3.1", + "@aws-amplify/deployed-backend-client": "^1.4.1", + "@aws-amplify/form-generator": "^1.0.2", + "@aws-amplify/model-generator": "^1.0.7", "@aws-amplify/platform-core": "^1.0.5", - "@aws-amplify/plugin-types": "^1.2.1", - "@aws-amplify/sandbox": "^1.2.0", - "@aws-amplify/schema-generator": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/sandbox": "^1.2.2", + "@aws-amplify/schema-generator": "^1.2.3", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", diff --git a/packages/client-config/CHANGELOG.md b/packages/client-config/CHANGELOG.md index 9e35ebdc83d..9805ffc4100 100644 --- a/packages/client-config/CHANGELOG.md +++ b/packages/client-config/CHANGELOG.md @@ -1,5 +1,19 @@ # @aws-amplify/client-config +## 1.3.1 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- 8dd7286: fixed errors in plugin-types and cli-core along with any extraneous dependencies in other packages +- e648e8e: added main field to packages known to lack one +- Updated dependencies [e648e8e] +- Updated dependencies [8dd7286] +- Updated dependencies [e648e8e] + - @aws-amplify/deployed-backend-client@1.4.1 + - @aws-amplify/model-generator@1.0.7 + - @aws-amplify/plugin-types@1.2.2 + ## 1.3.0 ### Minor Changes diff --git a/packages/client-config/package.json b/packages/client-config/package.json index 74affd69dee..db3cc4bd9d8 100644 --- a/packages/client-config/package.json +++ b/packages/client-config/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/client-config", - "version": "1.3.0", + "version": "1.3.1", "type": "module", "publishConfig": { "access": "public" @@ -25,10 +25,10 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/deployed-backend-client": "^1.4.0", - "@aws-amplify/model-generator": "^1.0.5", + "@aws-amplify/deployed-backend-client": "^1.4.1", + "@aws-amplify/model-generator": "^1.0.7", "@aws-amplify/platform-core": "^1.0.7", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "zod": "^3.22.2" }, "devDependencies": { diff --git a/packages/create-amplify/CHANGELOG.md b/packages/create-amplify/CHANGELOG.md index aaf0868ebcb..7881bbcdbfb 100644 --- a/packages/create-amplify/CHANGELOG.md +++ b/packages/create-amplify/CHANGELOG.md @@ -1,5 +1,14 @@ # create-amplify +## 1.0.6 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- Updated dependencies [8dd7286] + - @aws-amplify/plugin-types@1.2.2 + - @aws-amplify/cli-core@1.1.3 + ## 1.0.5 ### Patch Changes diff --git a/packages/create-amplify/package.json b/packages/create-amplify/package.json index 53c9a4f5c83..7cbff22777e 100644 --- a/packages/create-amplify/package.json +++ b/packages/create-amplify/package.json @@ -1,6 +1,6 @@ { "name": "create-amplify", - "version": "1.0.5", + "version": "1.0.6", "type": "module", "main": "lib/index.js", "publishConfig": { @@ -17,9 +17,9 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/cli-core": "^1.1.1", + "@aws-amplify/cli-core": "^1.1.3", "@aws-amplify/platform-core": "^1.0.3", - "@aws-amplify/plugin-types": "^1.1.0", + "@aws-amplify/plugin-types": "^1.2.2", "execa": "^8.0.1", "kleur": "^4.1.5", "yargs": "^17.7.2" diff --git a/packages/deployed-backend-client/CHANGELOG.md b/packages/deployed-backend-client/CHANGELOG.md index 3df442dc355..7ee529cc2b6 100644 --- a/packages/deployed-backend-client/CHANGELOG.md +++ b/packages/deployed-backend-client/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/deployed-backend-client +## 1.4.1 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- 8dd7286: fixed errors in plugin-types and cli-core along with any extraneous dependencies in other packages +- e648e8e: added main field to packages known to lack one +- Updated dependencies [8dd7286] + - @aws-amplify/plugin-types@1.2.2 + ## 1.4.0 ### Minor Changes diff --git a/packages/deployed-backend-client/package.json b/packages/deployed-backend-client/package.json index 6f631b038b4..ef86b6e30e2 100644 --- a/packages/deployed-backend-client/package.json +++ b/packages/deployed-backend-client/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/deployed-backend-client", - "version": "1.4.0", + "version": "1.4.1", "type": "module", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", "@aws-amplify/platform-core": "^1.0.5", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "zod": "^3.22.2" }, "peerDependencies": { diff --git a/packages/form-generator/CHANGELOG.md b/packages/form-generator/CHANGELOG.md index a2b4d28246d..77772684396 100644 --- a/packages/form-generator/CHANGELOG.md +++ b/packages/form-generator/CHANGELOG.md @@ -1,5 +1,12 @@ # @aws-amplify/form-generator +## 1.0.2 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- e648e8e: added main field to packages known to lack one + ## 1.0.1 ### Patch Changes diff --git a/packages/form-generator/package.json b/packages/form-generator/package.json index 37703ba852a..7fd3600d6b2 100644 --- a/packages/form-generator/package.json +++ b/packages/form-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/form-generator", - "version": "1.0.1", + "version": "1.0.2", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/integration-tests/CHANGELOG.md b/packages/integration-tests/CHANGELOG.md index 528477bdbca..e9486358715 100644 --- a/packages/integration-tests/CHANGELOG.md +++ b/packages/integration-tests/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/integration-tests +## 0.5.9 + +### Patch Changes + +- 8dd7286: fixed errors in plugin-types and cli-core along with any extraneous dependencies in other packages + ## 0.5.8 ### Patch Changes diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 60073eac05a..cfd7db14626 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -1,20 +1,20 @@ { "name": "@aws-amplify/integration-tests", "private": true, - "version": "0.5.8", + "version": "0.5.9", "type": "module", "devDependencies": { "@apollo/client": "^3.10.1", "@aws-amplify/ai-constructs": "^0.1.0", - "@aws-amplify/auth-construct": "^1.2.2", - "@aws-amplify/backend": "^1.2.1", + "@aws-amplify/auth-construct": "^1.3.1", + "@aws-amplify/backend": "^1.2.2", "@aws-amplify/backend-ai": "^0.1.0", - "@aws-amplify/backend-secret": "^1.0.1", - "@aws-amplify/client-config": "^1.1.3", + "@aws-amplify/backend-secret": "^1.1.2", + "@aws-amplify/client-config": "^1.3.1", "@aws-amplify/data-schema": "^1.0.0", - "@aws-amplify/deployed-backend-client": "^1.3.0", + "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "@aws-sdk/client-accessanalyzer": "^3.624.0", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-bedrock-runtime": "^3.622.0", diff --git a/packages/model-generator/CHANGELOG.md b/packages/model-generator/CHANGELOG.md index 56dd0a8f932..36e27a7a4e0 100644 --- a/packages/model-generator/CHANGELOG.md +++ b/packages/model-generator/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/model-generator +## 1.0.7 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- 8dd7286: fixed errors in plugin-types and cli-core along with any extraneous dependencies in other packages +- e648e8e: added main field to packages known to lack one +- Updated dependencies [e648e8e] +- Updated dependencies [8dd7286] +- Updated dependencies [e648e8e] + - @aws-amplify/deployed-backend-client@1.4.1 + - @aws-amplify/plugin-types@1.2.2 + ## 1.0.6 ### Patch Changes diff --git a/packages/model-generator/package.json b/packages/model-generator/package.json index 2f6efcbd503..11d9f92cd9e 100644 --- a/packages/model-generator/package.json +++ b/packages/model-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/model-generator", - "version": "1.0.6", + "version": "1.0.7", "type": "module", "publishConfig": { "access": "public" @@ -20,11 +20,11 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", - "@aws-amplify/deployed-backend-client": "^1.3.0", + "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/graphql-generator": "^0.4.0", "@aws-amplify/graphql-types-generator": "^3.6.0", "@aws-amplify/platform-core": "^1.0.5", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "@aws-sdk/client-appsync": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/credential-providers": "^3.624.0", diff --git a/packages/plugin-types/CHANGELOG.md b/packages/plugin-types/CHANGELOG.md index 1fe3d4c0272..c77e6874de0 100644 --- a/packages/plugin-types/CHANGELOG.md +++ b/packages/plugin-types/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/plugin-types +## 1.2.2 + +### Patch Changes + +- 8dd7286: fixed errors in plugin-types and cli-core along with any extraneous dependencies in other packages + ## 1.2.1 ### Patch Changes diff --git a/packages/plugin-types/package.json b/packages/plugin-types/package.json index 81a4624688c..4b928b3f114 100644 --- a/packages/plugin-types/package.json +++ b/packages/plugin-types/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/plugin-types", - "version": "1.2.1", + "version": "1.2.2", "types": "lib/index.d.ts", "type": "commonjs", "publishConfig": { diff --git a/packages/sandbox/CHANGELOG.md b/packages/sandbox/CHANGELOG.md index a129089dab3..6a8608ba710 100644 --- a/packages/sandbox/CHANGELOG.md +++ b/packages/sandbox/CHANGELOG.md @@ -1,5 +1,26 @@ # @aws-amplify/sandbox +## 1.2.2 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- 0ff73ec: add ExpiredToken in the list of credentials error +- 8dd7286: fixed errors in plugin-types and cli-core along with any extraneous dependencies in other packages +- e648e8e: added main field to packages known to lack one +- Updated dependencies [e648e8e] +- Updated dependencies [0ff73ec] +- Updated dependencies [c9c873c] +- Updated dependencies [cbac105] +- Updated dependencies [8dd7286] +- Updated dependencies [e648e8e] + - @aws-amplify/deployed-backend-client@1.4.1 + - @aws-amplify/backend-deployer@1.1.3 + - @aws-amplify/backend-secret@1.1.2 + - @aws-amplify/client-config@1.3.1 + - @aws-amplify/plugin-types@1.2.2 + - @aws-amplify/cli-core@1.1.3 + ## 1.2.1 ### Patch Changes diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index 36bfaf4c412..2ca57e592f3 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/sandbox", - "version": "1.2.1", + "version": "1.2.2", "type": "module", "publishConfig": { "access": "public" @@ -19,13 +19,13 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.0", - "@aws-amplify/backend-secret": "^1.1.1", - "@aws-amplify/cli-core": "^1.1.2", - "@aws-amplify/client-config": "^1.1.3", - "@aws-amplify/deployed-backend-client": "^1.3.0", + "@aws-amplify/backend-deployer": "^1.1.3", + "@aws-amplify/backend-secret": "^1.1.2", + "@aws-amplify/cli-core": "^1.1.3", + "@aws-amplify/client-config": "^1.3.1", + "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", diff --git a/packages/schema-generator/CHANGELOG.md b/packages/schema-generator/CHANGELOG.md index 767c6a2687d..e18465fe2e1 100644 --- a/packages/schema-generator/CHANGELOG.md +++ b/packages/schema-generator/CHANGELOG.md @@ -1,5 +1,12 @@ # @aws-amplify/schema-generator +## 1.2.3 + +### Patch Changes + +- e648e8e: added main field to package.json so these packages are resolvable +- e648e8e: added main field to packages known to lack one + ## 1.2.2 ### Patch Changes diff --git a/packages/schema-generator/package.json b/packages/schema-generator/package.json index 160643bd6f8..4e569c34d17 100644 --- a/packages/schema-generator/package.json +++ b/packages/schema-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/schema-generator", - "version": "1.2.2", + "version": "1.2.3", "type": "module", "publishConfig": { "access": "public" From 98673b0d891be2648cbcf1ca3aba1fe30d1643e3 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 18 Sep 2024 11:19:09 -0700 Subject: [PATCH 006/199] Handle CDK version mismatch (#2022) --- .changeset/rich-jokes-divide.md | 5 +++++ .../backend-deployer/src/cdk_error_mapper.test.ts | 11 +++++++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 .changeset/rich-jokes-divide.md diff --git a/.changeset/rich-jokes-divide.md b/.changeset/rich-jokes-divide.md new file mode 100644 index 00000000000..8e654162360 --- /dev/null +++ b/.changeset/rich-jokes-divide.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +Improve type error regex diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 8d5f2e783fd..5e22f875d70 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -55,6 +55,17 @@ const testErrorMappings = [ EOL + ` at lookup(/some_random/path.js: 1: 3005)`, }, + { + errorMessage: `TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module ..../function/foo/resource.ts is not a valid package name imported from +/Users/foo/Desktop/amplify-app/amplify/storage/foo/resource.ts + at new NodeError (node:internal/errors:405:5)`, + expectedTopLevelErrorMessage: + 'Unable to build the Amplify backend definition.', + errorName: 'SyntaxError', + expectedDownstreamErrorMessage: `TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module ..../function/foo/resource.ts is not a valid package name imported from +/Users/foo/Desktop/amplify-app/amplify/storage/foo/resource.ts + at new NodeError (node:internal/errors:405:5)`, + }, { errorMessage: 'Has the environment been bootstrapped', expectedTopLevelErrorMessage: diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index c5c67c958c0..f194d0d4aaa 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -122,7 +122,7 @@ export class CdkErrorMapper { }, { errorRegex: new RegExp( - `(SyntaxError|ReferenceError|TypeError):((?:.|${this.multiLineEolRegex})*?at .*)` + `(SyntaxError|ReferenceError|TypeError)( \\[[A-Z_]+])?:((?:.|${this.multiLineEolRegex})*?at .*)` ), humanReadableErrorMessage: 'Unable to build the Amplify backend definition.', From 603b75dd8f988135f2074c0ef3a6f4d42d799248 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 18 Sep 2024 13:26:07 -0700 Subject: [PATCH 007/199] Classify pointing client config generator at metadata-less stack as user error (#2024) --- .changeset/poor-owls-listen.md | 5 +++ .../unified_client_config_generator.test.ts | 33 +++++++++++++++++++ .../src/unified_client_config_generator.ts | 14 ++++++++ 3 files changed, 52 insertions(+) create mode 100644 .changeset/poor-owls-listen.md diff --git a/.changeset/poor-owls-listen.md b/.changeset/poor-owls-listen.md new file mode 100644 index 00000000000..f7ab95c9224 --- /dev/null +++ b/.changeset/poor-owls-listen.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/client-config': patch +--- + +Classify pointing client config generator at metadata-less stack as user error diff --git a/packages/client-config/src/unified_client_config_generator.test.ts b/packages/client-config/src/unified_client_config_generator.test.ts index 9f1734ca2a2..4e6f0b0aed5 100644 --- a/packages/client-config/src/unified_client_config_generator.test.ts +++ b/packages/client-config/src/unified_client_config_generator.test.ts @@ -449,6 +449,39 @@ void describe('UnifiedClientConfigGenerator', () => { ); }); + void it('throws user error if the stack is missing metadata', async () => { + const outputRetrieval = mock.fn(() => { + throw new BackendOutputClientError( + BackendOutputClientErrorType.METADATA_RETRIEVAL_ERROR, + 'Stack template metadata is not a string' + ); + }); + const modelSchemaAdapter = new ModelIntrospectionSchemaAdapter( + stubClientProvider + ); + + const configContributors = new ClientConfigContributorFactory( + modelSchemaAdapter + ).getContributors('1.1'); + + const clientConfigGenerator = new UnifiedClientConfigGenerator( + outputRetrieval, + configContributors + ); + + await assert.rejects( + () => clientConfigGenerator.generateClientConfig(), + (error: AmplifyUserError) => { + assert.strictEqual( + error.message, + 'Stack was not created with Amplify.' + ); + assert.ok(error.resolution); + return true; + } + ); + }); + void it('throws user error if credentials are expired when getting backend outputs', async () => { const outputRetrieval = mock.fn(() => { throw new BackendOutputClientError( diff --git a/packages/client-config/src/unified_client_config_generator.ts b/packages/client-config/src/unified_client_config_generator.ts index eb8397b01b1..284bde00971 100644 --- a/packages/client-config/src/unified_client_config_generator.ts +++ b/packages/client-config/src/unified_client_config_generator.ts @@ -66,6 +66,20 @@ export class UnifiedClientConfigGenerator implements ClientConfigGenerator { error ); } + if ( + error instanceof BackendOutputClientError && + error.code === BackendOutputClientErrorType.METADATA_RETRIEVAL_ERROR + ) { + throw new AmplifyUserError( + 'NonAmplifyStackError', + { + message: 'Stack was not created with Amplify.', + resolution: + 'Ensure the CloudFormation stack ID references a main stack created with Amplify, then re-run this command.', + }, + error + ); + } if ( error instanceof BackendOutputClientError && error.code === BackendOutputClientErrorType.CREDENTIALS_ERROR From dce0518c35b21b0ba7539990e0cc983ba55c0cf4 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 19 Sep 2024 10:43:22 -0700 Subject: [PATCH 008/199] Handle parameter not found error (#2030) * Handle parameter not found error * Handle parameter not found error --- .changeset/silver-bulldogs-play.md | 5 ++++ ...secret_with_amplify_error_handling.test.ts | 27 +++++++++++++++++++ .../ssm_secret_with_amplify_error_handling.ts | 18 +++++++++++-- 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 .changeset/silver-bulldogs-play.md diff --git a/.changeset/silver-bulldogs-play.md b/.changeset/silver-bulldogs-play.md new file mode 100644 index 00000000000..25ffb0ebc50 --- /dev/null +++ b/.changeset/silver-bulldogs-play.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-secret': patch +--- + +Handle parameter not found error diff --git a/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.test.ts b/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.test.ts index 93e42d479bf..f85d327d780 100644 --- a/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.test.ts +++ b/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.test.ts @@ -62,6 +62,33 @@ void describe('getSecretClientWithAmplifyErrorHandling', () => { ); }); + void it('throws AmplifyUserError if getSecret fails due to ParameterNotFound error', async (context) => { + const notFoundError = new Error('Parameter not found error'); + notFoundError.name = 'ParameterNotFound'; + const secretsError = SecretError.createInstance(notFoundError); + context.mock.method(rawSecretClient, 'getSecret', () => { + throw secretsError; + }); + const secretName = 'testSecretName'; + await assert.rejects( + () => + classUnderTest.getSecret( + { + namespace: 'testSandboxId', + name: 'testSandboxName', + type: 'sandbox', + }, + { + name: secretName, + } + ), + new AmplifyUserError('SSMParameterNotFoundError', { + message: `Failed to get ${secretName} secret. ParameterNotFound: Parameter not found error`, + resolution: `Make sure that ${secretName} has been set. See https://docs.amplify.aws/react/deploy-and-host/fullstack-branching/secrets-and-vars/.`, + }) + ); + }); + void it('throws AmplifyFault if listSecrets fails due to a non-SSM exception other than expired credentials', async (context) => { const underlyingError = new Error('some secret error'); const secretsError = SecretError.createInstance(underlyingError); diff --git a/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.ts b/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.ts index 85e771db1c2..be3d4801aea 100644 --- a/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.ts +++ b/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.ts @@ -29,7 +29,7 @@ export class SSMSecretClientWithAmplifyErrorHandling implements SecretClient { secretIdentifier ); } catch (e) { - throw this.translateToAmplifyError(e, 'Get'); + throw this.translateToAmplifyError(e, 'Get', secretIdentifier); } }; @@ -73,7 +73,11 @@ export class SSMSecretClientWithAmplifyErrorHandling implements SecretClient { } }; - private translateToAmplifyError = (error: unknown, apiName: string) => { + private translateToAmplifyError = ( + error: unknown, + apiName: string, + secretIdentifier?: SecretIdentifier + ) => { if (error instanceof SecretError && error.cause) { if ( [ @@ -94,6 +98,16 @@ export class SSMSecretClientWithAmplifyErrorHandling implements SecretClient { 'Make sure your AWS credentials are set up correctly, refreshed and have necessary permissions to call SSM service', }); } + if ( + error.cause.name === 'ParameterNotFound' && + apiName === 'Get' && + secretIdentifier + ) { + return new AmplifyUserError('SSMParameterNotFoundError', { + message: `Failed to get ${secretIdentifier.name} secret. ${error.cause.name}: ${error.cause?.message}`, + resolution: `Make sure that ${secretIdentifier.name} has been set. See https://docs.amplify.aws/react/deploy-and-host/fullstack-branching/secrets-and-vars/.`, + }); + } let downstreamException: Error = error; if ( !(error.cause instanceof SSMServiceException) && From f33ff6b875470e8cf0135aff9082b5c18e6a59e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 21:49:31 +0000 Subject: [PATCH 009/199] Version Packages (#2023) Co-authored-by: github-actions[bot] --- .changeset/poor-owls-listen.md | 5 ----- .changeset/rich-jokes-divide.md | 5 ----- .changeset/silver-bulldogs-play.md | 5 ----- packages/backend-deployer/CHANGELOG.md | 6 ++++++ packages/backend-deployer/package.json | 2 +- packages/backend-secret/CHANGELOG.md | 6 ++++++ packages/backend-secret/package.json | 2 +- packages/client-config/CHANGELOG.md | 6 ++++++ packages/client-config/package.json | 2 +- 9 files changed, 21 insertions(+), 18 deletions(-) delete mode 100644 .changeset/poor-owls-listen.md delete mode 100644 .changeset/rich-jokes-divide.md delete mode 100644 .changeset/silver-bulldogs-play.md diff --git a/.changeset/poor-owls-listen.md b/.changeset/poor-owls-listen.md deleted file mode 100644 index f7ab95c9224..00000000000 --- a/.changeset/poor-owls-listen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/client-config': patch ---- - -Classify pointing client config generator at metadata-less stack as user error diff --git a/.changeset/rich-jokes-divide.md b/.changeset/rich-jokes-divide.md deleted file mode 100644 index 8e654162360..00000000000 --- a/.changeset/rich-jokes-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -Improve type error regex diff --git a/.changeset/silver-bulldogs-play.md b/.changeset/silver-bulldogs-play.md deleted file mode 100644 index 25ffb0ebc50..00000000000 --- a/.changeset/silver-bulldogs-play.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-secret': patch ---- - -Handle parameter not found error diff --git a/packages/backend-deployer/CHANGELOG.md b/packages/backend-deployer/CHANGELOG.md index a2427731f4e..944794ffd45 100644 --- a/packages/backend-deployer/CHANGELOG.md +++ b/packages/backend-deployer/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/backend-deployer +## 1.1.4 + +### Patch Changes + +- 98673b0: Improve type error regex + ## 1.1.3 ### Patch Changes diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index d329c432379..8e2be97e383 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-deployer", - "version": "1.1.3", + "version": "1.1.4", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/backend-secret/CHANGELOG.md b/packages/backend-secret/CHANGELOG.md index f044611a38a..495d2b9e2cf 100644 --- a/packages/backend-secret/CHANGELOG.md +++ b/packages/backend-secret/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/backend-secret +## 1.1.3 + +### Patch Changes + +- dce0518: Handle parameter not found error + ## 1.1.2 ### Patch Changes diff --git a/packages/backend-secret/package.json b/packages/backend-secret/package.json index 8972da637fe..e5e67350907 100644 --- a/packages/backend-secret/package.json +++ b/packages/backend-secret/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-secret", - "version": "1.1.2", + "version": "1.1.3", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/client-config/CHANGELOG.md b/packages/client-config/CHANGELOG.md index 9805ffc4100..4f6f163ebb1 100644 --- a/packages/client-config/CHANGELOG.md +++ b/packages/client-config/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/client-config +## 1.3.2 + +### Patch Changes + +- 603b75d: Classify pointing client config generator at metadata-less stack as user error + ## 1.3.1 ### Patch Changes diff --git a/packages/client-config/package.json b/packages/client-config/package.json index db3cc4bd9d8..242634a8c4e 100644 --- a/packages/client-config/package.json +++ b/packages/client-config/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/client-config", - "version": "1.3.1", + "version": "1.3.2", "type": "module", "publishConfig": { "access": "public" From 9e11e5ded6097454a4aa1ba7a2b77427bd9d8259 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 20 Sep 2024 09:41:18 -0700 Subject: [PATCH 010/199] correctly handle stack argument for generate schema command (#2028) * correctly handle stack argument for generate schema command * PR feedback * update type * add await * update resolver methods --- .changeset/fluffy-months-prove.md | 5 + .../backend_identifier_resolver.test.ts | 98 ++++++++++++++----- .../backend_identifier_resolver.ts | 30 +++++- ...d_identifier_with_sandbox_fallback.test.ts | 6 +- ...ackend_identifier_with_sandbox_fallback.ts | 15 ++- .../forms/generate_forms_command.test.ts | 36 ++++++- .../generate/forms/generate_forms_command.ts | 11 ++- .../generate_graphql_client_code_command.ts | 7 +- .../outputs/generate_outputs_command.ts | 7 +- .../generate_schema_command.test.ts | 8 +- .../generate_schema_command.ts | 17 ++-- 11 files changed, 181 insertions(+), 59 deletions(-) create mode 100644 .changeset/fluffy-months-prove.md diff --git a/.changeset/fluffy-months-prove.md b/.changeset/fluffy-months-prove.md new file mode 100644 index 00000000000..56eb1778f0e --- /dev/null +++ b/.changeset/fluffy-months-prove.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-cli': patch +--- + +correctly handle stack argument for generate schema command diff --git a/packages/cli/src/backend-identifier/backend_identifier_resolver.test.ts b/packages/cli/src/backend-identifier/backend_identifier_resolver.test.ts index 7493b02270a..dba0e76709e 100644 --- a/packages/cli/src/backend-identifier/backend_identifier_resolver.test.ts +++ b/packages/cli/src/backend-identifier/backend_identifier_resolver.test.ts @@ -3,38 +3,82 @@ import { describe, it } from 'node:test'; import { AppBackendIdentifierResolver } from './backend_identifier_resolver.js'; void describe('BackendIdentifierResolver', () => { - void it('returns an App Name and Branch identifier', async () => { - const backendIdResolver = new AppBackendIdentifierResolver({ - resolve: () => Promise.resolve('testAppName'), + void describe('resolveDeployedBackendIdentifier', () => { + void it('returns an App Name and Branch identifier', async () => { + const backendIdResolver = new AppBackendIdentifierResolver({ + resolve: () => Promise.resolve('testAppName'), + }); + assert.deepStrictEqual( + await backendIdResolver.resolveDeployedBackendIdentifier({ + branch: 'test', + }), + { + appName: 'testAppName', + branchName: 'test', + } + ); }); - assert.deepStrictEqual( - await backendIdResolver.resolve({ branch: 'test' }), - { - appName: 'testAppName', - branchName: 'test', - } - ); - }); - void it('returns a App Id identifier', async () => { - const backendIdResolver = new AppBackendIdentifierResolver({ - resolve: () => Promise.resolve('testAppName'), - }); - const actual = await backendIdResolver.resolve({ - appId: 'my-id', - branch: 'my-branch', + void it('returns a App Id identifier', async () => { + const backendIdResolver = new AppBackendIdentifierResolver({ + resolve: () => Promise.resolve('testAppName'), + }); + const actual = await backendIdResolver.resolveDeployedBackendIdentifier({ + appId: 'my-id', + branch: 'my-branch', + }); + assert.deepStrictEqual(actual, { + namespace: 'my-id', + name: 'my-branch', + type: 'branch', + }); }); - assert.deepStrictEqual(actual, { - namespace: 'my-id', - name: 'my-branch', - type: 'branch', + void it('returns a Stack name identifier', async () => { + const backendIdResolver = new AppBackendIdentifierResolver({ + resolve: () => Promise.resolve('testAppName'), + }); + assert.deepEqual( + await backendIdResolver.resolveDeployedBackendIdentifier({ + stack: 'my-stack', + }), + { + stackName: 'my-stack', + } + ); }); }); - void it('returns a Stack name identifier', async () => { - const backendIdResolver = new AppBackendIdentifierResolver({ - resolve: () => Promise.resolve('testAppName'), + + void describe('resolveDeployedBackendIdToBackendId', () => { + void it('returns backend identifier from App Name and Branch identifier', async () => { + const backendIdResolver = new AppBackendIdentifierResolver({ + resolve: () => Promise.resolve('testAppName'), + }); + assert.deepEqual( + await backendIdResolver.resolveBackendIdentifier({ + appId: 'testAppName', + branch: 'test', + }), + { + namespace: 'testAppName', + name: 'test', + type: 'branch', + } + ); }); - assert.deepEqual(await backendIdResolver.resolve({ stack: 'my-stack' }), { - stackName: 'my-stack', + void it('returns backend identifier from Stack identifier', async () => { + const backendIdResolver = new AppBackendIdentifierResolver({ + resolve: () => Promise.resolve('testAppName'), + }); + assert.deepEqual( + await backendIdResolver.resolveBackendIdentifier({ + stack: 'amplify-reasonableName-userName-branch-testHash', + }), + { + namespace: 'reasonableName', + name: 'userName', + type: 'branch', + hash: 'testHash', + } + ); }); }); }); diff --git a/packages/cli/src/backend-identifier/backend_identifier_resolver.ts b/packages/cli/src/backend-identifier/backend_identifier_resolver.ts index 1f0aeed51e8..0b74437a573 100644 --- a/packages/cli/src/backend-identifier/backend_identifier_resolver.ts +++ b/packages/cli/src/backend-identifier/backend_identifier_resolver.ts @@ -1,5 +1,7 @@ import { DeployedBackendIdentifier } from '@aws-amplify/deployed-backend-client'; import { NamespaceResolver } from './local_namespace_resolver.js'; +import { BackendIdentifier } from '@aws-amplify/plugin-types'; +import { BackendIdentifierConversions } from '@aws-amplify/platform-core'; export type BackendIdentifierParameters = { stack?: string; @@ -8,9 +10,12 @@ export type BackendIdentifierParameters = { }; export type BackendIdentifierResolver = { - resolve: ( + resolveDeployedBackendIdentifier: ( args: BackendIdentifierParameters ) => Promise; + resolveBackendIdentifier: ( + args: BackendIdentifierParameters + ) => Promise; }; /** @@ -22,7 +27,7 @@ export class AppBackendIdentifierResolver implements BackendIdentifierResolver { * Instantiates BackendIdentifierResolver */ constructor(private readonly namespaceResolver: NamespaceResolver) {} - resolve = async ( + resolveDeployedBackendIdentifier = async ( args: BackendIdentifierParameters ): Promise => { if (args.stack) { @@ -41,4 +46,25 @@ export class AppBackendIdentifierResolver implements BackendIdentifierResolver { } return undefined; }; + resolveBackendIdentifier = async ( + args: BackendIdentifierParameters + ): Promise => { + if (args.stack) { + return BackendIdentifierConversions.fromStackName(args.stack); + } else if (args.appId && args.branch) { + return { + namespace: args.appId, + name: args.branch, + type: 'branch', + }; + } else if (args.branch) { + return { + namespace: await this.namespaceResolver.resolve(), + name: args.branch, + type: 'branch', + }; + } + + return undefined; + }; } diff --git a/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.test.ts b/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.test.ts index acb95b9d1cd..f4dfc4155f5 100644 --- a/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.test.ts +++ b/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.test.ts @@ -15,7 +15,7 @@ void it('if backend identifier resolves without error, the resolved id is return defaultResolver, sandboxResolver ); - const resolvedId = await backendIdResolver.resolve({ + const resolvedId = await backendIdResolver.resolveDeployedBackendIdentifier({ appId: 'hello', branch: 'world', }); @@ -45,7 +45,9 @@ void it('uses the sandbox id if the default identifier resolver fails', async () defaultResolver, sandboxResolver ); - const resolvedId = await backendIdResolver.resolve({}); + const resolvedId = await backendIdResolver.resolveDeployedBackendIdentifier( + {} + ); assert.deepEqual(resolvedId, { namespace: appName, type: 'sandbox', diff --git a/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.ts b/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.ts index c7c632412a1..02a25c3ad96 100644 --- a/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.ts +++ b/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.ts @@ -20,9 +20,20 @@ export class BackendIdentifierResolverWithFallback /** * resolves the backend id, falling back to the sandbox id if there is an error */ - resolve = async (args: BackendIdentifierParameters) => { + resolveDeployedBackendIdentifier = async ( + args: BackendIdentifierParameters + ) => { return ( - (await this.defaultResolver.resolve(args)) ?? + (await this.defaultResolver.resolveDeployedBackendIdentifier(args)) ?? + (await this.fallbackResolver.resolve()) + ); + }; + /** + * Resolves deployed backend id to backend id, falling back to the sandbox id if there is an error + */ + resolveBackendIdentifier = async (args: BackendIdentifierParameters) => { + return ( + (await this.defaultResolver.resolveBackendIdentifier(args)) ?? (await this.fallbackResolver.resolve()) ); }; diff --git a/packages/cli/src/commands/generate/forms/generate_forms_command.test.ts b/packages/cli/src/commands/generate/forms/generate_forms_command.test.ts index b22f770e26b..d5bcd498c74 100644 --- a/packages/cli/src/commands/generate/forms/generate_forms_command.test.ts +++ b/packages/cli/src/commands/generate/forms/generate_forms_command.test.ts @@ -242,7 +242,14 @@ void describe('generate forms command', () => { void it('throws user error if the stack deployment is currently in progress', async () => { const fakeSandboxId = 'my-fake-app-my-fake-username'; const backendIdResolver = { - resolve: mock.fn(() => + resolveDeployedBackendIdentifier: mock.fn(() => + Promise.resolve({ + namespace: fakeSandboxId, + name: fakeSandboxId, + type: 'sandbox', + }) + ), + resolveBackendIdentifier: mock.fn(() => Promise.resolve({ namespace: fakeSandboxId, name: fakeSandboxId, @@ -289,7 +296,14 @@ void describe('generate forms command', () => { void it('throws user error if the stack does not exist', async () => { const fakeSandboxId = 'my-fake-app-my-fake-username'; const backendIdResolver = { - resolve: mock.fn(() => + resolveDeployedBackendIdentifier: mock.fn(() => + Promise.resolve({ + namespace: fakeSandboxId, + name: fakeSandboxId, + type: 'sandbox', + }) + ), + resolveBackendIdentifier: mock.fn(() => Promise.resolve({ namespace: fakeSandboxId, name: fakeSandboxId, @@ -333,7 +347,14 @@ void describe('generate forms command', () => { void it('throws user error if credentials are expired when getting backend outputs', async () => { const fakeSandboxId = 'my-fake-app-my-fake-username'; const backendIdResolver = { - resolve: mock.fn(() => + resolveDeployedBackendIdentifier: mock.fn(() => + Promise.resolve({ + namespace: fakeSandboxId, + name: fakeSandboxId, + type: 'sandbox', + }) + ), + resolveBackendIdentifier: mock.fn(() => Promise.resolve({ namespace: fakeSandboxId, name: fakeSandboxId, @@ -380,7 +401,14 @@ void describe('generate forms command', () => { void it('throws user error if access is denied when getting backend outputs', async () => { const fakeSandboxId = 'my-fake-app-my-fake-username'; const backendIdResolver = { - resolve: mock.fn(() => + resolveDeployedBackendIdentifier: mock.fn(() => + Promise.resolve({ + namespace: fakeSandboxId, + name: fakeSandboxId, + type: 'sandbox', + }) + ), + resolveBackendIdentifier: mock.fn(() => Promise.resolve({ namespace: fakeSandboxId, name: fakeSandboxId, diff --git a/packages/cli/src/commands/generate/forms/generate_forms_command.ts b/packages/cli/src/commands/generate/forms/generate_forms_command.ts index 63afe6aaea8..3c407da4886 100644 --- a/packages/cli/src/commands/generate/forms/generate_forms_command.ts +++ b/packages/cli/src/commands/generate/forms/generate_forms_command.ts @@ -52,7 +52,9 @@ export class GenerateFormsCommand } getBackendIdentifier = async (args: GenerateFormsCommandOptions) => { - return await this.backendIdentifierResolver.resolve(args); + return await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( + args + ); }; /** @@ -61,9 +63,10 @@ export class GenerateFormsCommand handler = async ( args: ArgumentsCamelCase ): Promise => { - const backendIdentifier = await this.backendIdentifierResolver.resolve( - args - ); + const backendIdentifier = + await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( + args + ); if (!backendIdentifier) { throw new Error('Could not resolve the backend identifier'); diff --git a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts index 287f3deb37e..ec8e4a6ed7e 100644 --- a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts +++ b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts @@ -89,9 +89,10 @@ export class GenerateGraphqlClientCodeCommand handler = async ( args: ArgumentsCamelCase ): Promise => { - const backendIdentifier = await this.backendIdentifierResolver.resolve( - args - ); + const backendIdentifier = + await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( + args + ); const out = this.getOutDir(args); const format = args.format ?? GenerateApiCodeFormat.GRAPHQL_CODEGEN; const formatParams = this.formatParamBuilders[format](args); diff --git a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts index 2feb82e7eea..0a6e2921064 100644 --- a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts +++ b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts @@ -54,9 +54,10 @@ export class GenerateOutputsCommand handler = async ( args: ArgumentsCamelCase ): Promise => { - const backendIdentifier = await this.backendIdentifierResolver.resolve( - args - ); + const backendIdentifier = + await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( + args + ); if (!backendIdentifier) { throw new Error('Could not resolve the backend identifier'); diff --git a/packages/cli/src/commands/generate/schema-from-database/generate_schema_command.test.ts b/packages/cli/src/commands/generate/schema-from-database/generate_schema_command.test.ts index 1144459a6f7..6fbb0ffdf8a 100644 --- a/packages/cli/src/commands/generate/schema-from-database/generate_schema_command.test.ts +++ b/packages/cli/src/commands/generate/schema-from-database/generate_schema_command.test.ts @@ -117,9 +117,15 @@ void describe('generate graphql-client-code command', () => { void it('generates and writes schema for stack', async () => { await commandRunner.runCommand( - 'schema-from-database --stack stack_name --connection-uri-secret CONN_STRING --out schema.rds.ts' + 'schema-from-database --stack amplify-reasonableName-userName-sandbox-testHash --connection-uri-secret CONN_STRING --out schema.rds.ts' ); assert.equal(secretClientGetSecret.mock.callCount(), 1); + assert.deepEqual(secretClientGetSecret.mock.calls[0].arguments[0], { + namespace: 'reasonableName', + name: 'userName', + type: 'sandbox', + hash: 'testHash', + }); assert.equal(schemaGeneratorGenerateMethod.mock.callCount(), 1); assert.deepEqual(schemaGeneratorGenerateMethod.mock.calls[0].arguments[0], { connectionUri: { diff --git a/packages/cli/src/commands/generate/schema-from-database/generate_schema_command.ts b/packages/cli/src/commands/generate/schema-from-database/generate_schema_command.ts index 56c79d3cbda..9aaefd9bc92 100644 --- a/packages/cli/src/commands/generate/schema-from-database/generate_schema_command.ts +++ b/packages/cli/src/commands/generate/schema-from-database/generate_schema_command.ts @@ -2,7 +2,6 @@ import { ArgumentsCamelCase, Argv, CommandModule } from 'yargs'; import { BackendIdentifierResolver } from '../../../backend-identifier/backend_identifier_resolver.js'; import { ArgumentsKebabCase } from '../../../kebab_case.js'; import { SecretClient } from '@aws-amplify/backend-secret'; -import { BackendIdentifier } from '@aws-amplify/plugin-types'; import { SchemaGenerator } from '@aws-amplify/schema-generator'; import { AmplifyFault } from '@aws-amplify/platform-core'; @@ -54,9 +53,8 @@ export class GenerateSchemaCommand handler = async ( args: ArgumentsCamelCase ): Promise => { - const backendIdentifier = await this.backendIdentifierResolver.resolve( - args - ); + const backendIdentifier = + await this.backendIdentifierResolver.resolveBackendIdentifier(args); if (!backendIdentifier) { throw new AmplifyFault('BackendIdentifierFault', { @@ -68,7 +66,7 @@ export class GenerateSchemaCommand const outputFile = args.out as string; const connectionUriSecret = await this.secretClient.getSecret( - backendIdentifier as BackendIdentifier, + backendIdentifier, { name: connectionUriSecretName, } @@ -77,12 +75,9 @@ export class GenerateSchemaCommand const sslCertSecretName = args.sslCertSecret as string; let sslCertSecret; if (sslCertSecretName) { - sslCertSecret = await this.secretClient.getSecret( - backendIdentifier as BackendIdentifier, - { - name: sslCertSecretName, - } - ); + sslCertSecret = await this.secretClient.getSecret(backendIdentifier, { + name: sslCertSecretName, + }); } await this.schemaGenerator.generate({ From 87dbf411b61177b94e2ab62c0c8699df0f7027c0 Mon Sep 17 00:00:00 2001 From: Leena <81589006+ShadowCat567@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:38:35 -0400 Subject: [PATCH 011/199] Exposing stack parameter at higher level for backend and related resources (#2021) * test with adding tags * testing stack change * implementation attempts * revert accidental package-lock change * added stack param to function resource * added changeset * api updates * Revert "api updates" This reverts commit 80c981e0103d8e3cf13994a8abd6a2350f85cf24. * adjusted getting stack param for auth * attempt to fix auth stack param * api updates * trying to make api happy * Revert "api updates" This reverts commit 79678cc2d9c794694117c2c531e470b1160a4091. * removed comment * update to exposed stack for auth, function, and backend * exposed stack for storage * update changesets * updated storage stack param * api updates * added tests to backend, auth, storage, and function * updated factory tests for function * updates to factory test for auth * updates to api * trying to make api happy * stack parameter exposed for data * updated changeset * api update * reverted exposing stack param for data * Revert "api update" This reverts commit 558692f44b97010ad661be8e15780b079b4e6a01. * removed changeset that is not needed * Update .changeset/chatty-beans-begin.md Co-authored-by: Kamil Sobol * update changeset, variable name changes * minor adjustment to backend-function factory test --------- Co-authored-by: Vieltojarvi Co-authored-by: Kamil Sobol --- .changeset/chatty-beans-begin.md | 8 ++++ .changeset/four-drinks-yell.md | 5 +++ packages/backend-auth/API.md | 3 +- packages/backend-auth/src/factory.test.ts | 17 ++++---- packages/backend-auth/src/factory.ts | 7 +++- packages/backend-function/API.md | 3 +- packages/backend-function/src/factory.test.ts | 42 ++++++++++--------- packages/backend-function/src/factory.ts | 7 +++- packages/backend-storage/API.md | 3 +- packages/backend-storage/src/construct.ts | 5 ++- packages/backend-storage/src/factory.test.ts | 10 +++++ packages/backend-storage/src/factory.ts | 3 +- packages/backend/API.md | 1 + packages/backend/src/backend.ts | 1 + packages/backend/src/backend_factory.test.ts | 5 +++ packages/backend/src/backend_factory.ts | 3 ++ .../src/define_backend_template_harness.ts | 6 ++- packages/plugin-types/API.md | 5 +++ packages/plugin-types/src/index.ts | 1 + packages/plugin-types/src/stack_provider.ts | 5 +++ 20 files changed, 103 insertions(+), 37 deletions(-) create mode 100644 .changeset/chatty-beans-begin.md create mode 100644 .changeset/four-drinks-yell.md create mode 100644 packages/plugin-types/src/stack_provider.ts diff --git a/.changeset/chatty-beans-begin.md b/.changeset/chatty-beans-begin.md new file mode 100644 index 00000000000..7e7be6e9662 --- /dev/null +++ b/.changeset/chatty-beans-begin.md @@ -0,0 +1,8 @@ +--- +'@aws-amplify/backend-function': minor +'@aws-amplify/backend-auth': minor +'@aws-amplify/backend': minor +'@aws-amplify/backend-storage': minor +--- + +expose stack property for backend, function resource, storage resource, and auth resource diff --git a/.changeset/four-drinks-yell.md b/.changeset/four-drinks-yell.md new file mode 100644 index 00000000000..99b9d3f2001 --- /dev/null +++ b/.changeset/four-drinks-yell.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/plugin-types': minor +--- + +add new type to handle exposing stack diff --git a/packages/backend-auth/API.md b/packages/backend-auth/API.md index b3cf7a91cd2..f9b6247cedf 100644 --- a/packages/backend-auth/API.md +++ b/packages/backend-auth/API.md @@ -20,6 +20,7 @@ import { OidcProviderProps } from '@aws-amplify/auth-construct'; import { ResourceAccessAcceptor } from '@aws-amplify/plugin-types'; import { ResourceAccessAcceptorFactory } from '@aws-amplify/plugin-types'; import { ResourceProvider } from '@aws-amplify/plugin-types'; +import { StackProvider } from '@aws-amplify/plugin-types'; import { TriggerEvent } from '@aws-amplify/auth-construct'; // @public @@ -77,7 +78,7 @@ export type AuthLoginWithFactoryProps = Omit & ResourceAccessAcceptorFactory; +export type BackendAuth = ResourceProvider & ResourceAccessAcceptorFactory & StackProvider; // @public export const defineAuth: (props: AmplifyAuthProps) => ConstructFactory; diff --git a/packages/backend-auth/src/factory.test.ts b/packages/backend-auth/src/factory.test.ts index 3865fdcec40..bbf285b7cd8 100644 --- a/packages/backend-auth/src/factory.test.ts +++ b/packages/backend-auth/src/factory.test.ts @@ -79,12 +79,15 @@ void describe('AmplifyAuthFactory', () => { assert.strictEqual(instance1, instance2); }); + void it('verifies stack property exists and is equivalent to auth stack', () => { + const backendAuth = authFactory.getInstance(getInstanceProps); + assert.equal(backendAuth.stack, Stack.of(backendAuth.resources.userPool)); + }); + void it('adds construct to stack', () => { const backendAuth = authFactory.getInstance(getInstanceProps); - const template = Template.fromStack( - Stack.of(backendAuth.resources.userPool) - ); + const template = Template.fromStack(backendAuth.stack); template.resourceCountIs('AWS::Cognito::UserPool', 1); }); @@ -98,9 +101,7 @@ void describe('AmplifyAuthFactory', () => { const backendAuth = authFactory.getInstance(getInstanceProps); - const template = Template.fromStack( - Stack.of(backendAuth.resources.userPool) - ); + const template = Template.fromStack(backendAuth.stack); template.resourceCountIs('AWS::Cognito::UserPool', 1); template.hasResourceProperties('AWS::Cognito::UserPool', { @@ -247,9 +248,7 @@ void describe('AmplifyAuthFactory', () => { const backendAuth = authWithTriggerFactory.getInstance(getInstanceProps); - const template = Template.fromStack( - Stack.of(backendAuth.resources.userPool) - ); + const template = Template.fromStack(backendAuth.stack); template.hasResourceProperties('AWS::Cognito::UserPool', { LambdaConfig: { // The key in the CFN template is the trigger event name with the first character uppercase diff --git a/packages/backend-auth/src/factory.ts b/packages/backend-auth/src/factory.ts index c5184cb7439..02edd4695c4 100644 --- a/packages/backend-auth/src/factory.ts +++ b/packages/backend-auth/src/factory.ts @@ -18,6 +18,7 @@ import { ResourceAccessAcceptor, ResourceAccessAcceptorFactory, ResourceProvider, + StackProvider, } from '@aws-amplify/plugin-types'; import { translateToAuthConstructLoginWith } from './translate_auth_props.js'; import { authAccessBuilder as _authAccessBuilder } from './access_builder.js'; @@ -28,10 +29,11 @@ import { Expand, } from './types.js'; import { UserPoolAccessPolicyFactory } from './userpool_access_policy_factory.js'; -import { Tags } from 'aws-cdk-lib'; +import { Stack, Tags } from 'aws-cdk-lib'; export type BackendAuth = ResourceProvider & - ResourceAccessAcceptorFactory; + ResourceAccessAcceptorFactory & + StackProvider; export type AmplifyAuthProps = Expand< Omit & { @@ -195,6 +197,7 @@ class AmplifyAuthGenerator implements ConstructContainerEntryGenerator { policy.attachToRole(role); }, }), + stack: Stack.of(authConstruct), }; if (!this.props.access) { return authConstructMixin; diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index 86a98d7e1e6..bf82df50b04 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -9,6 +9,7 @@ import { ConstructFactory } from '@aws-amplify/plugin-types'; import { FunctionResources } from '@aws-amplify/plugin-types'; import { ResourceAccessAcceptorFactory } from '@aws-amplify/plugin-types'; import { ResourceProvider } from '@aws-amplify/plugin-types'; +import { StackProvider } from '@aws-amplify/plugin-types'; // @public (undocumented) export type AddEnvironmentFactory = { @@ -19,7 +20,7 @@ export type AddEnvironmentFactory = { export type CronSchedule = `${string} ${string} ${string} ${string} ${string}` | `${string} ${string} ${string} ${string} ${string} ${string}`; // @public -export const defineFunction: (props?: FunctionProps) => ConstructFactory & ResourceAccessAcceptorFactory & AddEnvironmentFactory>; +export const defineFunction: (props?: FunctionProps) => ConstructFactory & ResourceAccessAcceptorFactory & AddEnvironmentFactory & StackProvider>; // @public (undocumented) export type FunctionProps = { diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index 316271131e1..477ce69aec6 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -59,10 +59,16 @@ void describe('AmplifyFunctionFactory', () => { assert.strictEqual(instance1, instance2); }); + void it('verifies stack property exists and is equal to function stack', () => { + const functionFactory = defaultLambda; + const lambda = functionFactory.getInstance(getInstanceProps); + assert.equal(lambda.stack, Stack.of(lambda.resources.lambda)); + }); + void it('resolves default name and entry when no args specified', () => { const functionFactory = defaultLambda; const lambda = functionFactory.getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); template.resourceCountIs('AWS::Lambda::Function', 1); template.hasResourceProperties('AWS::Lambda::Function', { Handler: 'index.handler', @@ -79,7 +85,7 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', }); const lambda = functionFactory.getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); template.resourceCountIs('AWS::Lambda::Function', 1); template.hasResourceProperties('AWS::Lambda::Function', { Handler: 'index.handler', @@ -96,7 +102,7 @@ void describe('AmplifyFunctionFactory', () => { name: 'myCoolLambda', }); const lambda = functionFactory.getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); template.resourceCountIs('AWS::Lambda::Function', 1); template.hasResourceProperties('AWS::Lambda::Function', { Handler: 'index.handler', @@ -113,7 +119,7 @@ void describe('AmplifyFunctionFactory', () => { name: 'myCoolLambda', }); const lambda = functionFactory.getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); template.resourceCountIs('AWS::Lambda::Function', 1); template.hasResourceProperties('AWS::Lambda::Function', { Tags: [{ Key: 'amplify:friendly-name', Value: 'myCoolLambda' }], @@ -137,7 +143,7 @@ void describe('AmplifyFunctionFactory', () => { void it('builds lambda with local and 3p dependencies', () => { const lambda = lambdaWithDependencies.getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); // There isn't a way to check the contents of the bundled lambda using the CDK Template utility // So we just check that the lambda was created properly in the CFN template. // There is an e2e test that validates proper lambda bundling @@ -159,7 +165,7 @@ void describe('AmplifyFunctionFactory', () => { }); const lambda = functionFactory.getInstance(getInstanceProps); lambda.addEnvironment('key1', 'value1'); - const stack = Stack.of(lambda.resources.lambda); + const stack = lambda.stack; const template = Template.fromStack(stack); template.resourceCountIs('AWS::Lambda::Function', 1); template.hasResourceProperties('AWS::Lambda::Function', { @@ -177,7 +183,7 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', timeoutSeconds: 10, }).getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); template.hasResourceProperties('AWS::Lambda::Function', { Timeout: 10, @@ -230,7 +236,7 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', memoryMB: 234, }).getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); template.hasResourceProperties('AWS::Lambda::Function', { MemorySize: 234, @@ -241,7 +247,7 @@ void describe('AmplifyFunctionFactory', () => { const lambda = defineFunction({ entry: './test-assets/default-lambda/handler.ts', }).getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); template.hasResourceProperties('AWS::Lambda::Function', { MemorySize: 512, @@ -294,7 +300,7 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', runtime: 16, }).getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); template.hasResourceProperties('AWS::Lambda::Function', { Runtime: Runtime.NODEJS_16_X.name, @@ -305,7 +311,7 @@ void describe('AmplifyFunctionFactory', () => { const lambda = defineFunction({ entry: './test-assets/default-lambda/handler.ts', }).getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); template.hasResourceProperties('AWS::Lambda::Function', { Runtime: Runtime.NODEJS_18_X.name, @@ -340,7 +346,7 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', schedule: 'every 5m', }).getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); template.hasResourceProperties('AWS::Events::Rule', { ScheduleExpression: 'cron(*/5 * * * ? *)', @@ -361,7 +367,7 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', schedule: '0 1 * * ?', }).getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); template.hasResourceProperties('AWS::Events::Rule', { ScheduleExpression: 'cron(0 1 * * ? *)', @@ -382,7 +388,7 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', schedule: ['0 1 * * ?', 'every 5m'], }).getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); template.resourceCountIs('AWS::Events::Rule', 2); @@ -399,7 +405,7 @@ void describe('AmplifyFunctionFactory', () => { const lambda = defineFunction({ entry: './test-assets/default-lambda/handler.ts', }).getInstance(getInstanceProps); - const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + const template = Template.fromStack(lambda.stack); template.resourceCountIs('AWS::Events::Rule', 0); }); @@ -412,7 +418,7 @@ void describe('AmplifyFunctionFactory', () => { name: 'myCoolLambda', }); const lambda = functionFactory.getInstance(getInstanceProps); - const stack = Stack.of(lambda.resources.lambda); + const stack = lambda.stack; const policy = new Policy(stack, 'testPolicy', { statements: [ new PolicyStatement({ @@ -503,9 +509,7 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', name: 'anotherName', }); - const functionStack = Stack.of( - functionFactory.getInstance(getInstanceProps).resources.lambda - ); + const functionStack = functionFactory.getInstance(getInstanceProps).stack; anotherFunction.getInstance(getInstanceProps); const template = Template.fromStack(functionStack); assert.equal( diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 0b184ddda27..46e66c15c47 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -11,6 +11,7 @@ import { ResourceNameValidator, ResourceProvider, SsmEnvironmentEntry, + StackProvider, } from '@aws-amplify/plugin-types'; import { Construct } from 'constructs'; import { NodejsFunction, OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs'; @@ -64,7 +65,8 @@ export const defineFunction = ( ): ConstructFactory< ResourceProvider & ResourceAccessAcceptorFactory & - AddEnvironmentFactory + AddEnvironmentFactory & + StackProvider > => new FunctionFactory(props, new Error().stack); export type FunctionProps = { @@ -308,6 +310,7 @@ class AmplifyFunction AddEnvironmentFactory { readonly resources: FunctionResources; + readonly stack: Stack; private readonly functionEnvironmentTranslator: FunctionEnvironmentTranslator; constructor( scope: Construct, @@ -318,6 +321,8 @@ class AmplifyFunction ) { super(scope, id); + this.stack = Stack.of(scope); + const runtime = nodeVersionMap[props.runtime]; const require = createRequire(import.meta.url); diff --git a/packages/backend-storage/API.md b/packages/backend-storage/API.md index e7472b5770f..aa65ada47b2 100644 --- a/packages/backend-storage/API.md +++ b/packages/backend-storage/API.md @@ -14,6 +14,7 @@ import { IBucket } from 'aws-cdk-lib/aws-s3'; import { ResourceAccessAcceptor } from '@aws-amplify/plugin-types'; import { ResourceAccessAcceptorFactory } from '@aws-amplify/plugin-types'; import { ResourceProvider } from '@aws-amplify/plugin-types'; +import { StackProvider } from '@aws-amplify/plugin-types'; import { StorageOutput } from '@aws-amplify/backend-output-schemas'; // @public (undocumented) @@ -34,7 +35,7 @@ export type AmplifyStorageProps = { export type AmplifyStorageTriggerEvent = 'onDelete' | 'onUpload'; // @public -export const defineStorage: (props: AmplifyStorageFactoryProps) => ConstructFactory>; +export const defineStorage: (props: AmplifyStorageFactoryProps) => ConstructFactory & StackProvider>; // @public export type EntityId = 'identity'; diff --git a/packages/backend-storage/src/construct.ts b/packages/backend-storage/src/construct.ts index 1cbdb716701..d31f0b97ebc 100644 --- a/packages/backend-storage/src/construct.ts +++ b/packages/backend-storage/src/construct.ts @@ -12,6 +12,7 @@ import { ConstructFactory, FunctionResources, ResourceProvider, + StackProvider, } from '@aws-amplify/plugin-types'; import { StorageOutput } from '@aws-amplify/backend-output-schemas'; import { RemovalPolicy, Stack } from 'aws-cdk-lib'; @@ -77,8 +78,9 @@ export type StorageResources = { */ export class AmplifyStorage extends Construct - implements ResourceProvider + implements ResourceProvider, StackProvider { + readonly stack: Stack; readonly resources: StorageResources; readonly isDefault: boolean; readonly name: string; @@ -89,6 +91,7 @@ export class AmplifyStorage super(scope, id); this.isDefault = props.isDefault || false; this.name = props.name; + this.stack = Stack.of(scope); const bucketProps: BucketProps = { versioned: props.versioned || false, diff --git a/packages/backend-storage/src/factory.test.ts b/packages/backend-storage/src/factory.test.ts index d3f425a6d2b..0ffd504d67a 100644 --- a/packages/backend-storage/src/factory.test.ts +++ b/packages/backend-storage/src/factory.test.ts @@ -149,6 +149,16 @@ void describe('AmplifyStorageFactory', () => { }; }); + void it('verifies stack property exists and is equal to storage stack', () => { + const storageConstruct = defineStorage({ name: 'testName' }).getInstance( + getInstanceProps + ); + assert.equal( + storageConstruct.stack, + Stack.of(storageConstruct.resources.bucket) + ); + }); + void it('if more than one default bucket, throw', () => { storageFactory = defineStorage({ name: 'testName', isDefault: true }); storageFactory2 = defineStorage({ name: 'testName2', isDefault: true }); diff --git a/packages/backend-storage/src/factory.ts b/packages/backend-storage/src/factory.ts index 49eff9ba1e6..4c5212c9649 100644 --- a/packages/backend-storage/src/factory.ts +++ b/packages/backend-storage/src/factory.ts @@ -3,6 +3,7 @@ import { ConstructFactory, ConstructFactoryGetInstanceProps, ResourceProvider, + StackProvider, } from '@aws-amplify/plugin-types'; import * as path from 'path'; import { AmplifyStorage, StorageResources } from './construct.js'; @@ -74,5 +75,5 @@ export class AmplifyStorageFactory */ export const defineStorage = ( props: AmplifyStorageFactoryProps -): ConstructFactory> => +): ConstructFactory & StackProvider> => new AmplifyStorageFactory(props, new Error().stack); diff --git a/packages/backend/API.md b/packages/backend/API.md index 1cb83ee09f3..ba53fec1756 100644 --- a/packages/backend/API.md +++ b/packages/backend/API.md @@ -49,6 +49,7 @@ export type Backend = BackendBase & { export type BackendBase = { createStack: (name: string) => Stack; addOutput: (clientConfigPart: DeepPartialAmplifyGeneratedConfigs) => void; + stack: Stack; }; export { BackendOutputEntry } diff --git a/packages/backend/src/backend.ts b/packages/backend/src/backend.ts index a4e767ada23..bd2d5d3d2f7 100644 --- a/packages/backend/src/backend.ts +++ b/packages/backend/src/backend.ts @@ -12,6 +12,7 @@ export type BackendBase = { addOutput: ( clientConfigPart: DeepPartialAmplifyGeneratedConfigs ) => void; + stack: Stack; }; // Type that allows construct factories to be defined using any keys except those used in BackendHelpers diff --git a/packages/backend/src/backend_factory.test.ts b/packages/backend/src/backend_factory.test.ts index 4016e50b782..268bede3ffe 100644 --- a/packages/backend/src/backend_factory.test.ts +++ b/packages/backend/src/backend_factory.test.ts @@ -148,6 +148,11 @@ void describe('Backend', () => { ); }); + void it('verifies stack property exists and is equivalent to rootStack', () => { + const backend = new BackendFactory({}, rootStack); + assert.equal(rootStack, backend.stack); + }); + void it('stores attribution metadata in root stack', () => { new BackendFactory({}, rootStack); const rootStackTemplate = Template.fromStack(rootStack); diff --git a/packages/backend/src/backend_factory.ts b/packages/backend/src/backend_factory.ts index d9bf2f545a3..023f52244f6 100644 --- a/packages/backend/src/backend_factory.ts +++ b/packages/backend/src/backend_factory.ts @@ -49,6 +49,7 @@ export class BackendFactory< [K in keyof T]: ReturnType; }; + readonly stack; private readonly stackResolver: StackResolver; private readonly customOutputsAccumulator: CustomOutputsAccumulator; /** @@ -56,6 +57,7 @@ export class BackendFactory< * If no CDK App is specified a new one is created */ constructor(constructFactories: T, stack: Stack = createDefaultStack()) { + this.stack = stack; new AttributionMetadataStorage().storeAttributionMetadata( stack, rootStackTypeIdentifier, @@ -157,5 +159,6 @@ export const defineBackend = ( ...backend.resources, createStack: backend.createStack, addOutput: backend.addOutput, + stack: backend.stack, }; }; diff --git a/packages/integration-tests/src/define_backend_template_harness.ts b/packages/integration-tests/src/define_backend_template_harness.ts index 455c8b0cf64..ae484e4b9ef 100644 --- a/packages/integration-tests/src/define_backend_template_harness.ts +++ b/packages/integration-tests/src/define_backend_template_harness.ts @@ -66,10 +66,14 @@ const backendTemplatesCollector: SynthesizeBackendTemplates = < } as Partial<{ [K in keyof T]: Template }> & { root: Template }; for (const [key, resourceRecord] of Object.entries(backend)) { - // skip over the methods that we add on to the backend object + // skip over the properties and methods that we add on to the backend object if (typeof resourceRecord === 'function') { continue; } + // skip non-resource properties + if (!('resources' in resourceRecord)) { + continue; + } // find some construct in the resources exposed by the resourceRecord const firstConstruct = Object.values(resourceRecord.resources).find( (value) => value instanceof Construct diff --git a/packages/plugin-types/API.md b/packages/plugin-types/API.md index 58b1302c884..97bf48886ae 100644 --- a/packages/plugin-types/API.md +++ b/packages/plugin-types/API.md @@ -238,6 +238,11 @@ export type StableBackendIdentifiers = { getStableBackendHash: () => string; }; +// @public (undocumented) +export type StackProvider = { + stack: Stack; +}; + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/plugin-types/src/index.ts b/packages/plugin-types/src/index.ts index 3b8c7bf18e8..ee50df55a3c 100644 --- a/packages/plugin-types/src/index.ts +++ b/packages/plugin-types/src/index.ts @@ -20,3 +20,4 @@ export * from './deep_partial.js'; export * from './stable_backend_identifiers.js'; export * from './resource_name_validator.js'; export * from './aws_client_provider.js'; +export * from './stack_provider.js'; diff --git a/packages/plugin-types/src/stack_provider.ts b/packages/plugin-types/src/stack_provider.ts new file mode 100644 index 00000000000..1c88482f7b2 --- /dev/null +++ b/packages/plugin-types/src/stack_provider.ts @@ -0,0 +1,5 @@ +import { Stack } from 'aws-cdk-lib'; + +export type StackProvider = { + stack: Stack; +}; From 7aad5e82b089a708061b33fc15aad3a53fdff089 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 20 Sep 2024 11:40:46 -0700 Subject: [PATCH 012/199] update fallback for backend id resolvers if stack, app id, or branch are in args (#2034) * update fallback for backend id resolvers if stack, app id, or branch are in args * pr feedback --- .changeset/itchy-vans-behave.md | 5 ++++ ...d_identifier_with_sandbox_fallback.test.ts | 27 ++++++++++++++++++- ...ackend_identifier_with_sandbox_fallback.ts | 22 ++++++++------- .../generate/forms/generate_forms_command.ts | 6 ++++- .../outputs/generate_outputs_command.ts | 7 ++++- .../generate_schema_command.ts | 8 +++--- 6 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 .changeset/itchy-vans-behave.md diff --git a/.changeset/itchy-vans-behave.md b/.changeset/itchy-vans-behave.md new file mode 100644 index 00000000000..7d823ef8380 --- /dev/null +++ b/.changeset/itchy-vans-behave.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-cli': patch +--- + +update fallback for backend id resolvers if stack, app id, or branch are in args diff --git a/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.test.ts b/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.test.ts index f4dfc4155f5..0509b4e80b9 100644 --- a/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.test.ts +++ b/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.test.ts @@ -26,7 +26,7 @@ void it('if backend identifier resolves without error, the resolved id is return }); }); -void it('uses the sandbox id if the default identifier resolver fails', async () => { +void it('uses the sandbox id if the default identifier resolver fails and there is no stack, appId or branch in args', async () => { const appName = 'testAppName'; const namespaceResolver = { resolve: () => Promise.resolve(appName), @@ -54,3 +54,28 @@ void it('uses the sandbox id if the default identifier resolver fails', async () name: 'test-user', }); }); + +void it('does not use sandbox id if the default identifier resolver fails and there is stack, appId or branch in args', async () => { + const appName = 'testAppName'; + const namespaceResolver = { + resolve: () => Promise.resolve(appName), + }; + + const defaultResolver = new AppBackendIdentifierResolver(namespaceResolver); + const username = 'test-user'; + const sandboxResolver = new SandboxBackendIdResolver( + namespaceResolver, + () => + ({ + username, + } as never) + ); + const backendIdResolver = new BackendIdentifierResolverWithFallback( + defaultResolver, + sandboxResolver + ); + const resolvedId = await backendIdResolver.resolveDeployedBackendIdentifier({ + appId: 'testAppName', + }); + assert.deepEqual(resolvedId, undefined); +}); diff --git a/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.ts b/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.ts index 02a25c3ad96..f1bcbd2808a 100644 --- a/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.ts +++ b/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.ts @@ -18,23 +18,25 @@ export class BackendIdentifierResolverWithFallback private fallbackResolver: SandboxBackendIdResolver ) {} /** - * resolves the backend id, falling back to the sandbox id if there is an error + * resolves to deployed backend id, falling back to the sandbox id if stack or appId and branch inputs are not provided */ resolveDeployedBackendIdentifier = async ( args: BackendIdentifierParameters ) => { - return ( - (await this.defaultResolver.resolveDeployedBackendIdentifier(args)) ?? - (await this.fallbackResolver.resolve()) - ); + if (args.stack || args.appId || args.branch) { + return this.defaultResolver.resolveDeployedBackendIdentifier(args); + } + + return this.fallbackResolver.resolve(); }; /** - * Resolves deployed backend id to backend id, falling back to the sandbox id if there is an error + * Resolves deployed backend id to backend id, falling back to the sandbox id if stack or appId and branch inputs are not provided */ resolveBackendIdentifier = async (args: BackendIdentifierParameters) => { - return ( - (await this.defaultResolver.resolveBackendIdentifier(args)) ?? - (await this.fallbackResolver.resolve()) - ); + if (args.stack || args.appId || args.branch) { + return this.defaultResolver.resolveBackendIdentifier(args); + } + + return this.fallbackResolver.resolve(); }; } diff --git a/packages/cli/src/commands/generate/forms/generate_forms_command.ts b/packages/cli/src/commands/generate/forms/generate_forms_command.ts index 3c407da4886..34667029dc3 100644 --- a/packages/cli/src/commands/generate/forms/generate_forms_command.ts +++ b/packages/cli/src/commands/generate/forms/generate_forms_command.ts @@ -69,7 +69,11 @@ export class GenerateFormsCommand ); if (!backendIdentifier) { - throw new Error('Could not resolve the backend identifier'); + throw new AmplifyUserError('BackendIdentifierResolverError', { + message: 'Could not resolve the backend identifier.', + resolution: + 'Ensure stack name or Amplify App ID and branch specified are correct and exists, then re-run this command.', + }); } const backendOutputClient = this.backendOutputClientBuilder(); diff --git a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts index 0a6e2921064..0edb427a00e 100644 --- a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts +++ b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts @@ -8,6 +8,7 @@ import { import { BackendIdentifierResolver } from '../../../backend-identifier/backend_identifier_resolver.js'; import { ClientConfigGeneratorAdapter } from '../../../client-config/client_config_generator_adapter.js'; import { ArgumentsKebabCase } from '../../../kebab_case.js'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; export type GenerateOutputsCommandOptions = ArgumentsKebabCase; @@ -60,7 +61,11 @@ export class GenerateOutputsCommand ); if (!backendIdentifier) { - throw new Error('Could not resolve the backend identifier'); + throw new AmplifyUserError('BackendIdentifierResolverError', { + message: 'Could not resolve the backend identifier.', + resolution: + 'Ensure stack name or Amplify App ID and branch specified are correct and exists, then re-run this command.', + }); } await this.clientConfigGenerator.generateClientConfigToFile( diff --git a/packages/cli/src/commands/generate/schema-from-database/generate_schema_command.ts b/packages/cli/src/commands/generate/schema-from-database/generate_schema_command.ts index 9aaefd9bc92..a0896a7b30e 100644 --- a/packages/cli/src/commands/generate/schema-from-database/generate_schema_command.ts +++ b/packages/cli/src/commands/generate/schema-from-database/generate_schema_command.ts @@ -3,7 +3,7 @@ import { BackendIdentifierResolver } from '../../../backend-identifier/backend_i import { ArgumentsKebabCase } from '../../../kebab_case.js'; import { SecretClient } from '@aws-amplify/backend-secret'; import { SchemaGenerator } from '@aws-amplify/schema-generator'; -import { AmplifyFault } from '@aws-amplify/platform-core'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; const DEFAULT_OUTPUT = 'amplify/data/schema.sql.ts'; @@ -57,8 +57,10 @@ export class GenerateSchemaCommand await this.backendIdentifierResolver.resolveBackendIdentifier(args); if (!backendIdentifier) { - throw new AmplifyFault('BackendIdentifierFault', { - message: 'Could not resolve the backend identifier', + throw new AmplifyUserError('BackendIdentifierResolverError', { + message: 'Could not resolve the backend identifier.', + resolution: + 'Ensure stack name or Amplify App ID and branch specified are correct and exists, then re-run this command.', }); } From e325044cd07ce466a41e4fec72d8747217e62f41 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Fri, 20 Sep 2024 13:31:27 -0700 Subject: [PATCH 013/199] Prefer amplify errors in generators (#2035) --- .changeset/forty-squids-help.md | 7 +++++++ packages/eslint-rules/src/index.ts | 6 +++--- .../src/local_codegen_graphql_form_generator.ts | 2 ++ .../form-generator/src/s3_string_object_fetcher.ts | 1 + .../src/create_graphql_document_generator.ts | 3 +++ .../src/create_graphql_models_generator.ts | 5 +++++ .../src/create_graphql_types_generator.ts | 3 +++ packages/model-generator/src/generate_api_code.ts | 1 + .../src/get_backend_output_with_error_handling.ts | 4 ---- .../src/graphql_document_generator.ts | 1 + .../model-generator/src/graphql_models_generator.ts | 1 + .../model-generator/src/graphql_types_generator.ts | 1 + .../model-generator/src/s3_string_object_fetcher.ts | 1 + .../schema-generator/src/generate_schema.test.ts | 10 ++++++++++ packages/schema-generator/src/generate_schema.ts | 13 +++++++++---- 15 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 .changeset/forty-squids-help.md diff --git a/.changeset/forty-squids-help.md b/.changeset/forty-squids-help.md new file mode 100644 index 00000000000..a4395a47549 --- /dev/null +++ b/.changeset/forty-squids-help.md @@ -0,0 +1,7 @@ +--- +'@aws-amplify/schema-generator': patch +'@aws-amplify/model-generator': patch +'@aws-amplify/form-generator': patch +--- + +Prefer amplify errors in generators diff --git a/packages/eslint-rules/src/index.ts b/packages/eslint-rules/src/index.ts index 11cc1b7ab16..88ee3632028 100644 --- a/packages/eslint-rules/src/index.ts +++ b/packages/eslint-rules/src/index.ts @@ -27,6 +27,9 @@ export const configs = { 'packages/backend-auth/src/**', 'packages/backend-deployer/src/**', 'packages/create-amplify/src/**', + 'packages/form-generator/src/**', + 'packages/model-generator/src/**', + 'packages/schema-generator/src/**', ], excludedFiles: ['**/*.test.ts'], rules: { @@ -39,9 +42,6 @@ export const configs = { 'packages/ai-constructs/src/**', 'packages/backend-output-storage/src/**', 'packages/deployed-backend-client/src/**', - 'packages/form-generator/src/**', - 'packages/model-generator/src/**', - 'packages/schema-generator/src/**', ], rules: { 'amplify-backend-rules/no-amplify-errors': 'error', diff --git a/packages/form-generator/src/local_codegen_graphql_form_generator.ts b/packages/form-generator/src/local_codegen_graphql_form_generator.ts index 9f98f919436..1d268581bf7 100644 --- a/packages/form-generator/src/local_codegen_graphql_form_generator.ts +++ b/packages/form-generator/src/local_codegen_graphql_form_generator.ts @@ -222,11 +222,13 @@ export class LocalGraphqlFormGenerator implements GraphqlFormGenerator { ([key]) => key.toLowerCase() === model.toLowerCase() ); if (!entry) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error(`Could not find specified model ${model}`); } prev.push(entry); return prev; } + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error(`Could not find specified model ${model}`); }, [] diff --git a/packages/form-generator/src/s3_string_object_fetcher.ts b/packages/form-generator/src/s3_string_object_fetcher.ts index 0b29d0e6c28..3ae37595923 100644 --- a/packages/form-generator/src/s3_string_object_fetcher.ts +++ b/packages/form-generator/src/s3_string_object_fetcher.ts @@ -19,6 +19,7 @@ export class S3StringObjectFetcher { ); const schema = await getSchemaCommandResult.Body?.transformToString(); if (!schema) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error('Error on parsing output schema'); } return schema; diff --git a/packages/model-generator/src/create_graphql_document_generator.ts b/packages/model-generator/src/create_graphql_document_generator.ts index 2de7652a8f2..b2a6b3aef4d 100644 --- a/packages/model-generator/src/create_graphql_document_generator.ts +++ b/packages/model-generator/src/create_graphql_document_generator.ts @@ -29,9 +29,11 @@ export const createGraphqlDocumentGenerator = ({ awsClientProvider, }: GraphqlDocumentGeneratorFactoryParams): GraphqlDocumentGenerator => { if (!backendIdentifier) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error('`backendIdentifier` must be defined'); } if (!awsClientProvider) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error('`awsClientProvider` must be defined'); } @@ -44,6 +46,7 @@ export const createGraphqlDocumentGenerator = ({ ); const apiId = output[graphqlOutputKey]?.payload.awsAppsyncApiId; if (!apiId) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error(`Unable to determine AppSync API ID.`); } diff --git a/packages/model-generator/src/create_graphql_models_generator.ts b/packages/model-generator/src/create_graphql_models_generator.ts index b0f8d0cd085..c1a2920242a 100644 --- a/packages/model-generator/src/create_graphql_models_generator.ts +++ b/packages/model-generator/src/create_graphql_models_generator.ts @@ -61,9 +61,11 @@ const createGraphqlModelsGeneratorFromBackendIdentifier = ({ awsClientProvider, }: GraphqlModelsFromBackendIdentifierParams): GraphqlModelsGenerator => { if (!backendIdentifier) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error('`backendIdentifier` must be defined'); } if (!awsClientProvider) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error('`awsClientProvider` must be defined'); } @@ -90,9 +92,11 @@ export const createGraphqlModelsFromS3UriGenerator = ({ awsClientProvider, }: GraphqlModelsFromS3UriGeneratorFactoryParams): GraphqlModelsGenerator => { if (!modelSchemaS3Uri) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error('`modelSchemaS3Uri` must be defined'); } if (!awsClientProvider) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error('`awsClientProvider` must be defined'); } return new StackMetadataGraphqlModelsGenerator( @@ -118,6 +122,7 @@ const getModelSchema = async ( const modelSchemaS3Uri = output[graphqlOutputKey]?.payload.amplifyApiModelSchemaS3Uri; if (!modelSchemaS3Uri) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error(`Cannot find model schema at amplifyApiModelSchemaS3Uri`); } diff --git a/packages/model-generator/src/create_graphql_types_generator.ts b/packages/model-generator/src/create_graphql_types_generator.ts index a6c72479709..17f0e799e20 100644 --- a/packages/model-generator/src/create_graphql_types_generator.ts +++ b/packages/model-generator/src/create_graphql_types_generator.ts @@ -29,9 +29,11 @@ export const createGraphqlTypesGenerator = ({ awsClientProvider, }: GraphqlTypesGeneratorFactoryParams): GraphqlTypesGenerator => { if (!backendIdentifier) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error('`backendIdentifier` must be defined'); } if (!awsClientProvider) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error('`awsClientProvider` must be defined'); } @@ -44,6 +46,7 @@ export const createGraphqlTypesGenerator = ({ ); const apiId = output[graphqlOutputKey]?.payload.awsAppsyncApiId; if (!apiId) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error(`Unable to determine AppSync API ID.`); } diff --git a/packages/model-generator/src/generate_api_code.ts b/packages/model-generator/src/generate_api_code.ts index 29ad2fa7664..43ca3864334 100644 --- a/packages/model-generator/src/generate_api_code.ts +++ b/packages/model-generator/src/generate_api_code.ts @@ -145,6 +145,7 @@ export class ApiCodeGenerator { return this.generateIntrospectionApiCode(); } default: + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error( `${ (props as GenerateApiCodeProps).format as string diff --git a/packages/model-generator/src/get_backend_output_with_error_handling.ts b/packages/model-generator/src/get_backend_output_with_error_handling.ts index 1c5e9feb846..98fb6ffc703 100644 --- a/packages/model-generator/src/get_backend_output_with_error_handling.ts +++ b/packages/model-generator/src/get_backend_output_with_error_handling.ts @@ -20,7 +20,6 @@ export const getBackendOutputWithErrorHandling = async ( error instanceof BackendOutputClientError && error.code === BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS ) { - // eslint-disable-next-line amplify-backend-rules/no-amplify-errors throw new AmplifyUserError( 'DeploymentInProgressError', { @@ -34,7 +33,6 @@ export const getBackendOutputWithErrorHandling = async ( error instanceof BackendOutputClientError && error.code === BackendOutputClientErrorType.NO_STACK_FOUND ) { - // eslint-disable-next-line amplify-backend-rules/no-amplify-errors throw new AmplifyUserError( 'StackDoesNotExistError', { @@ -49,7 +47,6 @@ export const getBackendOutputWithErrorHandling = async ( error instanceof BackendOutputClientError && error.code === BackendOutputClientErrorType.CREDENTIALS_ERROR ) { - // eslint-disable-next-line amplify-backend-rules/no-amplify-errors throw new AmplifyUserError( 'CredentialsError', { @@ -64,7 +61,6 @@ export const getBackendOutputWithErrorHandling = async ( error instanceof BackendOutputClientError && error.code === BackendOutputClientErrorType.ACCESS_DENIED ) { - // eslint-disable-next-line amplify-backend-rules/no-amplify-errors throw new AmplifyUserError( 'AccessDeniedError', { diff --git a/packages/model-generator/src/graphql_document_generator.ts b/packages/model-generator/src/graphql_document_generator.ts index 82ee5c33c08..8ac6dbff5cc 100644 --- a/packages/model-generator/src/graphql_document_generator.ts +++ b/packages/model-generator/src/graphql_document_generator.ts @@ -27,6 +27,7 @@ export class AppSyncGraphqlDocumentGenerator const schema = await this.fetchSchema(); if (!schema) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error('Invalid schema'); } diff --git a/packages/model-generator/src/graphql_models_generator.ts b/packages/model-generator/src/graphql_models_generator.ts index 534c2af8c02..d116e2b33f8 100644 --- a/packages/model-generator/src/graphql_models_generator.ts +++ b/packages/model-generator/src/graphql_models_generator.ts @@ -33,6 +33,7 @@ export class StackMetadataGraphqlModelsGenerator const schema = await this.fetchSchema(); if (!schema) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error('Invalid schema'); } diff --git a/packages/model-generator/src/graphql_types_generator.ts b/packages/model-generator/src/graphql_types_generator.ts index 33c9285b5d9..38a9c7e6715 100644 --- a/packages/model-generator/src/graphql_types_generator.ts +++ b/packages/model-generator/src/graphql_types_generator.ts @@ -30,6 +30,7 @@ export class AppSyncGraphqlTypesGenerator implements GraphqlTypesGenerator { const schema = await this.fetchSchema(); if (!schema) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error('Invalid schema'); } diff --git a/packages/model-generator/src/s3_string_object_fetcher.ts b/packages/model-generator/src/s3_string_object_fetcher.ts index 0b29d0e6c28..3ae37595923 100644 --- a/packages/model-generator/src/s3_string_object_fetcher.ts +++ b/packages/model-generator/src/s3_string_object_fetcher.ts @@ -19,6 +19,7 @@ export class S3StringObjectFetcher { ); const schema = await getSchemaCommandResult.Body?.transformToString(); if (!schema) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors throw new Error('Error on parsing output schema'); } return schema; diff --git a/packages/schema-generator/src/generate_schema.test.ts b/packages/schema-generator/src/generate_schema.test.ts index e281560cddb..4680b567643 100644 --- a/packages/schema-generator/src/generate_schema.test.ts +++ b/packages/schema-generator/src/generate_schema.test.ts @@ -154,4 +154,14 @@ void describe('SchemaGenerator', () => { 'Unable to parse the database URL. One or more parts of the database URL is missing. Missing [username, password].', }); }); + + void it('should throw error if database engine is incorrect', async () => { + const parse = () => + parseDatabaseUrl('incorrect://user:password@test-host-name/db'); + assert.throws(parse, { + name: 'DatabaseUrlParseError', + message: + 'Unable to parse the database URL. Unsupported database engine: incorrect', + }); + }); }); diff --git a/packages/schema-generator/src/generate_schema.ts b/packages/schema-generator/src/generate_schema.ts index bfe5e28ea8f..4f26e9ff5f7 100644 --- a/packages/schema-generator/src/generate_schema.ts +++ b/packages/schema-generator/src/generate_schema.ts @@ -16,6 +16,7 @@ export type SchemaGeneratorConfig = { type AmplifyGenerateSchemaError = | 'DatabaseConnectionError' + | 'DatabaseUnsupportedEngineError' | 'DatabaseUrlParseError'; /** @@ -39,7 +40,6 @@ export class SchemaGenerator { } catch (err) { const databaseError = err as DatabaseConnectError; if (databaseError.code === 'ETIMEDOUT') { - // eslint-disable-next-line amplify-backend-rules/no-amplify-errors throw new AmplifyUserError( 'DatabaseConnectionError', { @@ -118,7 +118,6 @@ export const parseDatabaseUrl = (databaseUrl: string): SQLDataSourceConfig => { ).filter((part) => !config[part]); if (missingParts.length > 0) { - // eslint-disable-next-line amplify-backend-rules/no-amplify-errors throw new AmplifyUserError( 'DatabaseUrlParseError', { @@ -134,7 +133,6 @@ export const parseDatabaseUrl = (databaseUrl: string): SQLDataSourceConfig => { return config; } catch (err) { const error = err as Error; - // eslint-disable-next-line amplify-backend-rules/no-amplify-errors throw new AmplifyUserError( 'DatabaseUrlParseError', { @@ -153,7 +151,14 @@ const constructDBEngine = (engine: string): SQLEngine => { case 'postgres': return 'postgresql'; default: - throw new Error(`Unsupported database engine: ${engine}`); + throw new AmplifyUserError( + 'DatabaseUnsupportedEngineError', + { + message: `Unsupported database engine: ${engine}`, + resolution: + 'Ensure that database URL specifies supported engine. Supported engines are "mysql", "postgresql", "postgres".', + } + ); } }; From f6b1943b42d2e1669109f3411ff3c19bae14a2c0 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Fri, 20 Sep 2024 14:39:39 -0700 Subject: [PATCH 014/199] Handle schema errors (#2036) --- .changeset/spicy-toys-scream.md | 5 ++ .../src/generate_schema.test.ts | 52 +++++++++++++++++++ .../schema-generator/src/generate_schema.ts | 21 +++++++- 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 .changeset/spicy-toys-scream.md diff --git a/.changeset/spicy-toys-scream.md b/.changeset/spicy-toys-scream.md new file mode 100644 index 00000000000..8d7d5505d65 --- /dev/null +++ b/.changeset/spicy-toys-scream.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/schema-generator': patch +--- + +Handle schema errors diff --git a/packages/schema-generator/src/generate_schema.test.ts b/packages/schema-generator/src/generate_schema.test.ts index 4680b567643..625cdb40771 100644 --- a/packages/schema-generator/src/generate_schema.test.ts +++ b/packages/schema-generator/src/generate_schema.test.ts @@ -2,10 +2,13 @@ import { beforeEach, describe, it, mock } from 'node:test'; import { SchemaGenerator, parseDatabaseUrl } from './generate_schema.js'; import assert from 'node:assert'; import { + EmptySchemaError, + InvalidSchemaError, TypescriptDataSchemaGenerator, TypescriptDataSchemaGeneratorConfig, } from '@aws-amplify/graphql-schema-generator'; import fs from 'fs/promises'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; const mockGenerateMethod = mock.fn<(config: TypescriptDataSchemaGeneratorConfig) => Promise>(); @@ -164,4 +167,53 @@ void describe('SchemaGenerator', () => { 'Unable to parse the database URL. Unsupported database engine: incorrect', }); }); + + void it('should throw error if database schema is incorrect', async () => { + mockGenerateMethod.mock.mockImplementationOnce(() => { + throw new InvalidSchemaError([{}], ['missingColumn']); + }); + const schemaGenerator = new SchemaGenerator(); + await assert.rejects( + () => + schemaGenerator.generate({ + connectionUri: { + secretName: 'FAKE_SECRET_NAME', + value: 'mysql://user:password@hostname:3306/db', + }, + out: 'schema.ts', + }), + (error: AmplifyUserError) => { + assert.strictEqual(error.name, 'DatabaseSchemaError'); + assert.strictEqual( + error.message, + 'Imported SQL schema is invalid. Imported schema is missing columns: missingColumn' + ); + assert.strictEqual(error.resolution, 'Check the database schema.'); + return true; + } + ); + }); + + void it('should throw error if database schema is empty', async () => { + mockGenerateMethod.mock.mockImplementationOnce(() => { + throw new EmptySchemaError(); + }); + const schemaGenerator = new SchemaGenerator(); + await assert.rejects( + () => + schemaGenerator.generate({ + connectionUri: { + secretName: 'FAKE_SECRET_NAME', + value: 'mysql://user:password@hostname:3306/db', + }, + out: 'schema.ts', + }), + (error: AmplifyUserError) => { + assert.strictEqual(error.name, 'DatabaseSchemaError'); + assert.strictEqual(error.message, 'Imported SQL schema is empty.'); + assert.strictEqual(error.resolution, 'Check the database schema.'); + return true; + } + ); + }); }); diff --git a/packages/schema-generator/src/generate_schema.ts b/packages/schema-generator/src/generate_schema.ts index 4f26e9ff5f7..49fc857b862 100644 --- a/packages/schema-generator/src/generate_schema.ts +++ b/packages/schema-generator/src/generate_schema.ts @@ -1,4 +1,8 @@ -import { TypescriptDataSchemaGenerator } from '@aws-amplify/graphql-schema-generator'; +import { + EmptySchemaError, + InvalidSchemaError, + TypescriptDataSchemaGenerator, +} from '@aws-amplify/graphql-schema-generator'; import fs from 'fs/promises'; import { AmplifyUserError } from '@aws-amplify/platform-core'; @@ -16,6 +20,7 @@ export type SchemaGeneratorConfig = { type AmplifyGenerateSchemaError = | 'DatabaseConnectionError' + | 'DatabaseSchemaError' | 'DatabaseUnsupportedEngineError' | 'DatabaseUrlParseError'; @@ -38,6 +43,20 @@ export class SchemaGenerator { }); await fs.writeFile(props.out, schema); } catch (err) { + if ( + err instanceof EmptySchemaError || + err instanceof InvalidSchemaError + ) { + throw new AmplifyUserError( + 'DatabaseSchemaError', + { + // the message already contains descriptive error. + message: err.message, + resolution: 'Check the database schema.', + }, + err + ); + } const databaseError = err as DatabaseConnectError; if (databaseError.code === 'ETIMEDOUT') { throw new AmplifyUserError( From 0d8f5b36003efd69415a4feadc4734997d4b0fe7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 22:12:41 +0000 Subject: [PATCH 015/199] Version Packages (#2033) Co-authored-by: github-actions[bot] --- .changeset/chatty-beans-begin.md | 8 -------- .changeset/fluffy-months-prove.md | 5 ----- .changeset/forty-squids-help.md | 7 ------- .changeset/four-drinks-yell.md | 5 ----- .changeset/itchy-vans-behave.md | 5 ----- .changeset/spicy-toys-scream.md | 5 ----- packages/backend-auth/CHANGELOG.md | 11 +++++++++++ packages/backend-auth/package.json | 4 ++-- packages/backend-function/CHANGELOG.md | 11 +++++++++++ packages/backend-function/package.json | 4 ++-- packages/backend-storage/CHANGELOG.md | 11 +++++++++++ packages/backend-storage/package.json | 4 ++-- packages/backend/CHANGELOG.md | 15 +++++++++++++++ packages/backend/package.json | 10 +++++----- packages/cli/CHANGELOG.md | 14 ++++++++++++++ packages/cli/package.json | 10 +++++----- packages/form-generator/CHANGELOG.md | 6 ++++++ packages/form-generator/package.json | 2 +- packages/model-generator/CHANGELOG.md | 8 ++++++++ packages/model-generator/package.json | 4 ++-- packages/plugin-types/CHANGELOG.md | 6 ++++++ packages/plugin-types/package.json | 2 +- packages/schema-generator/CHANGELOG.md | 7 +++++++ packages/schema-generator/package.json | 2 +- 24 files changed, 110 insertions(+), 56 deletions(-) delete mode 100644 .changeset/chatty-beans-begin.md delete mode 100644 .changeset/fluffy-months-prove.md delete mode 100644 .changeset/forty-squids-help.md delete mode 100644 .changeset/four-drinks-yell.md delete mode 100644 .changeset/itchy-vans-behave.md delete mode 100644 .changeset/spicy-toys-scream.md diff --git a/.changeset/chatty-beans-begin.md b/.changeset/chatty-beans-begin.md deleted file mode 100644 index 7e7be6e9662..00000000000 --- a/.changeset/chatty-beans-begin.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@aws-amplify/backend-function': minor -'@aws-amplify/backend-auth': minor -'@aws-amplify/backend': minor -'@aws-amplify/backend-storage': minor ---- - -expose stack property for backend, function resource, storage resource, and auth resource diff --git a/.changeset/fluffy-months-prove.md b/.changeset/fluffy-months-prove.md deleted file mode 100644 index 56eb1778f0e..00000000000 --- a/.changeset/fluffy-months-prove.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-cli': patch ---- - -correctly handle stack argument for generate schema command diff --git a/.changeset/forty-squids-help.md b/.changeset/forty-squids-help.md deleted file mode 100644 index a4395a47549..00000000000 --- a/.changeset/forty-squids-help.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@aws-amplify/schema-generator': patch -'@aws-amplify/model-generator': patch -'@aws-amplify/form-generator': patch ---- - -Prefer amplify errors in generators diff --git a/.changeset/four-drinks-yell.md b/.changeset/four-drinks-yell.md deleted file mode 100644 index 99b9d3f2001..00000000000 --- a/.changeset/four-drinks-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/plugin-types': minor ---- - -add new type to handle exposing stack diff --git a/.changeset/itchy-vans-behave.md b/.changeset/itchy-vans-behave.md deleted file mode 100644 index 7d823ef8380..00000000000 --- a/.changeset/itchy-vans-behave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-cli': patch ---- - -update fallback for backend id resolvers if stack, app id, or branch are in args diff --git a/.changeset/spicy-toys-scream.md b/.changeset/spicy-toys-scream.md deleted file mode 100644 index 8d7d5505d65..00000000000 --- a/.changeset/spicy-toys-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/schema-generator': patch ---- - -Handle schema errors diff --git a/packages/backend-auth/CHANGELOG.md b/packages/backend-auth/CHANGELOG.md index 0cee4996550..8aa5b2b4a36 100644 --- a/packages/backend-auth/CHANGELOG.md +++ b/packages/backend-auth/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend-auth +## 1.2.0 + +### Minor Changes + +- 87dbf41: expose stack property for backend, function resource, storage resource, and auth resource + +### Patch Changes + +- Updated dependencies [87dbf41] + - @aws-amplify/plugin-types@1.3.0 + ## 1.1.5 ### Patch Changes diff --git a/packages/backend-auth/package.json b/packages/backend-auth/package.json index 68c0b590190..c9bddfe7c90 100644 --- a/packages/backend-auth/package.json +++ b/packages/backend-auth/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-auth", - "version": "1.1.5", + "version": "1.2.0", "type": "module", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "dependencies": { "@aws-amplify/auth-construct": "^1.3.1", "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/plugin-types": "^1.2.2" + "@aws-amplify/plugin-types": "^1.3.0" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.5", diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index a8f9cfe8cd8..6ef7160de58 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend-function +## 1.5.0 + +### Minor Changes + +- 87dbf41: expose stack property for backend, function resource, storage resource, and auth resource + +### Patch Changes + +- Updated dependencies [87dbf41] + - @aws-amplify/plugin-types@1.3.0 + ## 1.4.1 ### Patch Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index c30cb8017a1..097ad1f416f 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.4.1", + "version": "1.5.0", "type": "module", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/plugin-types": "^1.3.0", "execa": "^8.0.1" }, "devDependencies": { diff --git a/packages/backend-storage/CHANGELOG.md b/packages/backend-storage/CHANGELOG.md index 3f1b5f28039..117fdf5186d 100644 --- a/packages/backend-storage/CHANGELOG.md +++ b/packages/backend-storage/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend-storage +## 1.2.0 + +### Minor Changes + +- 87dbf41: expose stack property for backend, function resource, storage resource, and auth resource + +### Patch Changes + +- Updated dependencies [87dbf41] + - @aws-amplify/plugin-types@1.3.0 + ## 1.1.3 ### Patch Changes diff --git a/packages/backend-storage/package.json b/packages/backend-storage/package.json index 7d4abbf78a5..913852c427f 100644 --- a/packages/backend-storage/package.json +++ b/packages/backend-storage/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-storage", - "version": "1.1.3", + "version": "1.2.0", "type": "module", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/plugin-types": "^1.2.2" + "@aws-amplify/plugin-types": "^1.3.0" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.5", diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 2173352b44b..47fbc092bc8 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @aws-amplify/backend +## 1.3.0 + +### Minor Changes + +- 87dbf41: expose stack property for backend, function resource, storage resource, and auth resource + +### Patch Changes + +- Updated dependencies [87dbf41] +- Updated dependencies [87dbf41] + - @aws-amplify/backend-function@1.5.0 + - @aws-amplify/backend-auth@1.2.0 + - @aws-amplify/backend-storage@1.2.0 + - @aws-amplify/plugin-types@1.3.0 + ## 1.2.2 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index d05dc6f45fd..d2c31853fb4 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.2.2", + "version": "1.3.0", "type": "module", "publishConfig": { "access": "public" @@ -26,16 +26,16 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/data-schema": "^1.0.0", - "@aws-amplify/backend-auth": "^1.1.5", - "@aws-amplify/backend-function": "^1.4.1", + "@aws-amplify/backend-auth": "^1.2.0", + "@aws-amplify/backend-function": "^1.5.0", "@aws-amplify/backend-data": "^1.1.4", "@aws-amplify/backend-output-schemas": "^1.2.0", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/backend-storage": "^1.1.3", + "@aws-amplify/backend-storage": "^1.2.0", "@aws-amplify/client-config": "^1.3.1", "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/plugin-types": "^1.3.0", "@aws-sdk/client-amplify": "^3.624.0", "lodash.snakecase": "^4.1.1" }, diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 231b451546b..a28a6bf3749 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,19 @@ # @aws-amplify/backend-cli +## 1.2.8 + +### Patch Changes + +- 9e11e5d: correctly handle stack argument for generate schema command +- 7aad5e8: update fallback for backend id resolvers if stack, app id, or branch are in args +- Updated dependencies [e325044] +- Updated dependencies [87dbf41] +- Updated dependencies [f6b1943] + - @aws-amplify/schema-generator@1.2.4 + - @aws-amplify/model-generator@1.0.8 + - @aws-amplify/form-generator@1.0.3 + - @aws-amplify/plugin-types@1.3.0 + ## 1.2.7 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 373c854cd51..84f45f654e7 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-cli", - "version": "1.2.7", + "version": "1.2.8", "description": "Command line interface for various Amplify tools", "bin": { "ampx": "lib/ampx.js", @@ -37,12 +37,12 @@ "@aws-amplify/cli-core": "^1.1.3", "@aws-amplify/client-config": "^1.3.1", "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/form-generator": "^1.0.2", - "@aws-amplify/model-generator": "^1.0.7", + "@aws-amplify/form-generator": "^1.0.3", + "@aws-amplify/model-generator": "^1.0.8", "@aws-amplify/platform-core": "^1.0.5", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/plugin-types": "^1.3.0", "@aws-amplify/sandbox": "^1.2.2", - "@aws-amplify/schema-generator": "^1.2.3", + "@aws-amplify/schema-generator": "^1.2.4", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", diff --git a/packages/form-generator/CHANGELOG.md b/packages/form-generator/CHANGELOG.md index 77772684396..b09cd2d4edc 100644 --- a/packages/form-generator/CHANGELOG.md +++ b/packages/form-generator/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/form-generator +## 1.0.3 + +### Patch Changes + +- e325044: Prefer amplify errors in generators + ## 1.0.2 ### Patch Changes diff --git a/packages/form-generator/package.json b/packages/form-generator/package.json index 7fd3600d6b2..31a56527203 100644 --- a/packages/form-generator/package.json +++ b/packages/form-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/form-generator", - "version": "1.0.2", + "version": "1.0.3", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/model-generator/CHANGELOG.md b/packages/model-generator/CHANGELOG.md index 36e27a7a4e0..80adb901230 100644 --- a/packages/model-generator/CHANGELOG.md +++ b/packages/model-generator/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/model-generator +## 1.0.8 + +### Patch Changes + +- e325044: Prefer amplify errors in generators +- Updated dependencies [87dbf41] + - @aws-amplify/plugin-types@1.3.0 + ## 1.0.7 ### Patch Changes diff --git a/packages/model-generator/package.json b/packages/model-generator/package.json index 11d9f92cd9e..b45b00a62a4 100644 --- a/packages/model-generator/package.json +++ b/packages/model-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/model-generator", - "version": "1.0.7", + "version": "1.0.8", "type": "module", "publishConfig": { "access": "public" @@ -24,7 +24,7 @@ "@aws-amplify/graphql-generator": "^0.4.0", "@aws-amplify/graphql-types-generator": "^3.6.0", "@aws-amplify/platform-core": "^1.0.5", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/plugin-types": "^1.3.0", "@aws-sdk/client-appsync": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/credential-providers": "^3.624.0", diff --git a/packages/plugin-types/CHANGELOG.md b/packages/plugin-types/CHANGELOG.md index c77e6874de0..552d3102783 100644 --- a/packages/plugin-types/CHANGELOG.md +++ b/packages/plugin-types/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/plugin-types +## 1.3.0 + +### Minor Changes + +- 87dbf41: add new type to handle exposing stack + ## 1.2.2 ### Patch Changes diff --git a/packages/plugin-types/package.json b/packages/plugin-types/package.json index 4b928b3f114..70711832d6a 100644 --- a/packages/plugin-types/package.json +++ b/packages/plugin-types/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/plugin-types", - "version": "1.2.2", + "version": "1.3.0", "types": "lib/index.d.ts", "type": "commonjs", "publishConfig": { diff --git a/packages/schema-generator/CHANGELOG.md b/packages/schema-generator/CHANGELOG.md index e18465fe2e1..f74d158e93e 100644 --- a/packages/schema-generator/CHANGELOG.md +++ b/packages/schema-generator/CHANGELOG.md @@ -1,5 +1,12 @@ # @aws-amplify/schema-generator +## 1.2.4 + +### Patch Changes + +- e325044: Prefer amplify errors in generators +- f6b1943: Handle schema errors + ## 1.2.3 ### Patch Changes diff --git a/packages/schema-generator/package.json b/packages/schema-generator/package.json index 4e569c34d17..7ee91355ad0 100644 --- a/packages/schema-generator/package.json +++ b/packages/schema-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/schema-generator", - "version": "1.2.3", + "version": "1.2.4", "type": "module", "publishConfig": { "access": "public" From c99636f26e1eac755da5c902f49640222da3b681 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 23 Sep 2024 09:53:12 -0700 Subject: [PATCH 016/199] Use global lock for auth credential access in tests. (#2038) * Use global lock for auth credential access. * add timeout --- .changeset/seven-rabbits-poke.md | 2 ++ .github/workflows/health_checks.yml | 1 + .../src/amplify_auth_credentials_factory.ts | 19 ++++++++++--------- 3 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 .changeset/seven-rabbits-poke.md diff --git a/.changeset/seven-rabbits-poke.md b/.changeset/seven-rabbits-poke.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/seven-rabbits-poke.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/workflows/health_checks.yml b/.github/workflows/health_checks.yml index 36dd88d9346..e9aafec01aa 100644 --- a/.github/workflows/health_checks.yml +++ b/.github/workflows/health_checks.yml @@ -436,6 +436,7 @@ jobs: check_package_versions: if: github.event_name == 'pull_request' runs-on: ubuntu-latest + timeout-minutes: 10 needs: - install steps: diff --git a/packages/integration-tests/src/amplify_auth_credentials_factory.ts b/packages/integration-tests/src/amplify_auth_credentials_factory.ts index a08b1455bd4..90773c67117 100644 --- a/packages/integration-tests/src/amplify_auth_credentials_factory.ts +++ b/packages/integration-tests/src/amplify_auth_credentials_factory.ts @@ -16,16 +16,17 @@ import { AsyncLock } from './async_lock.js'; * This class is safe to use in concurrent settings, i.e. tests running in parallel. */ export class AmplifyAuthCredentialsFactory { - private readonly userPoolId: string; - private readonly userPoolClientId: string; - private readonly identityPoolId: string; - private readonly allowGuestAccess: boolean | undefined; /** * Asynchronous lock is used to assure that all calls to Amplify JS library are * made in single transaction. This is because that library maintains global state, * for example auth session. */ - private readonly lock: AsyncLock = new AsyncLock(60 * 1000); + private static readonly lock: AsyncLock = new AsyncLock(60 * 1000); + + private readonly userPoolId: string; + private readonly userPoolClientId: string; + private readonly identityPoolId: string; + private readonly allowGuestAccess: boolean | undefined; /** * Creates Amplify Auth credentials factory. @@ -47,7 +48,7 @@ export class AmplifyAuthCredentialsFactory { iamCredentials: IamCredentials; accessToken: string; }> => { - await this.lock.acquire(); + await AmplifyAuthCredentialsFactory.lock.acquire(); try { const username = `amplify-backend-${shortUuid()}@amazon.com`; const temporaryPassword = `Test1@Temp${shortUuid()}`; @@ -103,12 +104,12 @@ export class AmplifyAuthCredentialsFactory { accessToken: authSession.tokens.accessToken.toString(), }; } finally { - this.lock.release(); + AmplifyAuthCredentialsFactory.lock.release(); } }; getGuestAccessCredentials = async (): Promise => { - await this.lock.acquire(); + await AmplifyAuthCredentialsFactory.lock.acquire(); try { Amplify.configure({ Auth: { @@ -131,7 +132,7 @@ export class AmplifyAuthCredentialsFactory { return authSession.credentials; } finally { - this.lock.release(); + AmplifyAuthCredentialsFactory.lock.release(); } }; } From 80e493eb0b0e0a13b14e8c6d1437f602602e6640 Mon Sep 17 00:00:00 2001 From: Leena <81589006+ShadowCat567@users.noreply.github.com> Date: Tue, 24 Sep 2024 14:40:23 -0400 Subject: [PATCH 017/199] `@aws-amplify/backend` should inherit version bump of same kind from dependencies (#2042) * refactor check_changeset_completeness with functions * check backend version bump attempt 1 with temp workaround * testing * Revert "testing" This reverts commit 65c6794a0e7bb5c578bab63ffdd656b8b9d3f558. * attempt 2 at checking changesets * testing new attempt * removed testing changesets * testing * testing changes * testing * removed unnecessary comments * explictly add return type to modifiedPackages function * added changeset types to dev dep * altered logic of checkBackendDepVersion * altered naming * moved missing changesets check to function --------- Co-authored-by: Vieltojarvi --- package-lock.json | 1 + package.json | 1 + scripts/check_changeset_completeness.ts | 155 ++++++++++++++++++------ 3 files changed, 122 insertions(+), 35 deletions(-) diff --git a/package-lock.json b/package-lock.json index 35127fe7aa4..ba819efd320 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "@aws-sdk/client-ssm": "^3.624.0", "@changesets/cli": "^2.26.1", "@changesets/get-release-plan": "^4.0.0", + "@changesets/types": "^6.0.0", "@microsoft/api-extractor": "7.43.8", "@octokit/webhooks-types": "^7.5.1", "@shopify/eslint-plugin": "^43.0.0", diff --git a/package.json b/package.json index ff4526d10a9..4a0fb2be0f0 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "@aws-sdk/client-ssm": "^3.624.0", "@changesets/cli": "^2.26.1", "@changesets/get-release-plan": "^4.0.0", + "@changesets/types": "^6.0.0", "@microsoft/api-extractor": "7.43.8", "@octokit/webhooks-types": "^7.5.1", "@shopify/eslint-plugin": "^43.0.0", diff --git a/scripts/check_changeset_completeness.ts b/scripts/check_changeset_completeness.ts index 0dbd34d7f9f..28cc61a3be2 100644 --- a/scripts/check_changeset_completeness.ts +++ b/scripts/check_changeset_completeness.ts @@ -2,51 +2,136 @@ import getReleasePlan from '@changesets/get-release-plan'; import { GitClient } from './components/git_client.js'; import { readPackageJson } from './components/package-json/package_json.js'; import { EOL } from 'os'; +import { ReleasePlan, VersionType } from '@changesets/types'; -const gitClient = new GitClient(); - -const baseRef = process.argv[2]; -if (baseRef === undefined) { - throw new Error('No base ref specified for changeset completeness check'); +enum VersionTypeEnum { + 'NONE' = 0, + 'PATCH' = 1, + 'MINOR' = 2, + 'MAJOR' = 3, } -const releasePlan = await getReleasePlan(process.cwd()); +const checkForMissingChangesets = async ( + releasePlan: ReleasePlan, + gitClient: GitClient, + baseRef: string +) => { + const packagesWithChangeset = new Set( + releasePlan.releases.map((release) => release.name) + ); -const packagesWithChangeset = new Set( - releasePlan.releases.map((release) => release.name) -); + const changedFiles = await gitClient.getChangedFiles(baseRef); + const modifiedPackageDirs = new Set(); -const changedFiles = await gitClient.getChangedFiles(baseRef); + changedFiles + .filter( + (changedFile) => + changedFile.startsWith('packages/') && !changedFile.endsWith('test.ts') + ) + .forEach((changedPackageFile) => { + modifiedPackageDirs.add( + changedPackageFile.split('/').slice(0, 2).join('/') + ); + }); -const modifiedPackageDirs = new Set(); + const packagesMissingChangesets = []; + for (const modifiedPackageDir of modifiedPackageDirs) { + const { name: modifiedPackageName, private: isPrivate } = + await readPackageJson(modifiedPackageDir); + if (isPrivate) { + continue; + } + if (!packagesWithChangeset.has(modifiedPackageName)) { + packagesMissingChangesets.push(modifiedPackageName); + } + } -changedFiles - .filter( - (changedFile) => - changedFile.startsWith('packages/') && !changedFile.endsWith('test.ts') - ) - .forEach((changedPackageFile) => { - modifiedPackageDirs.add( - changedPackageFile.split('/').slice(0, 2).join('/') + if (packagesMissingChangesets.length > 0) { + throw new Error( + `The following packages have changes but are not included in any changeset:${EOL}${EOL}${packagesMissingChangesets.join( + EOL + )}${EOL}${EOL}Add a changeset using 'npx changeset add'.` ); - }); - -const packagesMissingChangesets = []; -for (const modifiedPackageDir of modifiedPackageDirs) { - const { name: modifiedPackageName, private: isPrivate } = - await readPackageJson(modifiedPackageDir); - if (isPrivate) { - continue; } - if (!packagesWithChangeset.has(modifiedPackageName)) { - packagesMissingChangesets.push(modifiedPackageName); +}; + +const convertVersionType = (version: VersionType): VersionTypeEnum => { + switch (version) { + case 'major': + return VersionTypeEnum.MAJOR; + case 'minor': + return VersionTypeEnum.MINOR; + case 'patch': + return VersionTypeEnum.PATCH; + case 'none': + return VersionTypeEnum.NONE; } -} +}; -if (packagesMissingChangesets.length > 0) { - throw new Error( - `The following packages have changes but are not included in any changeset:${EOL}${EOL}${packagesMissingChangesets.join( - EOL - )}${EOL}${EOL}Add a changeset using 'npx changeset add'.` +const findEffectiveVersion = ( + releasePlan: ReleasePlan, + packageName: string +): VersionTypeEnum => { + let effectiveVersion: VersionTypeEnum = VersionTypeEnum.NONE; + + for (const changeset of releasePlan.changesets) { + for (const release of changeset.releases) { + if (release.name === packageName) { + const releaseVersionType = convertVersionType(release.type); + if (releaseVersionType > effectiveVersion) { + effectiveVersion = releaseVersionType; + } + } + } + } + return effectiveVersion; +}; + +const checkBackendDependenciesVersion = (releasePlan: ReleasePlan) => { + const backendVersion: VersionTypeEnum = findEffectiveVersion( + releasePlan, + '@aws-amplify/backend' + ); + const backendAuthVersion: VersionTypeEnum = findEffectiveVersion( + releasePlan, + '@aws-amplify/backend-auth' + ); + const backendDataVersion: VersionTypeEnum = findEffectiveVersion( + releasePlan, + '@aws-amplify/backend-data' ); + const backendFunctionVersion: VersionTypeEnum = findEffectiveVersion( + releasePlan, + '@aws-amplify/backend-function' + ); + const backendStorageVersion: VersionTypeEnum = findEffectiveVersion( + releasePlan, + '@aws-amplify/backend-storage' + ); + + if ( + backendVersion < + Math.max( + backendAuthVersion, + backendDataVersion, + backendFunctionVersion, + backendStorageVersion + ) + ) { + throw new Error( + `@aws-amplify/backend has a version bump of a different kind from its dependencies (@aws-amplify/backend-auth, @aws-amplify/backend-data, @aws-amplify/backend-function, or @aws-amplify/backend-storage) but is expected to have a version bump of the same kind.${EOL}` + ); + } +}; + +const gitClient = new GitClient(); + +const baseRef = process.argv[2]; +if (baseRef === undefined) { + throw new Error('No base ref specified for changeset completeness check'); } + +const releasePlan = await getReleasePlan(process.cwd()); + +await checkForMissingChangesets(releasePlan, gitClient, baseRef); +checkBackendDependenciesVersion(releasePlan); From d8692b0c96584fb699e892183ae68fe302740680 Mon Sep 17 00:00:00 2001 From: Leena <81589006+ShadowCat567@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:30:26 -0400 Subject: [PATCH 018/199] Update to `stop:npm-proxy` for Windows (#2053) * npm stop proxy update for windows * make lint happy * added netstat to eslint dictionary * adjusted shell prop * specify listenting ports --------- Co-authored-by: Vieltojarvi --- .eslint_dictionary.json | 1 + scripts/stop_npm_proxy.ts | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index e249b3861f8..2e0a49ae60c 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -103,6 +103,7 @@ "mysql", "namespace", "namespaces", + "netstat", "nodejs", "nodenext", "nodir", diff --git a/scripts/stop_npm_proxy.ts b/scripts/stop_npm_proxy.ts index 070126177ea..dd8555313db 100644 --- a/scripts/stop_npm_proxy.ts +++ b/scripts/stop_npm_proxy.ts @@ -13,10 +13,20 @@ await execa('npm', ['config', 'set', 'registry', NPM_REGISTRY]); // returns the process id of the process listening on the specified port let pid: number; try { - const lsofResult = await execaCommand( - `lsof -n -t -iTCP:${VERDACCIO_PORT} -sTCP:LISTEN` - ); - pid = Number.parseInt(lsofResult.stdout.toString()); + if (process.platform === 'win32') { + const netStatResult = await execaCommand( + `netstat -n -a -o | grep LISTENING | grep ${VERDACCIO_PORT}`, + { shell: 'bash' } + ); + pid = Number.parseInt( + netStatResult.stdout.toString().split(/(\s)/).slice(-1)[0] + ); + } else { + const lsofResult = await execaCommand( + `lsof -n -t -iTCP:${VERDACCIO_PORT} -sTCP:LISTEN` + ); + pid = Number.parseInt(lsofResult.stdout.toString()); + } } catch (err) { console.warn( 'Could not determine npm proxy process id. Most likely the process has already been stopped.' From 190239a4c5b647f13d710dccd029e7d867230386 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 26 Sep 2024 10:58:53 -0700 Subject: [PATCH 019/199] Increase network timeout in yarn classic e2e tests. (#2057) * Increase network timeout. * Increase network timeout. * not that --- .changeset/short-rats-kneel.md | 2 ++ packages/integration-tests/src/setup_package_manager.ts | 5 +++++ 2 files changed, 7 insertions(+) create mode 100644 .changeset/short-rats-kneel.md diff --git a/.changeset/short-rats-kneel.md b/.changeset/short-rats-kneel.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/short-rats-kneel.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/setup_package_manager.ts b/packages/integration-tests/src/setup_package_manager.ts index e1b51622f0c..ad72f3270fa 100644 --- a/packages/integration-tests/src/setup_package_manager.ts +++ b/packages/integration-tests/src/setup_package_manager.ts @@ -39,6 +39,11 @@ const initializeYarnClassic = async (execaOptions: { ['config', 'set', 'registry', customRegistry], execaOptions ); + await execa( + packageManager, + ['config', 'set', 'network-timeout', '60000'], + execaOptions + ); await execa(packageManager, ['config', 'get', 'registry'], execaOptions); await execa(packageManager, ['cache', 'clean'], execaOptions); }; From d538ecc52b73cad3b029724430458f429f9ca6fa Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Thu, 26 Sep 2024 13:06:02 -0700 Subject: [PATCH 020/199] add storage access rules to outputs (#1927) * add storage access rules to outputs * fix tests * remove read action from storage access rule outputs in favor of get and list * update api.md * update permissions for non entity roles for entity_id paths * add entity id substitution constant * add comments for new behavior with owner paths * add back read * pr feedback * update api.md --- .changeset/two-sloths-clap.md | 10 + .../backend-output-schemas/src/storage/v1.ts | 20 + .../backend-storage/src/access_builder.ts | 3 +- packages/backend-storage/src/constants.ts | 1 + packages/backend-storage/src/construct.ts | 9 + packages/backend-storage/src/private_types.ts | 6 + .../src/storage_access_orchestrator.test.ts | 120 ++++- .../src/storage_access_orchestrator.ts | 71 ++- .../src/storage_container_entry_generator.ts | 4 +- .../src/storage_outputs_aspect.test.ts | 44 ++ .../src/storage_outputs_aspect.ts | 19 +- packages/backend-storage/src/types.ts | 16 + packages/backend/src/backend_factory.test.ts | 2 +- packages/backend/src/backend_factory.ts | 2 +- .../engine/custom_outputs_accumulator.test.ts | 6 +- .../outputs/generate_outputs_command.test.ts | 12 +- .../commands/sandbox/sandbox_command.test.ts | 6 +- .../sandbox_event_handler_factory.test.ts | 6 +- packages/client-config/API.md | 170 ++++++- .../client_config_contributor_factory.ts | 22 +- .../client_config_contributor_v1.test.ts | 34 +- .../client_config_contributor_v1.ts | 74 ++- .../client_config_v1.2.ts | 272 ++++++++++ .../src/client-config-schema/schema_v1.2.json | 476 ++++++++++++++++++ .../src/client-config-types/client_config.ts | 14 +- .../client_config_formatter_default.test.ts | 4 +- .../client_config_formatter_legacy.test.ts | 2 +- .../client_config_to_legacy_converter.test.ts | 18 +- .../client_config_to_legacy_converter.ts | 14 +- .../client_config_writer.test.ts | 2 +- ...nerate_empty_client_config_to_file.test.ts | 6 +- .../generate_empty_client_config_to_file.ts | 2 +- .../unified_client_config_generator.test.ts | 123 ++++- .../src/amplify_auth_credentials_factory.ts | 2 +- .../access_testing_project.ts | 12 +- .../cdk/auth_cdk_project.ts | 2 +- .../custom-outputs/amplify/backend.ts | 6 +- 37 files changed, 1470 insertions(+), 142 deletions(-) create mode 100644 .changeset/two-sloths-clap.md create mode 100644 packages/client-config/src/client-config-schema/client_config_v1.2.ts create mode 100644 packages/client-config/src/client-config-schema/schema_v1.2.json diff --git a/.changeset/two-sloths-clap.md b/.changeset/two-sloths-clap.md new file mode 100644 index 00000000000..35f1fcf7c65 --- /dev/null +++ b/.changeset/two-sloths-clap.md @@ -0,0 +1,10 @@ +--- +'@aws-amplify/client-config': minor +'@aws-amplify/backend-output-schemas': patch +'@aws-amplify/integration-tests': patch +'@aws-amplify/backend-storage': patch +'@aws-amplify/backend': patch +'@aws-amplify/backend-cli': patch +--- + +add storage access rules to outputs diff --git a/packages/backend-output-schemas/src/storage/v1.ts b/packages/backend-output-schemas/src/storage/v1.ts index 5095714a813..0827b34d69e 100644 --- a/packages/backend-output-schemas/src/storage/v1.ts +++ b/packages/backend-output-schemas/src/storage/v1.ts @@ -1,9 +1,29 @@ import { z } from 'zod'; +const storageAccessActionEnum = z.enum([ + 'read', + 'get', + 'list', + 'write', + 'delete', +]); + +const pathSchema = z.record( + z.string(), + z.object({ + guest: z.array(storageAccessActionEnum).optional(), + authenticated: z.array(storageAccessActionEnum).optional(), + groups: z.array(storageAccessActionEnum).optional(), + entity: z.array(storageAccessActionEnum).optional(), + resource: z.array(storageAccessActionEnum).optional(), + }) +); + const bucketSchema = z.object({ name: z.string(), bucketName: z.string(), storageRegion: z.string(), + paths: pathSchema.optional(), }); export const storageOutputSchema = z.object({ diff --git a/packages/backend-storage/src/access_builder.ts b/packages/backend-storage/src/access_builder.ts index 129c8b64ee3..7309e1efa73 100644 --- a/packages/backend-storage/src/access_builder.ts +++ b/packages/backend-storage/src/access_builder.ts @@ -5,6 +5,7 @@ import { ResourceProvider, } from '@aws-amplify/plugin-types'; import { StorageAccessBuilder } from './types.js'; +import { entityIdSubstitution } from './constants.js'; export const roleAccessBuilder: StorageAccessBuilder = { authenticated: { @@ -69,7 +70,7 @@ export const roleAccessBuilder: StorageAccessBuilder = { }, ], actions, - idSubstitution: '${cognito-identity.amazonaws.com:sub}', + idSubstitution: entityIdSubstitution, }), }), resource: (other) => ({ diff --git a/packages/backend-storage/src/constants.ts b/packages/backend-storage/src/constants.ts index 8ee0e17bd51..7588e609976 100644 --- a/packages/backend-storage/src/constants.ts +++ b/packages/backend-storage/src/constants.ts @@ -1 +1,2 @@ export const entityIdPathToken = '{entity_id}'; +export const entityIdSubstitution = '${cognito-identity.amazonaws.com:sub}'; diff --git a/packages/backend-storage/src/construct.ts b/packages/backend-storage/src/construct.ts index d31f0b97ebc..4308b3cecaa 100644 --- a/packages/backend-storage/src/construct.ts +++ b/packages/backend-storage/src/construct.ts @@ -20,6 +20,7 @@ import { AttributionMetadataStorage } from '@aws-amplify/backend-output-storage' import { fileURLToPath } from 'node:url'; import { IFunction } from 'aws-cdk-lib/aws-lambda'; import { S3EventSourceV2 } from 'aws-cdk-lib/aws-lambda-event-sources'; +import { StorageAccessDefinitionOutput } from './private_types.js'; // Be very careful editing this value. It is the string that is used to attribute stacks to Amplify Storage in BI metrics const storageStackType = 'storage-S3'; @@ -84,6 +85,7 @@ export class AmplifyStorage readonly resources: StorageResources; readonly isDefault: boolean; readonly name: string; + accessDefinition: StorageAccessDefinitionOutput; /** * Create a new AmplifyStorage instance */ @@ -146,4 +148,11 @@ export class AmplifyStorage new S3EventSourceV2(this.resources.bucket, { events }) ); }; + + /** + * Add access definitions to storage + */ + addAccessDefinition = (accessOutput: StorageAccessDefinitionOutput) => { + this.accessDefinition = accessOutput; + }; } diff --git a/packages/backend-storage/src/private_types.ts b/packages/backend-storage/src/private_types.ts index 8c7e53f27ac..8daaf2af48a 100644 --- a/packages/backend-storage/src/private_types.ts +++ b/packages/backend-storage/src/private_types.ts @@ -15,3 +15,9 @@ export type StorageError = * StorageAction type intended to be used after mapping "read" to "get" and "list" */ export type InternalStorageAction = Exclude; + +/** + * Storage access types intended to be used to map storage access to storage outputs + */ +export type StorageAccessConfig = Record; +export type StorageAccessDefinitionOutput = Record; diff --git a/packages/backend-storage/src/storage_access_orchestrator.test.ts b/packages/backend-storage/src/storage_access_orchestrator.test.ts index ffcc031de31..75cecc30baf 100644 --- a/packages/backend-storage/src/storage_access_orchestrator.test.ts +++ b/packages/backend-storage/src/storage_access_orchestrator.test.ts @@ -4,7 +4,7 @@ import { ConstructFactoryGetInstanceProps } from '@aws-amplify/plugin-types'; import { App, Stack } from 'aws-cdk-lib'; import { Bucket } from 'aws-cdk-lib/aws-s3'; import assert from 'node:assert'; -import { entityIdPathToken } from './constants.js'; +import { entityIdPathToken, entityIdSubstitution } from './constants.js'; import { StorageAccessPolicyFactory } from './storage_access_policy_factory.js'; import { StorageAccessDefinition } from './types.js'; @@ -78,7 +78,8 @@ void describe('StorageAccessOrchestrator', () => { storageAccessPolicyFactory ); - storageAccessOrchestrator.orchestrateStorageAccess(); + const storageAccessDefinitionOutput = + storageAccessOrchestrator.orchestrateStorageAccess(); assert.equal(acceptResourceAccessMock.mock.callCount(), 1); assert.deepStrictEqual( acceptResourceAccessMock.mock.calls[0].arguments[0].document.toJSON(), @@ -102,6 +103,11 @@ void describe('StorageAccessOrchestrator', () => { acceptResourceAccessMock.mock.calls[0].arguments[1], ssmEnvironmentEntriesStub ); + assert.deepStrictEqual(storageAccessDefinitionOutput, { + 'test/prefix/*': { + acceptor: ['get', 'write'], + }, + }); }); void it('handles multiple permissions for the same resource access acceptor', () => { @@ -132,7 +138,8 @@ void describe('StorageAccessOrchestrator', () => { storageAccessPolicyFactory ); - storageAccessOrchestrator.orchestrateStorageAccess(); + const storageAccessDefinitionOutput = + storageAccessOrchestrator.orchestrateStorageAccess(); assert.equal(acceptResourceAccessMock.mock.callCount(), 1); assert.deepStrictEqual( acceptResourceAccessMock.mock.calls[0].arguments[0].document.toJSON(), @@ -164,6 +171,14 @@ void describe('StorageAccessOrchestrator', () => { acceptResourceAccessMock.mock.calls[0].arguments[1], ssmEnvironmentEntriesStub ); + assert.deepStrictEqual(storageAccessDefinitionOutput, { + 'test/prefix/*': { + acceptor: ['get', 'write', 'delete'], + }, + 'another/prefix/*': { + acceptor: ['get'], + }, + }); }); void it('handles multiple resource access acceptors', () => { @@ -204,7 +219,8 @@ void describe('StorageAccessOrchestrator', () => { storageAccessPolicyFactory ); - storageAccessOrchestrator.orchestrateStorageAccess(); + const storageAccessDefinitionOutput = + storageAccessOrchestrator.orchestrateStorageAccess(); assert.equal(acceptResourceAccessMock1.mock.callCount(), 1); assert.deepStrictEqual( acceptResourceAccessMock1.mock.calls[0].arguments[0].document.toJSON(), @@ -259,6 +275,15 @@ void describe('StorageAccessOrchestrator', () => { acceptResourceAccessMock2.mock.calls[0].arguments[1], ssmEnvironmentEntriesStub ); + assert.deepStrictEqual(storageAccessDefinitionOutput, { + 'test/prefix/*': { + acceptor1: ['get', 'write', 'delete'], + acceptor2: ['get'], + }, + 'another/prefix/*': { + acceptor2: ['get', 'delete'], + }, + }); }); void it('replaces owner placeholder in s3 prefix', () => { @@ -274,7 +299,7 @@ void describe('StorageAccessOrchestrator', () => { acceptResourceAccess: acceptResourceAccessMock, }), ], - idSubstitution: '{testOwnerSub}', + idSubstitution: entityIdSubstitution, uniqueDefinitionIdValidations: accessDefinitionTestDefaults('acceptor') .uniqueDefinitionIdValidations, @@ -286,7 +311,8 @@ void describe('StorageAccessOrchestrator', () => { storageAccessPolicyFactory ); - storageAccessOrchestrator.orchestrateStorageAccess(); + const storageAccessDefinitionOutput = + storageAccessOrchestrator.orchestrateStorageAccess(); assert.equal(acceptResourceAccessMock.mock.callCount(), 1); assert.deepStrictEqual( acceptResourceAccessMock.mock.calls[0].arguments[0].document.toJSON(), @@ -295,12 +321,12 @@ void describe('StorageAccessOrchestrator', () => { { Action: 's3:GetObject', Effect: 'Allow', - Resource: `${bucket.bucketArn}/test/{testOwnerSub}/*`, + Resource: `${bucket.bucketArn}/test/${entityIdSubstitution}/*`, }, { Action: 's3:PutObject', Effect: 'Allow', - Resource: `${bucket.bucketArn}/test/{testOwnerSub}/*`, + Resource: `${bucket.bucketArn}/test/${entityIdSubstitution}/*`, }, ], Version: '2012-10-17', @@ -310,6 +336,11 @@ void describe('StorageAccessOrchestrator', () => { acceptResourceAccessMock.mock.calls[0].arguments[1], ssmEnvironmentEntriesStub ); + assert.deepStrictEqual(storageAccessDefinitionOutput, { + [`test/${entityIdSubstitution}/*`]: { + acceptor: ['get', 'write'], + }, + }); }); void it('denies parent actions on a subpath by default', () => { @@ -347,7 +378,8 @@ void describe('StorageAccessOrchestrator', () => { storageAccessPolicyFactory ); - storageAccessOrchestrator.orchestrateStorageAccess(); + const storageAccessDefinitionOutput = + storageAccessOrchestrator.orchestrateStorageAccess(); assert.equal(acceptResourceAccessMock1.mock.callCount(), 1); assert.deepStrictEqual( acceptResourceAccessMock1.mock.calls[0].arguments[0].document.toJSON(), @@ -396,6 +428,14 @@ void describe('StorageAccessOrchestrator', () => { Version: '2012-10-17', } ); + assert.deepStrictEqual(storageAccessDefinitionOutput, { + 'foo/*': { + acceptor1: ['get', 'write'], + }, + 'foo/bar/*': { + acceptor2: ['get'], + }, + }); }); void it('combines owner rules for same resource access acceptor', () => { @@ -411,7 +451,7 @@ void describe('StorageAccessOrchestrator', () => { { actions: ['write', 'delete'], getResourceAccessAcceptors: [authenticatedResourceAccessAcceptor], - idSubstitution: '{idSub}', + idSubstitution: entityIdSubstitution, uniqueDefinitionIdValidations: accessDefinitionTestDefaults('auth-with-id') .uniqueDefinitionIdValidations, @@ -428,7 +468,8 @@ void describe('StorageAccessOrchestrator', () => { storageAccessPolicyFactory ); - storageAccessOrchestrator.orchestrateStorageAccess(); + const storageAccessDefinitionOutput = + storageAccessOrchestrator.orchestrateStorageAccess(); assert.equal(acceptResourceAccessMock.mock.callCount(), 1); assert.deepStrictEqual( acceptResourceAccessMock.mock.calls[0].arguments[0].document.toJSON(), @@ -437,17 +478,17 @@ void describe('StorageAccessOrchestrator', () => { { Action: 's3:PutObject', Effect: 'Allow', - Resource: `${bucket.bucketArn}/foo/{idSub}/*`, + Resource: `${bucket.bucketArn}/foo/${entityIdSubstitution}/*`, }, { Action: 's3:DeleteObject', Effect: 'Allow', - Resource: `${bucket.bucketArn}/foo/{idSub}/*`, + Resource: `${bucket.bucketArn}/foo/${entityIdSubstitution}/*`, }, { Action: 's3:GetObject', Effect: 'Allow', - Resource: `${bucket.bucketArn}/foo/*/*`, + Resource: `${bucket.bucketArn}/foo/*`, }, ], Version: '2012-10-17', @@ -457,6 +498,14 @@ void describe('StorageAccessOrchestrator', () => { acceptResourceAccessMock.mock.calls[0].arguments[1], ssmEnvironmentEntriesStub ); + assert.deepStrictEqual(storageAccessDefinitionOutput, { + 'foo/*': { + auth: ['get'], + }, + [`foo/${entityIdSubstitution}/*`]: { + 'auth-with-id': ['write', 'delete'], + }, + }); }); void it('handles multiple resource access acceptors on multiple prefixes', () => { @@ -488,7 +537,7 @@ void describe('StorageAccessOrchestrator', () => { { actions: ['get'], getResourceAccessAcceptors: [getResourceAccessAcceptorStub2], - idSubstitution: '{idSub}', + idSubstitution: entityIdSubstitution, uniqueDefinitionIdValidations: accessDefinitionTestDefaults('stub2') .uniqueDefinitionIdValidations, @@ -509,7 +558,7 @@ void describe('StorageAccessOrchestrator', () => { { actions: ['get', 'write', 'delete'], getResourceAccessAcceptors: [getResourceAccessAcceptorStub2], - idSubstitution: '{idSub}', + idSubstitution: entityIdSubstitution, uniqueDefinitionIdValidations: accessDefinitionTestDefaults('stub2') .uniqueDefinitionIdValidations, @@ -526,7 +575,8 @@ void describe('StorageAccessOrchestrator', () => { storageAccessPolicyFactory ); - storageAccessOrchestrator.orchestrateStorageAccess(); + const storageAccessDefinitionOutput = + storageAccessOrchestrator.orchestrateStorageAccess(); assert.equal(acceptResourceAccessMock1.mock.callCount(), 1); assert.deepStrictEqual( acceptResourceAccessMock1.mock.calls[0].arguments[0].document.toJSON(), @@ -537,7 +587,7 @@ void describe('StorageAccessOrchestrator', () => { Effect: 'Allow', Resource: [ `${bucket.bucketArn}/foo/*`, - `${bucket.bucketArn}/other/*/*`, + `${bucket.bucketArn}/other/*`, ], }, { @@ -577,23 +627,40 @@ void describe('StorageAccessOrchestrator', () => { Effect: 'Allow', Resource: [ `${bucket.bucketArn}/foo/bar/*`, - `${bucket.bucketArn}/other/{idSub}/*`, + `${bucket.bucketArn}/other/${entityIdSubstitution}/*`, ], }, { Action: 's3:PutObject', Effect: 'Allow', - Resource: `${bucket.bucketArn}/other/{idSub}/*`, + Resource: `${bucket.bucketArn}/other/${entityIdSubstitution}/*`, }, { Action: 's3:DeleteObject', Effect: 'Allow', - Resource: `${bucket.bucketArn}/other/{idSub}/*`, + Resource: `${bucket.bucketArn}/other/${entityIdSubstitution}/*`, }, ], Version: '2012-10-17', } ); + assert.deepStrictEqual(storageAccessDefinitionOutput, { + 'foo/*': { + stub1: ['get', 'write'], + }, + 'foo/bar/*': { + stub2: ['get'], + }, + 'foo/baz/*': { + stub1: ['get'], + }, + 'other/*': { + stub1: ['get'], + }, + [`other/${entityIdSubstitution}/*`]: { + stub2: ['get', 'write', 'delete'], + }, + }); }); void it('throws validation error for multiple rules on the same resource access acceptor', () => { @@ -658,7 +725,8 @@ void describe('StorageAccessOrchestrator', () => { storageAccessPolicyFactory ); - storageAccessOrchestrator.orchestrateStorageAccess(); + const storageAccessDefinitionOutput = + storageAccessOrchestrator.orchestrateStorageAccess(); assert.equal(acceptResourceAccessMock.mock.callCount(), 1); assert.deepStrictEqual( acceptResourceAccessMock.mock.calls[0].arguments[0].document.toJSON(), @@ -695,6 +763,14 @@ void describe('StorageAccessOrchestrator', () => { acceptResourceAccessMock.mock.calls[0].arguments[1], ssmEnvironmentEntriesStub ); + assert.deepStrictEqual(storageAccessDefinitionOutput, { + 'foo/bar/*': { + auth: ['get', 'list'], + }, + 'other/baz/*': { + auth: ['get', 'list'], + }, + }); }); }); }); diff --git a/packages/backend-storage/src/storage_access_orchestrator.ts b/packages/backend-storage/src/storage_access_orchestrator.ts index e4b4c0e22a3..35d2553ab42 100644 --- a/packages/backend-storage/src/storage_access_orchestrator.ts +++ b/packages/backend-storage/src/storage_access_orchestrator.ts @@ -8,11 +8,15 @@ import { StorageAccessGenerator, StoragePath, } from './types.js'; -import { entityIdPathToken } from './constants.js'; +import { entityIdPathToken, entityIdSubstitution } from './constants.js'; import { StorageAccessPolicyFactory } from './storage_access_policy_factory.js'; import { validateStorageAccessPaths as _validateStorageAccessPaths } from './validate_storage_access_paths.js'; import { roleAccessBuilder as _roleAccessBuilder } from './access_builder.js'; -import { InternalStorageAction, StorageError } from './private_types.js'; +import { + InternalStorageAction, + StorageAccessConfig, + StorageError, +} from './private_types.js'; import { AmplifyUserError } from '@aws-amplify/platform-core'; /* some types internal to this file to improve readability */ @@ -88,12 +92,26 @@ export class StorageAccessOrchestrator { // verify that the paths in the access definition are valid this.validateStorageAccessPaths(Object.keys(storageAccessDefinition)); + const storageOutputAccessDefinition: Record = + {}; + // iterate over the access definition and group permissions by ResourceAccessAcceptor Object.entries(storageAccessDefinition).forEach( ([s3Prefix, accessPermissions]) => { const uniqueDefinitionIdSet = new Set(); // iterate over all of the access definitions for a given prefix accessPermissions.forEach((permission) => { + const accessConfig: StorageAccessConfig = {}; + // replace "read" with "get" and "list" in actions + const replaceReadWithGetAndList = permission.actions.flatMap( + (action) => (action === 'read' ? ['get', 'list'] : [action]) + ) as InternalStorageAction[]; + + // ensure the actions list has no duplicates + const noDuplicateActions = Array.from( + new Set(replaceReadWithGetAndList) + ); + // iterate over all uniqueDefinitionIdValidations and ensure uniqueness within this path prefix permission.uniqueDefinitionIdValidations.forEach( ({ uniqueDefinitionId, validationErrorOptions }) => { @@ -105,24 +123,21 @@ export class StorageAccessOrchestrator { } else { uniqueDefinitionIdSet.add(uniqueDefinitionId); } + + accessConfig[uniqueDefinitionId] = noDuplicateActions; } ); // make the owner placeholder substitution in the s3 prefix - const prefix = s3Prefix.replaceAll( - entityIdPathToken, + const prefix = placeholderSubstitution( + s3Prefix, permission.idSubstitution - ) as StoragePath; - - // replace "read" with "get" and "list" in actions - const replaceReadWithGetAndList = permission.actions.flatMap( - (action) => (action === 'read' ? ['get', 'list'] : [action]) - ) as InternalStorageAction[]; - - // ensure the actions list has no duplicates - const noDuplicateActions = Array.from( - new Set(replaceReadWithGetAndList) ); + storageOutputAccessDefinition[prefix] = { + ...storageOutputAccessDefinition[prefix], + ...accessConfig, + }; + // set an entry that maps this permission to each resource acceptor permission.getResourceAccessAcceptors.forEach( (getResourceAccessAcceptor) => { @@ -139,6 +154,8 @@ export class StorageAccessOrchestrator { // iterate over the access map entries and invoke each ResourceAccessAcceptor to accept the permissions this.attachPolicies(this.ssmEnvironmentEntries); + + return storageOutputAccessDefinition; }; /** @@ -195,7 +212,11 @@ export class StorageAccessOrchestrator { const allPaths = Array.from(this.prefixDenyMap.keys()); allPaths.forEach((storagePath) => { const parent = findParent(storagePath, allPaths); - if (!parent) { + // do not add to prefix deny map if there is no parent or the path is a subpath with entity id + if ( + !parent || + parent === storagePath.replaceAll(`${entityIdSubstitution}/`, '') + ) { return; } // if a parent path is defined, invoke the denyByDefault callback on this subpath for all policies that exist on the parent path @@ -258,6 +279,26 @@ export class StorageAccessOrchestratorFactory { ); } +/** + * Performs the owner placeholder substitution in the s3 prefix + */ +const placeholderSubstitution = ( + s3Prefix: string, + idSubstitution: string +): StoragePath => { + const prefix = s3Prefix.replaceAll( + entityIdPathToken, + idSubstitution + ) as StoragePath; + + // for owner paths where prefix ends with '/*/*' remove the last wildcard + if (prefix.endsWith('/*/*')) { + return prefix.slice(0, -2) as StoragePath; + } + + return prefix as StoragePath; +}; + /** * Returns the element in paths that is a prefix of path, if any * Note that there can only be one at this point because of upstream validation diff --git a/packages/backend-storage/src/storage_container_entry_generator.ts b/packages/backend-storage/src/storage_container_entry_generator.ts index 90fbd59bf32..b7da0eaaed4 100644 --- a/packages/backend-storage/src/storage_container_entry_generator.ts +++ b/packages/backend-storage/src/storage_container_entry_generator.ts @@ -78,7 +78,9 @@ export class StorageContainerEntryGenerator ); // the orchestrator generates policies according to the accessDefinition and attaches the policies to appropriate roles - storageAccessOrchestrator.orchestrateStorageAccess(); + const storageAccessOutput = + storageAccessOrchestrator.orchestrateStorageAccess(); + amplifyStorage.addAccessDefinition(storageAccessOutput); return amplifyStorage; }; diff --git a/packages/backend-storage/src/storage_outputs_aspect.test.ts b/packages/backend-storage/src/storage_outputs_aspect.test.ts index 7487f0864d5..cccf5ba94c9 100644 --- a/packages/backend-storage/src/storage_outputs_aspect.test.ts +++ b/packages/backend-storage/src/storage_outputs_aspect.test.ts @@ -7,6 +7,7 @@ import { StorageOutput } from '@aws-amplify/backend-output-schemas'; import { App, Stack } from 'aws-cdk-lib'; import { IConstruct } from 'constructs'; import { AmplifyUserError } from '@aws-amplify/platform-core'; +import { StorageAccessDefinitionOutput } from './private_types.js'; void describe('StorageOutputsAspect', () => { let app: App; @@ -129,6 +130,49 @@ void describe('StorageOutputsAspect', () => { }) ); }); + + void it('should add access paths if the storage has access rules configured', () => { + const accessDefinition = { + 'path/*': { + authenticated: ['get', 'list', 'write', 'delete'], + guest: ['get', 'list'], + }, + }; + const node = new AmplifyStorage(stack, 'test', { name: 'testName' }); + node.addAccessDefinition( + accessDefinition as StorageAccessDefinitionOutput + ); + aspect = new StorageOutputsAspect(outputStorageStrategy); + aspect.visit(node); + + assert.equal( + addBackendOutputEntryMock.mock.calls[0].arguments[0], + 'AWS::Amplify::Storage' + ); + assert.equal( + addBackendOutputEntryMock.mock.calls[0].arguments[1].payload + .storageRegion, + Stack.of(node).region + ); + assert.equal( + addBackendOutputEntryMock.mock.calls[0].arguments[1].payload.bucketName, + node.resources.bucket.bucketName + ); + assert.equal( + appendToBackendOutputListMock.mock.calls[0].arguments[0], + 'AWS::Amplify::Storage' + ); + assert.equal( + appendToBackendOutputListMock.mock.calls[0].arguments[1].payload + .buckets, + JSON.stringify({ + name: node.name, + bucketName: node.resources.bucket.bucketName, + storageRegion: Stack.of(node).region, + paths: accessDefinition, + }) + ); + }); }); void describe('Validate', () => { diff --git a/packages/backend-storage/src/storage_outputs_aspect.ts b/packages/backend-storage/src/storage_outputs_aspect.ts index 9e9a9fcfa03..dd448937a53 100644 --- a/packages/backend-storage/src/storage_outputs_aspect.ts +++ b/packages/backend-storage/src/storage_outputs_aspect.ts @@ -7,6 +7,7 @@ import { BackendOutputStorageStrategy } from '@aws-amplify/plugin-types'; import { IAspect, Stack } from 'aws-cdk-lib'; import { IConstruct } from 'constructs'; import { AmplifyStorage } from './construct.js'; +import { StorageAccessDefinitionOutput } from './private_types.js'; /** * Aspect to store the storage outputs in the backend @@ -95,16 +96,22 @@ export class StorageOutputsAspect implements IAspect { }, }); } - + const bucketsPayload: Record< + string, + string | StorageAccessDefinitionOutput + > = { + name: node.name, + bucketName: node.resources.bucket.bucketName, + storageRegion: Stack.of(node).region, + }; + if (node.accessDefinition) { + bucketsPayload.paths = node.accessDefinition; + } // both default and non-default buckets should have the name, bucket name, and storage region stored in `buckets` field outputStorageStrategy.appendToBackendOutputList(storageOutputKey, { version: '1', payload: { - buckets: JSON.stringify({ - name: node.name, - bucketName: node.resources.bucket.bucketName, - storageRegion: Stack.of(node).region, - }), + buckets: JSON.stringify(bucketsPayload), }, }); }; diff --git a/packages/backend-storage/src/types.ts b/packages/backend-storage/src/types.ts index 39f0a4dead3..caf7abc1c05 100644 --- a/packages/backend-storage/src/types.ts +++ b/packages/backend-storage/src/types.ts @@ -41,17 +41,29 @@ export type StorageAccessBuilder = { /** * Configure storage access for authenticated users. Requires `defineAuth` in the backend definition. * @see https://docs.amplify.aws/gen2/build-a-backend/storage/#authenticated-user-access + * + * When configuring access for paths with the `{entity_id}` token, the token is replaced with a wildcard (`*`). + * For a path like `media/profile-pictures/{entity_id}/*`, this means access is configured for authenticated users for any file within + * `media/profile-pictures/*`. */ authenticated: StorageActionBuilder; /** * Configure storage access for guest (unauthenticated) users. Requires `defineAuth` in the backend definition. * @see https://docs.amplify.aws/gen2/build-a-backend/storage/#guest-user-access + * + * When configuring access for paths with the `{entity_id}` token, the token is replaced with a wildcard (`*`). + * For a path like `media/profile-pictures/{entity_id}/*`, this means access is configured for guest users for any file within + * `media/profile-pictures/*`. */ guest: StorageActionBuilder; /** * Configure storage access for User Pool groups. Requires `defineAuth` with groups config in the backend definition. * @see https://docs.amplify.aws/gen2/build-a-backend/storage/#user-group-access * @param groupName The User Pool group name to configure access for + * + * When configuring access for paths with the `{entity_id}` token, the token is replaced with a wildcard (`*`). + * For a path like `media/profile-pictures/{entity_id}/*`, this means access is configured for that specific group for any file within + * `media/profile-pictures/*`. */ groups: (groupNames: string[]) => StorageActionBuilder; /** @@ -64,6 +76,10 @@ export type StorageAccessBuilder = { * Grant other resources in the Amplify backend access to storage. * @see https://docs.amplify.aws/gen2/build-a-backend/storage/#grant-function-access * @param other The target resource to grant access to. Currently only the return value of `defineFunction` is supported. + * + * When configuring access for paths with the `{entity_id}` token, the token is replaced with a wildcard (`*`). + * For a path like `media/profile-pictures/{entity_id}/*`, this means access is configured for resources for any file within + * `media/profile-pictures/*`. */ resource: ( other: ConstructFactory diff --git a/packages/backend/src/backend_factory.test.ts b/packages/backend/src/backend_factory.test.ts index 268bede3ffe..cfc01a9e8b9 100644 --- a/packages/backend/src/backend_factory.test.ts +++ b/packages/backend/src/backend_factory.test.ts @@ -196,7 +196,7 @@ void describe('Backend', () => { const backend = new BackendFactory({}, rootStack); const clientConfigPartial: DeepPartialAmplifyGeneratedConfigs = { - version: '1.1', + version: '1.2', custom: { someCustomOutput: 'someCustomOutputValue', }, diff --git a/packages/backend/src/backend_factory.ts b/packages/backend/src/backend_factory.ts index 023f52244f6..d2c7662df35 100644 --- a/packages/backend/src/backend_factory.ts +++ b/packages/backend/src/backend_factory.ts @@ -33,7 +33,7 @@ const rootStackTypeIdentifier = 'root'; // Client config version that is used by `backend.addOutput()` const DEFAULT_CLIENT_CONFIG_VERSION_FOR_BACKEND_ADD_OUTPUT = - ClientConfigVersionOption.V1_1; + ClientConfigVersionOption.V1_2; /** * Factory that collects and instantiates all the Amplify backend constructs diff --git a/packages/backend/src/engine/custom_outputs_accumulator.test.ts b/packages/backend/src/engine/custom_outputs_accumulator.test.ts index 6962175e4c2..dc89639ab8a 100644 --- a/packages/backend/src/engine/custom_outputs_accumulator.test.ts +++ b/packages/backend/src/engine/custom_outputs_accumulator.test.ts @@ -59,11 +59,11 @@ void describe('Custom outputs accumulator', () => { ); const configPart1: DeepPartialAmplifyGeneratedConfigs = { - version: '1.1', + version: '1.2', custom: { output1: 'val1' }, }; const configPart2: DeepPartialAmplifyGeneratedConfigs = { - version: '1.1', + version: '1.2', custom: { output2: 'val2' }, }; accumulator.addOutput(configPart1); @@ -115,7 +115,7 @@ void describe('Custom outputs accumulator', () => { assert.throws( () => - accumulator.addOutput({ version: '1.1', custom: { output1: 'val1' } }), + accumulator.addOutput({ version: '1.2', custom: { output1: 'val1' } }), (error: AmplifyUserError) => { assert.strictEqual( error.message, diff --git a/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts b/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts index 80333f072ca..b6aa2065d76 100644 --- a/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts +++ b/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts @@ -74,7 +74,7 @@ void describe('generate outputs command', () => { assert.equal(generateClientConfigMock.mock.callCount(), 1); assert.deepEqual( generateClientConfigMock.mock.calls[0].arguments[1], - '1.1' // default version + '1.2' // default version ); assert.deepEqual( generateClientConfigMock.mock.calls[0].arguments[2], @@ -97,7 +97,7 @@ void describe('generate outputs command', () => { assert.equal(generateClientConfigMock.mock.callCount(), 1); assert.deepEqual( generateClientConfigMock.mock.calls[0].arguments[1], - '1.1' // default version + '1.2' // default version ); assert.deepStrictEqual( generateClientConfigMock.mock.calls[0].arguments[2], @@ -118,7 +118,7 @@ void describe('generate outputs command', () => { namespace: 'app_id', type: 'branch', }, - '1.1', + '1.2', '/foo/bar', undefined, ] @@ -136,7 +136,7 @@ void describe('generate outputs command', () => { { stackName: 'stack_name', }, - '1.1', + '1.2', '/foo/bar', undefined, ] @@ -154,7 +154,7 @@ void describe('generate outputs command', () => { { stackName: 'stack_name', }, - '1.1', + '1.2', 'foo/bar', undefined, ] @@ -172,7 +172,7 @@ void describe('generate outputs command', () => { { stackName: 'stack_name', }, - '1.1', + '1.2', 'foo/bar', ClientConfigFormat.DART, ] diff --git a/packages/cli/src/commands/sandbox/sandbox_command.test.ts b/packages/cli/src/commands/sandbox/sandbox_command.test.ts index 7b653d877cc..c4854565496 100644 --- a/packages/cli/src/commands/sandbox/sandbox_command.test.ts +++ b/packages/cli/src/commands/sandbox/sandbox_command.test.ts @@ -427,15 +427,15 @@ void describe('sandbox command', () => { ); }); - void it('sandbox creates an empty client config file if one does not already exist for version 1.1', async (contextual) => { + void it('sandbox creates an empty client config file if one does not already exist for version 1.2', async (contextual) => { contextual.mock.method(fs, 'existsSync', () => false); const writeFileMock = contextual.mock.method(fsp, 'writeFile', () => true); - await commandRunner.runCommand('sandbox --outputs-version 1.1'); + await commandRunner.runCommand('sandbox --outputs-version 1.2'); assert.equal(sandboxStartMock.mock.callCount(), 1); assert.equal(writeFileMock.mock.callCount(), 1); assert.deepStrictEqual( writeFileMock.mock.calls[0].arguments[1], - `{\n "version": "1.1"\n}` + `{\n "version": "1.2"\n}` ); assert.deepStrictEqual( writeFileMock.mock.calls[0].arguments[0], diff --git a/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.test.ts b/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.test.ts index 2a3366612ca..2ba2996a0ea 100644 --- a/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.test.ts +++ b/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.test.ts @@ -23,7 +23,7 @@ void describe('sandbox_event_handler_factory', () => { } as unknown as ClientConfigGeneratorAdapter; const clientConfigLifecycleHandler = new ClientConfigLifecycleHandler( clientConfigGeneratorAdapterMock, - '1.1', + '1.2', 'test-out', ClientConfigFormat.JSON ); @@ -73,7 +73,7 @@ void describe('sandbox_event_handler_factory', () => { namespace: 'test', name: 'name', }, - '1.1', + '1.2', 'test-out', 'json', ]); @@ -185,7 +185,7 @@ void describe('sandbox_event_handler_factory', () => { namespace: 'test', name: 'name', }, - '1.1', + '1.2', 'test-out', 'json', ]); diff --git a/packages/client-config/API.md b/packages/client-config/API.md index 40b7882c3eb..397419f74fd 100644 --- a/packages/client-config/API.md +++ b/packages/client-config/API.md @@ -16,6 +16,9 @@ type AmazonCognitoStandardAttributes = 'address' | 'birthdate' | 'email' | 'fami // @public type AmazonCognitoStandardAttributes_2 = 'address' | 'birthdate' | 'email' | 'family_name' | 'gender' | 'given_name' | 'locale' | 'middle_name' | 'name' | 'nickname' | 'phone_number' | 'picture' | 'preferred_username' | 'profile' | 'sub' | 'updated_at' | 'website' | 'zoneinfo'; +// @public +type AmazonCognitoStandardAttributes_3 = 'address' | 'birthdate' | 'email' | 'family_name' | 'gender' | 'given_name' | 'locale' | 'middle_name' | 'name' | 'nickname' | 'phone_number' | 'picture' | 'preferred_username' | 'profile' | 'sub' | 'updated_at' | 'website' | 'zoneinfo'; + // @public interface AmazonLocationServiceConfig { name?: string; @@ -28,12 +31,38 @@ interface AmazonLocationServiceConfig_2 { style?: string; } +// @public +interface AmazonLocationServiceConfig_3 { + name?: string; + style?: string; +} + // @public type AmazonPinpointChannels = 'IN_APP_MESSAGING' | 'FCM' | 'APNS' | 'EMAIL' | 'SMS'; // @public type AmazonPinpointChannels_2 = 'IN_APP_MESSAGING' | 'FCM' | 'APNS' | 'EMAIL' | 'SMS'; +// @public +type AmazonPinpointChannels_3 = 'IN_APP_MESSAGING' | 'FCM' | 'APNS' | 'EMAIL' | 'SMS'; + +// @public (undocumented) +type AmplifyStorageAccessActions = 'read' | 'get' | 'list' | 'write' | 'delete'; + +// @public +interface AmplifyStorageAccessRule { + // (undocumented) + authenticated?: AmplifyStorageAccessActions[]; + // (undocumented) + entity?: AmplifyStorageAccessActions[]; + // (undocumented) + groups?: AmplifyStorageAccessActions[]; + // (undocumented) + guest?: AmplifyStorageAccessActions[]; + // (undocumented) + resource?: AmplifyStorageAccessActions[]; +} + // @public (undocumented) interface AmplifyStorageBucket { // (undocumented) @@ -42,6 +71,20 @@ interface AmplifyStorageBucket { bucket_name: string; // (undocumented) name: string; + // (undocumented) + paths?: { + [k: string]: AmplifyStorageAccessRule; + }; +} + +// @public (undocumented) +interface AmplifyStorageBucket_2 { + // (undocumented) + aws_region: string; + // (undocumented) + bucket_name: string; + // (undocumented) + name: string; } // @public (undocumented) @@ -88,12 +131,12 @@ export type AuthClientConfig = { interface AWSAmplifyBackendOutputs { analytics?: { amazon_pinpoint?: { - aws_region: AwsRegion; + aws_region: string; app_id: string; }; }; auth?: { - aws_region: AwsRegion; + aws_region: string; user_pool_id: string; user_pool_client_id: string; identity_pool_id?: string; @@ -133,7 +176,7 @@ interface AWSAmplifyBackendOutputs { authorization_types: AwsAppsyncAuthorizationType[]; }; geo?: { - aws_region: AwsRegion; + aws_region: string; maps?: { items: { [k: string]: AmazonLocationServiceConfig; @@ -159,7 +202,7 @@ interface AWSAmplifyBackendOutputs { bucket_name: string; buckets?: AmplifyStorageBucket[]; }; - version: '1.1'; + version: '1.2'; } // @public @@ -235,6 +278,84 @@ interface AWSAmplifyBackendOutputs_2 { storage?: { aws_region: AwsRegion_2; bucket_name: string; + buckets?: AmplifyStorageBucket_2[]; + }; + version: '1.1'; +} + +// @public +interface AWSAmplifyBackendOutputs_3 { + analytics?: { + amazon_pinpoint?: { + aws_region: AwsRegion_3; + app_id: string; + }; + }; + auth?: { + aws_region: AwsRegion_3; + user_pool_id: string; + user_pool_client_id: string; + identity_pool_id?: string; + password_policy?: { + min_length: number; + require_numbers: boolean; + require_lowercase: boolean; + require_uppercase: boolean; + require_symbols: boolean; + }; + oauth?: { + identity_providers: ('GOOGLE' | 'FACEBOOK' | 'LOGIN_WITH_AMAZON' | 'SIGN_IN_WITH_APPLE')[]; + domain: string; + scopes: string[]; + redirect_sign_in_uri: string[]; + redirect_sign_out_uri: string[]; + response_type: 'code' | 'token'; + }; + standard_required_attributes?: AmazonCognitoStandardAttributes_3[]; + username_attributes?: ('email' | 'phone_number' | 'username')[]; + user_verification_types?: ('email' | 'phone_number')[]; + unauthenticated_identities_enabled?: boolean; + mfa_configuration?: 'NONE' | 'OPTIONAL' | 'REQUIRED'; + mfa_methods?: ('SMS' | 'TOTP')[]; + }; + custom?: { + [k: string]: unknown; + }; + data?: { + aws_region: AwsRegion_3; + url: string; + model_introspection?: { + [k: string]: unknown; + }; + api_key?: string; + default_authorization_type: AwsAppsyncAuthorizationType_3; + authorization_types: AwsAppsyncAuthorizationType_3[]; + }; + geo?: { + aws_region: AwsRegion_3; + maps?: { + items: { + [k: string]: AmazonLocationServiceConfig_3; + }; + default: string; + }; + search_indices?: { + items: string[]; + default: string; + }; + geofence_collections?: { + items: string[]; + default: string; + }; + }; + notifications?: { + aws_region: AwsRegion_3; + amazon_pinpoint_app_id: string; + channels: AmazonPinpointChannels_3[]; + }; + storage?: { + aws_region: AwsRegion_3; + bucket_name: string; }; version: '1'; } @@ -245,14 +366,20 @@ type AwsAppsyncAuthorizationType = 'AMAZON_COGNITO_USER_POOLS' | 'API_KEY' | 'AW // @public type AwsAppsyncAuthorizationType_2 = 'AMAZON_COGNITO_USER_POOLS' | 'API_KEY' | 'AWS_IAM' | 'AWS_LAMBDA' | 'OPENID_CONNECT'; +// @public +type AwsAppsyncAuthorizationType_3 = 'AMAZON_COGNITO_USER_POOLS' | 'API_KEY' | 'AWS_IAM' | 'AWS_LAMBDA' | 'OPENID_CONNECT'; + // @public (undocumented) type AwsRegion = string; // @public (undocumented) type AwsRegion_2 = string; +// @public (undocumented) +type AwsRegion_3 = string; + // @public -export type ClientConfig = clientConfigTypesV1_1.AWSAmplifyBackendOutputs | clientConfigTypesV1.AWSAmplifyBackendOutputs; +export type ClientConfig = clientConfigTypesV1_2.AWSAmplifyBackendOutputs | clientConfigTypesV1_1.AWSAmplifyBackendOutputs | clientConfigTypesV1.AWSAmplifyBackendOutputs; // @public (undocumented) export enum ClientConfigFileBaseName { @@ -280,29 +407,44 @@ export enum ClientConfigFormat { export type ClientConfigLegacy = Partial; declare namespace clientConfigTypesV1 { + export { + AmazonCognitoStandardAttributes_3 as AmazonCognitoStandardAttributes, + AwsRegion_3 as AwsRegion, + AwsAppsyncAuthorizationType_3 as AwsAppsyncAuthorizationType, + AmazonPinpointChannels_3 as AmazonPinpointChannels, + AWSAmplifyBackendOutputs_3 as AWSAmplifyBackendOutputs, + AmazonLocationServiceConfig_3 as AmazonLocationServiceConfig + } +} +export { clientConfigTypesV1 } + +declare namespace clientConfigTypesV1_1 { export { AmazonCognitoStandardAttributes_2 as AmazonCognitoStandardAttributes, AwsRegion_2 as AwsRegion, AwsAppsyncAuthorizationType_2 as AwsAppsyncAuthorizationType, AmazonPinpointChannels_2 as AmazonPinpointChannels, AWSAmplifyBackendOutputs_2 as AWSAmplifyBackendOutputs, - AmazonLocationServiceConfig_2 as AmazonLocationServiceConfig + AmazonLocationServiceConfig_2 as AmazonLocationServiceConfig, + AmplifyStorageBucket_2 as AmplifyStorageBucket } } -export { clientConfigTypesV1 } +export { clientConfigTypesV1_1 } -declare namespace clientConfigTypesV1_1 { +declare namespace clientConfigTypesV1_2 { export { AmazonCognitoStandardAttributes, AwsRegion, AwsAppsyncAuthorizationType, AmazonPinpointChannels, + AmplifyStorageAccessActions, AWSAmplifyBackendOutputs, AmazonLocationServiceConfig, - AmplifyStorageBucket + AmplifyStorageBucket, + AmplifyStorageAccessRule } } -export { clientConfigTypesV1_1 } +export { clientConfigTypesV1_2 } // @public (undocumented) export type ClientConfigVersion = `${ClientConfigVersionOption}`; @@ -314,11 +456,13 @@ export enum ClientConfigVersionOption { // (undocumented) V1 = "1", // (undocumented) - V1_1 = "1.1" + V1_1 = "1.1", + // (undocumented) + V1_2 = "1.2" } // @public -export type ClientConfigVersionTemplateType = T extends '1.1' ? clientConfigTypesV1_1.AWSAmplifyBackendOutputs : T extends '1' ? clientConfigTypesV1.AWSAmplifyBackendOutputs : never; +export type ClientConfigVersionTemplateType = T extends '1.2' ? clientConfigTypesV1_2.AWSAmplifyBackendOutputs : T extends '1.1' ? clientConfigTypesV1_1.AWSAmplifyBackendOutputs : T extends '1' ? clientConfigTypesV1.AWSAmplifyBackendOutputs : never; // @public (undocumented) export type CustomClientConfig = { @@ -329,7 +473,7 @@ export type CustomClientConfig = { export const DEFAULT_CLIENT_CONFIG_VERSION: ClientConfigVersion; // @public -export const generateClientConfig: (backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{ +export const generateClientConfig: (backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{ getS3Client: S3Client; getAmplifyClient: AmplifyClient; getCloudFormationClient: CloudFormationClient; diff --git a/packages/client-config/src/client-config-contributor/client_config_contributor_factory.ts b/packages/client-config/src/client-config-contributor/client_config_contributor_factory.ts index 0c269c82855..6c8fb49d45b 100644 --- a/packages/client-config/src/client-config-contributor/client_config_contributor_factory.ts +++ b/packages/client-config/src/client-config-contributor/client_config_contributor_factory.ts @@ -4,9 +4,11 @@ import { CustomClientConfigContributor as Custom1_1, DataClientConfigContributor as Data1_1, StorageClientConfigContributorV1 as Storage1, - StorageClientConfigContributor as Storage1_1, - VersionContributor as VersionContributor1_1, + StorageClientConfigContributorV1_1 as Storage1_1, + StorageClientConfigContributor as Storage1_2, + VersionContributor as VersionContributor1_2, VersionContributorV1, + VersionContributorV1_1, } from './client_config_contributor_v1.js'; import { ClientConfigContributor } from '../client-config-types/client_config_contributor.js'; @@ -31,11 +33,19 @@ export class ClientConfigContributorFactory { private readonly modelIntrospectionSchemaAdapter: ModelIntrospectionSchemaAdapter ) { this.versionedClientConfigContributors = { + [ClientConfigVersionOption.V1_2]: [ + new Auth1_1(), + new Data1_1(this.modelIntrospectionSchemaAdapter), + new Storage1_2(), + new VersionContributor1_2(), + new Custom1_1(), + ], + [ClientConfigVersionOption.V1_1]: [ new Auth1_1(), new Data1_1(this.modelIntrospectionSchemaAdapter), new Storage1_1(), - new VersionContributor1_1(), + new VersionContributorV1_1(), new Custom1_1(), ], @@ -48,12 +58,12 @@ export class ClientConfigContributorFactory { new Custom1_1(), ], - // Legacy config is derived from V1.1 (latest) of unified default config + // Legacy config is derived from V1.2 (latest) of unified default config [ClientConfigVersionOption.V0]: [ new Auth1_1(), new Data1_1(this.modelIntrospectionSchemaAdapter), - new Storage1_1(), - new VersionContributor1_1(), + new Storage1_2(), + new VersionContributor1_2(), new Custom1_1(), ], }; diff --git a/packages/client-config/src/client-config-contributor/client_config_contributor_v1.test.ts b/packages/client-config/src/client-config-contributor/client_config_contributor_v1.test.ts index dfab479b0ee..022bea9dd49 100644 --- a/packages/client-config/src/client-config-contributor/client_config_contributor_v1.test.ts +++ b/packages/client-config/src/client-config-contributor/client_config_contributor_v1.test.ts @@ -8,7 +8,7 @@ import { } from './client_config_contributor_v1.js'; import { ClientConfig, - clientConfigTypesV1_1, + clientConfigTypesV1_2, } from '../client-config-types/client_config.js'; import assert from 'node:assert'; import { @@ -74,7 +74,7 @@ void describe('auth client config contributor v1', () => { identity_pool_id: 'testIdentityPoolId', unauthenticated_identities_enabled: true, }, - } as Partial + } as Partial ); }); @@ -99,7 +99,7 @@ void describe('auth client config contributor v1', () => { aws_region: 'testRegion', identity_pool_id: 'testIdentityPoolId', }, - } as Partial + } as Partial ); }); @@ -133,7 +133,7 @@ void describe('auth client config contributor v1', () => { require_uppercase: true, }, }, - } as Partial + } as Partial ); }); @@ -166,7 +166,7 @@ void describe('auth client config contributor v1', () => { require_uppercase: false, }, }, - } as Partial + } as Partial ); }); @@ -236,7 +236,7 @@ void describe('auth client config contributor v1', () => { response_type: 'code', }, }, - } as Partial + } as Partial ); }); @@ -300,7 +300,7 @@ void describe('auth client config contributor v1', () => { response_type: 'code', }, }, - } as Partial + } as Partial ); }); @@ -358,7 +358,7 @@ void describe('auth client config contributor v1', () => { response_type: 'code', }, }, - } as Pick; + } as Pick; void it('returns translated config when mfa is disabled', () => { const contributor = new AuthClientConfigContributor(); @@ -459,7 +459,7 @@ void describe('data client config contributor v1', () => { url: 'testApiEndpoint', aws_region: 'us-east-1', }, - } as Partial); + } as Partial); }); void it('returns translated config with model introspection when resolvable', async () => { @@ -507,7 +507,7 @@ void describe('data client config contributor v1', () => { enums: {}, }, }, - } as Partial); + } as Partial); }); }); @@ -540,6 +540,12 @@ void describe('storage client config contributor v1', () => { name: 'testName', bucketName: 'testBucketName', storageRegion: 'testRegion', + paths: { + 'path/*': { + guest: ['get', 'list'], + authenticated: ['read', 'write', 'delete'], + }, + }, }), ]); assert.deepStrictEqual( @@ -562,6 +568,12 @@ void describe('storage client config contributor v1', () => { name: 'testName', bucket_name: 'testBucketName', aws_region: 'testRegion', + paths: { + 'path/*': { + guest: ['get', 'list'], + authenticated: ['read', 'write', 'delete'], + }, + }, }, ], }, @@ -613,6 +625,6 @@ void describe('Custom client config contributor v1', () => { void describe('Custom client config contributor v1', () => { void it('contributes the version correctly', () => { - assert.deepEqual(new VersionContributor().contribute(), { version: '1.1' }); + assert.deepEqual(new VersionContributor().contribute(), { version: '1.2' }); }); }); diff --git a/packages/client-config/src/client-config-contributor/client_config_contributor_v1.ts b/packages/client-config/src/client-config-contributor/client_config_contributor_v1.ts index 30ce2a06492..425775a9783 100644 --- a/packages/client-config/src/client-config-contributor/client_config_contributor_v1.ts +++ b/packages/client-config/src/client-config-contributor/client_config_contributor_v1.ts @@ -9,18 +9,34 @@ import { import { ClientConfig, ClientConfigVersionOption, + clientConfigTypesV1, clientConfigTypesV1_1, + clientConfigTypesV1_2, } from '../client-config-types/client_config.js'; import { ModelIntrospectionSchemaAdapter } from '../model_introspection_schema_adapter.js'; import { AwsAppsyncAuthorizationType } from '../client-config-schema/client_config_v1.1.js'; +import { AmplifyStorageAccessRule } from '../client-config-schema/client_config_v1.2.js'; // All categories client config contributors are included here to mildly enforce them using // the same schema (version and other types) /** - * Translator for the version number of ClientConfig of V1.1 + * Translator for the version number of ClientConfig of V1.2 */ export class VersionContributor implements ClientConfigContributor { + /** + * Return the version of the schema types that this contributor uses + */ + contribute = (): ClientConfig => { + return { version: ClientConfigVersionOption.V1_2 }; + }; +} + +/** + * Translator for the version number of ClientConfig of V1.1 + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export class VersionContributorV1_1 implements ClientConfigContributor { /** * Return the version of the schema types that this contributor uses */ @@ -66,7 +82,7 @@ export class AuthClientConfigContributor implements ClientConfigContributor { obj[key] = JSON.parse(value); }; - const authClientConfig: Partial = + const authClientConfig: Partial = {}; authClientConfig.auth = { @@ -257,9 +273,59 @@ export class DataClientConfigContributor implements ClientConfigContributor { } /** - * Translator for the Storage portion of ClientConfig in V1.1 + * Translator for the Storage portion of ClientConfig in V1.2 */ +// eslint-disable-next-line @typescript-eslint/naming-convention export class StorageClientConfigContributor implements ClientConfigContributor { + /** + * Given some BackendOutput, contribute the Storage portion of the client config + */ + contribute = ({ + [storageOutputKey]: storageOutput, + }: UnifiedBackendOutput): Partial | Record => { + if (storageOutput === undefined) { + return {}; + } + const config: Partial = {}; + const bucketsStringArray = JSON.parse( + storageOutput.payload.buckets ?? '[]' + ); + config.storage = { + aws_region: storageOutput.payload.storageRegion, + bucket_name: storageOutput.payload.bucketName, + buckets: bucketsStringArray + .map((b: string) => JSON.parse(b)) + .map( + ({ + name, + bucketName, + storageRegion, + paths, + }: { + name: string; + bucketName: string; + storageRegion: string; + paths: Record; + }) => ({ + name, + bucket_name: bucketName, + aws_region: storageRegion, + paths, + }) + ), + }; + + return config; + }; +} + +/** + * Translator for the Storage portion of ClientConfig in V1.1 + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export class StorageClientConfigContributorV1_1 + implements ClientConfigContributor +{ /** * Given some BackendOutput, contribute the Storage portion of the client config */ @@ -314,7 +380,7 @@ export class StorageClientConfigContributorV1 if (storageOutput === undefined) { return {}; } - const config: Partial = {}; + const config: Partial = {}; config.storage = { aws_region: storageOutput.payload.storageRegion, diff --git a/packages/client-config/src/client-config-schema/client_config_v1.2.ts b/packages/client-config/src/client-config-schema/client_config_v1.2.ts new file mode 100644 index 00000000000..f9aa397d970 --- /dev/null +++ b/packages/client-config/src/client-config-schema/client_config_v1.2.ts @@ -0,0 +1,272 @@ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +/** + * Amazon Cognito standard attributes for users -- https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html + */ +export type AmazonCognitoStandardAttributes = + | 'address' + | 'birthdate' + | 'email' + | 'family_name' + | 'gender' + | 'given_name' + | 'locale' + | 'middle_name' + | 'name' + | 'nickname' + | 'phone_number' + | 'picture' + | 'preferred_username' + | 'profile' + | 'sub' + | 'updated_at' + | 'website' + | 'zoneinfo'; +export type AwsRegion = string; +/** + * List of supported auth types for AWS AppSync + */ +export type AwsAppsyncAuthorizationType = + | 'AMAZON_COGNITO_USER_POOLS' + | 'API_KEY' + | 'AWS_IAM' + | 'AWS_LAMBDA' + | 'OPENID_CONNECT'; +/** + * supported channels for Amazon Pinpoint + */ +export type AmazonPinpointChannels = + | 'IN_APP_MESSAGING' + | 'FCM' + | 'APNS' + | 'EMAIL' + | 'SMS'; +export type AmplifyStorageAccessActions = + | 'read' + | 'get' + | 'list' + | 'write' + | 'delete'; + +/** + * Config format for Amplify Gen 2 client libraries to communicate with backend services. + */ +export interface AWSAmplifyBackendOutputs { + /** + * Version of this schema + */ + version: '1.2'; + /** + * Outputs manually specified by developers for use with frontend library + */ + analytics?: { + amazon_pinpoint?: { + /** + * AWS Region of Amazon Pinpoint resources + */ + aws_region: string; + app_id: string; + }; + }; + /** + * Outputs generated from defineAuth + */ + auth?: { + /** + * AWS Region of Amazon Cognito resources + */ + aws_region: string; + /** + * Cognito User Pool ID + */ + user_pool_id: string; + /** + * Cognito User Pool Client ID + */ + user_pool_client_id: string; + /** + * Cognito Identity Pool ID + */ + identity_pool_id?: string; + /** + * Cognito User Pool password policy + */ + password_policy?: { + min_length: number; + require_numbers: boolean; + require_lowercase: boolean; + require_uppercase: boolean; + require_symbols: boolean; + }; + oauth?: { + /** + * Identity providers set on Cognito User Pool + * + * @minItems 0 + */ + identity_providers: ( + | 'GOOGLE' + | 'FACEBOOK' + | 'LOGIN_WITH_AMAZON' + | 'SIGN_IN_WITH_APPLE' + )[]; + /** + * Domain used for identity providers + */ + domain: string; + /** + * @minItems 0 + */ + scopes: string[]; + /** + * URIs used to redirect after signing in using an identity provider + * + * @minItems 1 + */ + redirect_sign_in_uri: string[]; + /** + * URIs used to redirect after signing out + * + * @minItems 1 + */ + redirect_sign_out_uri: string[]; + response_type: 'code' | 'token'; + }; + /** + * Cognito User Pool standard attributes required for signup + * + * @minItems 0 + */ + standard_required_attributes?: AmazonCognitoStandardAttributes[]; + /** + * Cognito User Pool username attributes + * + * @minItems 1 + */ + username_attributes?: ('email' | 'phone_number' | 'username')[]; + user_verification_types?: ('email' | 'phone_number')[]; + unauthenticated_identities_enabled?: boolean; + mfa_configuration?: 'NONE' | 'OPTIONAL' | 'REQUIRED'; + mfa_methods?: ('SMS' | 'TOTP')[]; + }; + /** + * Outputs generated from defineData + */ + data?: { + aws_region: AwsRegion; + /** + * AppSync endpoint URL + */ + url: string; + /** + * generated model introspection schema for use with generateClient + */ + model_introspection?: { + [k: string]: unknown; + }; + api_key?: string; + default_authorization_type: AwsAppsyncAuthorizationType; + authorization_types: AwsAppsyncAuthorizationType[]; + }; + /** + * Outputs manually specified by developers for use with frontend library + */ + geo?: { + /** + * AWS Region of Amazon Location Service resources + */ + aws_region: string; + /** + * Maps from Amazon Location Service + */ + maps?: { + items: { + [k: string]: AmazonLocationServiceConfig; + }; + default: string; + }; + /** + * Location search (search by places, addresses, coordinates) + */ + search_indices?: { + /** + * @minItems 1 + */ + items: string[]; + default: string; + }; + /** + * Geofencing (visualize virtual perimeters) + */ + geofence_collections?: { + /** + * @minItems 1 + */ + items: string[]; + default: string; + }; + }; + /** + * Outputs manually specified by developers for use with frontend library + */ + notifications?: { + aws_region: AwsRegion; + amazon_pinpoint_app_id: string; + /** + * @minItems 1 + */ + channels: AmazonPinpointChannels[]; + }; + /** + * Outputs generated from defineStorage + */ + storage?: { + aws_region: AwsRegion; + bucket_name: string; + buckets?: AmplifyStorageBucket[]; + }; + /** + * Outputs generated from backend.addOutput({ custom: }) + */ + custom?: { + [k: string]: unknown; + }; +} +/** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` ".*". + */ +export interface AmazonLocationServiceConfig { + /** + * Map resource name + */ + name?: string; + /** + * Map style + */ + style?: string; +} +export interface AmplifyStorageBucket { + name: string; + bucket_name: string; + aws_region: string; + paths?: { + [k: string]: AmplifyStorageAccessRule; + }; +} +/** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` ".*". + */ +export interface AmplifyStorageAccessRule { + guest?: AmplifyStorageAccessActions[]; + authenticated?: AmplifyStorageAccessActions[]; + groups?: AmplifyStorageAccessActions[]; + entity?: AmplifyStorageAccessActions[]; + resource?: AmplifyStorageAccessActions[]; +} diff --git a/packages/client-config/src/client-config-schema/schema_v1.2.json b/packages/client-config/src/client-config-schema/schema_v1.2.json new file mode 100644 index 00000000000..b85a10ff30c --- /dev/null +++ b/packages/client-config/src/client-config-schema/schema_v1.2.json @@ -0,0 +1,476 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://amplify.aws/2024-02/outputs-schema.json", + "title": "AWS Amplify Backend Outputs", + "description": "Config format for Amplify Gen 2 client libraries to communicate with backend services.", + "type": "object", + "additionalProperties": false, + "properties": { + "$schema": { + "description": "JSON schema", + "type": "string" + }, + "version": { + "description": "Version of this schema", + "const": "1.2" + }, + "analytics": { + "description": "Outputs manually specified by developers for use with frontend library", + "type": "object", + "additionalProperties": false, + "properties": { + "amazon_pinpoint": { + "type": "object", + "additionalProperties": false, + "properties": { + "aws_region": { + "description": "AWS Region of Amazon Pinpoint resources", + "$ref": "#/$defs/aws_region" + }, + "app_id": { + "type": "string" + } + }, + "required": ["aws_region", "app_id"] + } + } + }, + "auth": { + "description": "Outputs generated from defineAuth", + "type": "object", + "additionalProperties": false, + "properties": { + "aws_region": { + "description": "AWS Region of Amazon Cognito resources", + "$ref": "#/$defs/aws_region" + }, + "user_pool_id": { + "description": "Cognito User Pool ID", + "type": "string" + }, + "user_pool_client_id": { + "description": "Cognito User Pool Client ID", + "type": "string" + }, + "identity_pool_id": { + "description": "Cognito Identity Pool ID", + "type": "string" + }, + "password_policy": { + "description": "Cognito User Pool password policy", + "type": "object", + "additionalProperties": false, + "properties": { + "min_length": { + "type": "integer", + "minimum": 6, + "maximum": 99 + }, + "require_numbers": { + "type": "boolean" + }, + "require_lowercase": { + "type": "boolean" + }, + "require_uppercase": { + "type": "boolean" + }, + "require_symbols": { + "type": "boolean" + } + }, + "required": [ + "min_length", + "require_numbers", + "require_lowercase", + "require_uppercase", + "require_symbols" + ] + }, + "oauth": { + "type": "object", + "additionalProperties": false, + "properties": { + "identity_providers": { + "description": "Identity providers set on Cognito User Pool", + "type": "array", + "items": { + "type": "string", + "enum": [ + "GOOGLE", + "FACEBOOK", + "LOGIN_WITH_AMAZON", + "SIGN_IN_WITH_APPLE" + ] + }, + "minItems": 0, + "uniqueItems": true + }, + "domain": { + "description": "Domain used for identity providers", + "type": "string" + }, + "scopes": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 0, + "uniqueItems": true + }, + "redirect_sign_in_uri": { + "description": "URIs used to redirect after signing in using an identity provider", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "redirect_sign_out_uri": { + "description": "URIs used to redirect after signing out", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "response_type": { + "type": "string", + "enum": ["code", "token"] + } + }, + "required": [ + "identity_providers", + "domain", + "scopes", + "redirect_sign_in_uri", + "redirect_sign_out_uri", + "response_type" + ] + }, + "standard_required_attributes": { + "description": "Cognito User Pool standard attributes required for signup", + "type": "array", + "items": { + "$ref": "#/$defs/amazon_cognito_standard_attributes" + }, + "minItems": 0, + "uniqueItems": true + }, + "username_attributes": { + "description": "Cognito User Pool username attributes", + "type": "array", + "items": { + "type": "string", + "enum": ["email", "phone_number", "username"] + }, + "minItems": 1, + "uniqueItems": true + }, + "user_verification_types": { + "type": "array", + "items": { + "type": "string", + "enum": ["email", "phone_number"] + } + }, + "unauthenticated_identities_enabled": { + "type": "boolean", + "default": true + }, + "mfa_configuration": { + "type": "string", + "enum": ["NONE", "OPTIONAL", "REQUIRED"] + }, + "mfa_methods": { + "type": "array", + "items": { + "enum": ["SMS", "TOTP"] + } + } + }, + "required": ["aws_region", "user_pool_id", "user_pool_client_id"] + }, + "data": { + "description": "Outputs generated from defineData", + "type": "object", + "additionalProperties": false, + "properties": { + "aws_region": { + "$ref": "#/$defs/aws_region" + }, + "url": { + "description": "AppSync endpoint URL", + "type": "string" + }, + "model_introspection": { + "description": "generated model introspection schema for use with generateClient", + "type": "object" + }, + "api_key": { + "type": "string" + }, + "default_authorization_type": { + "$ref": "#/$defs/aws_appsync_authorization_type" + }, + "authorization_types": { + "type": "array", + "items": { + "$ref": "#/$defs/aws_appsync_authorization_type" + } + } + }, + "required": [ + "aws_region", + "url", + "default_authorization_type", + "authorization_types" + ] + }, + "geo": { + "description": "Outputs manually specified by developers for use with frontend library", + "type": "object", + "additionalProperties": false, + "properties": { + "aws_region": { + "description": "AWS Region of Amazon Location Service resources", + "$ref": "#/$defs/aws_region" + }, + "maps": { + "description": "Maps from Amazon Location Service", + "type": "object", + "additionalProperties": false, + "properties": { + "items": { + "type": "object", + "additionalProperties": false, + "propertyNames": { + "description": "Amazon Location Service Map name", + "type": "string" + }, + "patternProperties": { + ".*": { + "$ref": "#/$defs/amazon_location_service_config" + } + } + }, + "default": { + "type": "string" + } + }, + "required": ["items", "default"] + }, + "search_indices": { + "description": "Location search (search by places, addresses, coordinates)", + "type": "object", + "additionalProperties": false, + "properties": { + "items": { + "type": "array", + "uniqueItems": true, + "minItems": 1, + "items": { + "description": "Actual search name", + "type": "string" + } + }, + "default": { + "type": "string" + } + }, + "required": ["items", "default"] + }, + "geofence_collections": { + "description": "Geofencing (visualize virtual perimeters)", + "type": "object", + "additionalProperties": false, + "properties": { + "items": { + "type": "array", + "uniqueItems": true, + "minItems": 1, + "items": { + "description": "Geofence name", + "type": "string" + } + }, + "default": { + "type": "string" + } + }, + "required": ["items", "default"] + } + }, + "required": ["aws_region"] + }, + "notifications": { + "type": "object", + "description": "Outputs manually specified by developers for use with frontend library", + "additionalProperties": false, + "properties": { + "aws_region": { + "$ref": "#/$defs/aws_region" + }, + "amazon_pinpoint_app_id": { + "type": "string" + }, + "channels": { + "type": "array", + "items": { + "$ref": "#/$defs/amazon_pinpoint_channels" + }, + "minItems": 1, + "uniqueItems": true + } + }, + "required": ["aws_region", "amazon_pinpoint_app_id", "channels"] + }, + "storage": { + "type": "object", + "description": "Outputs generated from defineStorage", + "additionalProperties": false, + "properties": { + "aws_region": { + "$ref": "#/$defs/aws_region" + }, + "bucket_name": { + "type": "string" + }, + "buckets": { + "type": "array", + "items": { + "$ref": "#/$defs/amplify_storage_bucket" + } + } + }, + "required": ["aws_region", "bucket_name"] + }, + "custom": { + "description": "Outputs generated from backend.addOutput({ custom: })", + "type": "object" + } + }, + "required": ["version"], + "$defs": { + "amplify_storage_access_actions": { + "type": "string", + "enum": ["read", "get", "list", "write", "delete"] + }, + "amplify_storage_access_rule": { + "type": "object", + "additionalProperties": false, + "properties": { + "guest": { + "type": "array", + "items": { + "$ref": "#/$defs/amplify_storage_access_actions" + } + }, + "authenticated": { + "type": "array", + "items": { + "$ref": "#/$defs/amplify_storage_access_actions" + } + }, + "groups": { + "type": "array", + "items": { + "$ref": "#/$defs/amplify_storage_access_actions" + } + }, + "entity": { + "type": "array", + "items": { + "$ref": "#/$defs/amplify_storage_access_actions" + } + }, + "resource": { + "type": "array", + "items": { + "$ref": "#/$defs/amplify_storage_access_actions" + } + } + } + }, + "amplify_storage_bucket": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "bucket_name": { + "type": "string" + }, + "aws_region": { + "type": "string" + }, + "paths": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + ".*": { + "$ref": "#/$defs/amplify_storage_access_rule" + } + } + } + }, + "required": ["bucket_name", "aws_region", "name"] + }, + "aws_region": { + "type": "string" + }, + "amazon_cognito_standard_attributes": { + "description": "Amazon Cognito standard attributes for users -- https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html", + "type": "string", + "enum": [ + "address", + "birthdate", + "email", + "family_name", + "gender", + "given_name", + "locale", + "middle_name", + "name", + "nickname", + "phone_number", + "picture", + "preferred_username", + "profile", + "sub", + "updated_at", + "website", + "zoneinfo" + ] + }, + "aws_appsync_authorization_type": { + "description": "List of supported auth types for AWS AppSync", + "type": "string", + "enum": [ + "AMAZON_COGNITO_USER_POOLS", + "API_KEY", + "AWS_IAM", + "AWS_LAMBDA", + "OPENID_CONNECT" + ] + }, + "amazon_location_service_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "style": { + "description": "Map style", + "type": "string" + } + } + }, + "amazon_pinpoint_channels": { + "description": "supported channels for Amazon Pinpoint", + "type": "string", + "enum": ["IN_APP_MESSAGING", "FCM", "APNS", "EMAIL", "SMS"] + } + } +} diff --git a/packages/client-config/src/client-config-types/client_config.ts b/packages/client-config/src/client-config-types/client_config.ts index d9cc71e1aac..5467484d672 100644 --- a/packages/client-config/src/client-config-types/client_config.ts +++ b/packages/client-config/src/client-config-types/client_config.ts @@ -9,8 +9,10 @@ import { NotificationsClientConfig } from './notifications_client_config.js'; // Versions of new unified config schemas import * as clientConfigTypesV1 from '../client-config-schema/client_config_v1.js'; -// eslint-disable-next-line @typescript-eslint/naming-convention +/* eslint-disable @typescript-eslint/naming-convention */ import * as clientConfigTypesV1_1 from '../client-config-schema/client_config_v1.1.js'; +import * as clientConfigTypesV1_2 from '../client-config-schema/client_config_v1.2.js'; +/* eslint-enable @typescript-eslint/naming-convention */ /** * Merged type of all category client config legacy types @@ -32,22 +34,24 @@ export type ClientConfigLegacy = Partial< * ClientConfig = clientConfigTypesV1.AWSAmplifyBackendOutputs | clientConfigTypesV2.AWSAmplifyBackendOutputs; */ export type ClientConfig = + | clientConfigTypesV1_2.AWSAmplifyBackendOutputs | clientConfigTypesV1_1.AWSAmplifyBackendOutputs | clientConfigTypesV1.AWSAmplifyBackendOutputs; -export { clientConfigTypesV1, clientConfigTypesV1_1 }; +export { clientConfigTypesV1, clientConfigTypesV1_1, clientConfigTypesV1_2 }; export enum ClientConfigVersionOption { V0 = '0', // Legacy client config V1 = '1', V1_1 = '1.1', + V1_2 = '1.2', } export type ClientConfigVersion = `${ClientConfigVersionOption}`; // Client config version that is generated by default if customers didn't specify one export const DEFAULT_CLIENT_CONFIG_VERSION: ClientConfigVersion = - ClientConfigVersionOption.V1_1; + ClientConfigVersionOption.V1_2; /** * Return type of `getClientConfig`. This types narrow the returned client config version @@ -60,7 +64,9 @@ export const DEFAULT_CLIENT_CONFIG_VERSION: ClientConfigVersion = * ? clientConfigTypesV2.AWSAmplifyBackendOutputs * : never; */ -export type ClientConfigVersionTemplateType = T extends '1.1' +export type ClientConfigVersionTemplateType = T extends '1.2' + ? clientConfigTypesV1_2.AWSAmplifyBackendOutputs + : T extends '1.1' ? clientConfigTypesV1_1.AWSAmplifyBackendOutputs : T extends '1' ? clientConfigTypesV1.AWSAmplifyBackendOutputs diff --git a/packages/client-config/src/client-config-writer/client_config_formatter_default.test.ts b/packages/client-config/src/client-config-writer/client_config_formatter_default.test.ts index 72023851a45..e605ef8967a 100644 --- a/packages/client-config/src/client-config-writer/client_config_formatter_default.test.ts +++ b/packages/client-config/src/client-config-writer/client_config_formatter_default.test.ts @@ -13,7 +13,7 @@ void describe('client config formatter', () => { const sampleIdentityPoolId = 'test_identity_pool_id'; const sampleUserPoolClientId = 'test_user_pool_client_id'; const clientConfig: ClientConfig = { - version: '1.1', + version: '1.2', auth: { aws_region: sampleRegion, identity_pool_id: sampleIdentityPoolId, @@ -23,7 +23,7 @@ void describe('client config formatter', () => { }; const expectedConfigReturned: ClientConfig = { - version: '1.1', + version: '1.2', auth: { aws_region: sampleRegion, identity_pool_id: sampleIdentityPoolId, diff --git a/packages/client-config/src/client-config-writer/client_config_formatter_legacy.test.ts b/packages/client-config/src/client-config-writer/client_config_formatter_legacy.test.ts index 479c542efa4..737257273d9 100644 --- a/packages/client-config/src/client-config-writer/client_config_formatter_legacy.test.ts +++ b/packages/client-config/src/client-config-writer/client_config_formatter_legacy.test.ts @@ -20,7 +20,7 @@ void describe('client config formatter', () => { const sampleUserPoolId = randomUUID(); const clientConfig: ClientConfig = { - version: '1.1', + version: '1.2', auth: { aws_region: sampleRegion, identity_pool_id: sampleIdentityPoolId, diff --git a/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.test.ts b/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.test.ts index 1d564740a09..dda5c288a99 100644 --- a/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.test.ts +++ b/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.test.ts @@ -26,7 +26,7 @@ void describe('ClientConfigLegacyConverter', () => { version: '3' as any, }), new AmplifyFault('UnsupportedClientConfigVersionFault', { - message: 'Only version 1.1 of ClientConfig is supported.', + message: 'Only version 1.2 of ClientConfig is supported.', }) ); }); @@ -35,7 +35,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); const v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_1, + version: ClientConfigVersionOption.V1_2, auth: { identity_pool_id: 'testIdentityPoolId', user_pool_id: 'testUserPoolId', @@ -133,7 +133,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); const v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_1, + version: ClientConfigVersionOption.V1_2, data: { aws_region: 'testRegion', url: 'testUrl', @@ -274,7 +274,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); const v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_1, + version: ClientConfigVersionOption.V1_2, storage: { aws_region: 'testRegion', bucket_name: 'testBucket', @@ -296,7 +296,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); const v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_1, + version: ClientConfigVersionOption.V1_2, custom: { customKey: { customNestedKey: { @@ -327,7 +327,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); const v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_1, + version: ClientConfigVersionOption.V1_2, analytics: { amazon_pinpoint: { aws_region: 'testRegion', @@ -356,7 +356,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); const v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_1, + version: ClientConfigVersionOption.V1_2, geo: { aws_region: 'testRegion', maps: { @@ -409,7 +409,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); let v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_1, + version: ClientConfigVersionOption.V1_2, notifications: { amazon_pinpoint_app_id: 'testAppId', aws_region: 'testRegion', @@ -452,7 +452,7 @@ void describe('ClientConfigLegacyConverter', () => { // both APNS and FCM cannot be specified together as they both map to Push. v1Config = { - version: ClientConfigVersionOption.V1_1, + version: ClientConfigVersionOption.V1_2, notifications: { amazon_pinpoint_app_id: 'testAppId', aws_region: 'testRegion', diff --git a/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.ts b/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.ts index db61d1b1de5..3131b041edb 100644 --- a/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.ts +++ b/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.ts @@ -2,7 +2,7 @@ import { AmplifyFault } from '@aws-amplify/platform-core'; import { ClientConfig, ClientConfigLegacy, - clientConfigTypesV1_1, + clientConfigTypesV1_2, } from '../client-config-types/client_config.js'; import { @@ -22,10 +22,10 @@ export class ClientConfigLegacyConverter { * Converts client config to a shape consumable by legacy libraries. */ convertToLegacyConfig = (clientConfig: ClientConfig): ClientConfigLegacy => { - // We can only convert from V1.1 of ClientConfig. For everything else, throw - if (!this.isClientConfigV1_1(clientConfig)) { + // We can only convert from V1.2 of ClientConfig. For everything else, throw + if (!this.isClientConfigV1_2(clientConfig)) { throw new AmplifyFault('UnsupportedClientConfigVersionFault', { - message: 'Only version 1.1 of ClientConfig is supported.', + message: 'Only version 1.2 of ClientConfig is supported.', }); } @@ -274,9 +274,9 @@ export class ClientConfigLegacyConverter { }; // eslint-disable-next-line @typescript-eslint/naming-convention - isClientConfigV1_1 = ( + isClientConfigV1_2 = ( clientConfig: ClientConfig - ): clientConfig is clientConfigTypesV1_1.AWSAmplifyBackendOutputs => { - return clientConfig.version === '1.1'; + ): clientConfig is clientConfigTypesV1_2.AWSAmplifyBackendOutputs => { + return clientConfig.version === '1.2'; }; } diff --git a/packages/client-config/src/client-config-writer/client_config_writer.test.ts b/packages/client-config/src/client-config-writer/client_config_writer.test.ts index 69697ebe3e6..7f3771224de 100644 --- a/packages/client-config/src/client-config-writer/client_config_writer.test.ts +++ b/packages/client-config/src/client-config-writer/client_config_writer.test.ts @@ -42,7 +42,7 @@ void describe('client config writer', () => { }); const clientConfig: ClientConfig = { - version: '1.1', + version: '1.2', auth: { aws_region: sampleRegion, identity_pool_id: sampleIdentityPoolId, diff --git a/packages/client-config/src/generate_empty_client_config_to_file.test.ts b/packages/client-config/src/generate_empty_client_config_to_file.test.ts index 2552a1309ac..34dbbc9f82e 100644 --- a/packages/client-config/src/generate_empty_client_config_to_file.test.ts +++ b/packages/client-config/src/generate_empty_client_config_to_file.test.ts @@ -30,15 +30,15 @@ void describe('generate empty client config to file', () => { path.join(process.cwd(), 'userOutDir', 'amplifyconfiguration.ts') ); }); - void it('correctly generates an empty file for client config version 1.1', async () => { + void it('correctly generates an empty file for client config version 1.2', async () => { await generateEmptyClientConfigToFile( - ClientConfigVersionOption.V1_1, + ClientConfigVersionOption.V1_2, 'userOutDir' ); assert.equal(writeFileMock.mock.callCount(), 1); assert.deepStrictEqual( writeFileMock.mock.calls[0].arguments[1], - `{\n "version": "1.1"\n}` + `{\n "version": "1.2"\n}` ); assert.deepStrictEqual( writeFileMock.mock.calls[0].arguments[0], diff --git a/packages/client-config/src/generate_empty_client_config_to_file.ts b/packages/client-config/src/generate_empty_client_config_to_file.ts index 039cf781c4f..260dad61739 100644 --- a/packages/client-config/src/generate_empty_client_config_to_file.ts +++ b/packages/client-config/src/generate_empty_client_config_to_file.ts @@ -15,7 +15,7 @@ export const generateEmptyClientConfigToFile = async ( format?: ClientConfigFormat ): Promise => { const clientConfig: ClientConfig = { - version: '1.1', + version: '1.2', }; return writeClientConfigToFile(clientConfig, version, outDir, format); }; diff --git a/packages/client-config/src/unified_client_config_generator.test.ts b/packages/client-config/src/unified_client_config_generator.test.ts index 4e6f0b0aed5..c466311a68f 100644 --- a/packages/client-config/src/unified_client_config_generator.test.ts +++ b/packages/client-config/src/unified_client_config_generator.test.ts @@ -26,6 +26,115 @@ const stubClientProvider = { }; void describe('UnifiedClientConfigGenerator', () => { void describe('generateClientConfig', () => { + void it('transforms backend output into client config for V1.2', async () => { + const stubOutput: UnifiedBackendOutput = { + [platformOutputKey]: { + version: '1', + payload: { + deploymentType: 'branch', + region: 'us-east-1', + }, + }, + [authOutputKey]: { + version: '1', + payload: { + identityPoolId: 'testIdentityPoolId', + userPoolId: 'testUserPoolId', + webClientId: 'testWebClientId', + authRegion: 'us-east-1', + passwordPolicyMinLength: '8', + passwordPolicyRequirements: + '["REQUIRES_NUMBERS","REQUIRES_LOWERCASE","REQUIRES_UPPERCASE"]', + mfaTypes: '["SMS","TOTP"]', + mfaConfiguration: 'OPTIONAL', + verificationMechanisms: '["email","phone_number"]', + usernameAttributes: '["email"]', + signupAttributes: '["email"]', + allowUnauthenticatedIdentities: 'true', + }, + }, + [graphqlOutputKey]: { + version: '1', + payload: { + awsAppsyncApiEndpoint: 'testApiEndpoint', + awsAppsyncRegion: 'us-east-1', + awsAppsyncAuthenticationType: 'API_KEY', + awsAppsyncAdditionalAuthenticationTypes: 'API_KEY', + awsAppsyncConflictResolutionMode: 'AUTO_MERGE', + awsAppsyncApiKey: 'testApiKey', + awsAppsyncApiId: 'testApiId', + amplifyApiModelSchemaS3Uri: 'testApiSchemaUri', + }, + }, + [customOutputKey]: { + version: '1', + payload: { + customOutputs: JSON.stringify({ + custom: { + output1: 'val1', + output2: 'val2', + }, + }), + }, + }, + }; + const outputRetrieval = mock.fn(async () => stubOutput); + const modelSchemaAdapter = new ModelIntrospectionSchemaAdapter( + stubClientProvider + ); + + mock.method( + modelSchemaAdapter, + 'getModelIntrospectionSchemaFromS3Uri', + () => undefined + ); + const configContributors = new ClientConfigContributorFactory( + modelSchemaAdapter + ).getContributors('1.2'); + const clientConfigGenerator = new UnifiedClientConfigGenerator( + outputRetrieval, + configContributors + ); + const result = await clientConfigGenerator.generateClientConfig(); + const expectedClientConfig: ClientConfig = { + auth: { + user_pool_id: 'testUserPoolId', + aws_region: 'us-east-1', + user_pool_client_id: 'testWebClientId', + identity_pool_id: 'testIdentityPoolId', + mfa_methods: ['SMS', 'TOTP'], + standard_required_attributes: ['email'], + username_attributes: ['email'], + user_verification_types: ['email', 'phone_number'], + mfa_configuration: 'OPTIONAL', + + password_policy: { + min_length: 8, + require_lowercase: true, + require_numbers: true, + require_symbols: false, + require_uppercase: true, + }, + + unauthenticated_identities_enabled: true, + }, + data: { + url: 'testApiEndpoint', + aws_region: 'us-east-1', + api_key: 'testApiKey', + default_authorization_type: 'API_KEY', + authorization_types: ['API_KEY'], + }, + custom: { + output1: 'val1', + output2: 'val2', + }, + version: '1.2', + }; + + assert.deepStrictEqual(result, expectedClientConfig); + }); + void it('transforms backend output into client config for V1.1', async () => { const stubOutput: UnifiedBackendOutput = { [platformOutputKey]: { @@ -297,7 +406,7 @@ void describe('UnifiedClientConfigGenerator', () => { ); const configContributors = new ClientConfigContributorFactory( modelSchemaAdapter - ).getContributors('1.1'); //Generate with new configuration format + ).getContributors('1.2'); //Generate with new configuration format const clientConfigGenerator = new UnifiedClientConfigGenerator( outputRetrieval, configContributors @@ -329,7 +438,7 @@ void describe('UnifiedClientConfigGenerator', () => { output1: 'val1', output2: 'val2', }, - version: '1.1', // The max version prevails + version: '1.2', // The max version prevails }; assert.deepStrictEqual(result, expectedClientConfig); @@ -368,7 +477,7 @@ void describe('UnifiedClientConfigGenerator', () => { ); const configContributors = new ClientConfigContributorFactory( modelSchemaAdapter - ).getContributors('1.1'); + ).getContributors('1.2'); const clientConfigGenerator = new UnifiedClientConfigGenerator( outputRetrieval, @@ -400,7 +509,7 @@ void describe('UnifiedClientConfigGenerator', () => { const configContributors = new ClientConfigContributorFactory( modelSchemaAdapter - ).getContributors('1.1'); + ).getContributors('1.2'); const clientConfigGenerator = new UnifiedClientConfigGenerator( outputRetrieval, @@ -432,7 +541,7 @@ void describe('UnifiedClientConfigGenerator', () => { const configContributors = new ClientConfigContributorFactory( modelSchemaAdapter - ).getContributors('1.1'); + ).getContributors('1.2'); const clientConfigGenerator = new UnifiedClientConfigGenerator( outputRetrieval, @@ -495,7 +604,7 @@ void describe('UnifiedClientConfigGenerator', () => { const configContributors = new ClientConfigContributorFactory( modelSchemaAdapter - ).getContributors('1.1'); + ).getContributors('1.2'); const clientConfigGenerator = new UnifiedClientConfigGenerator( outputRetrieval, @@ -528,7 +637,7 @@ void describe('UnifiedClientConfigGenerator', () => { const configContributors = new ClientConfigContributorFactory( modelSchemaAdapter - ).getContributors('1.1'); + ).getContributors('1.2'); const clientConfigGenerator = new UnifiedClientConfigGenerator( outputRetrieval, diff --git a/packages/integration-tests/src/amplify_auth_credentials_factory.ts b/packages/integration-tests/src/amplify_auth_credentials_factory.ts index 90773c67117..7faffce0666 100644 --- a/packages/integration-tests/src/amplify_auth_credentials_factory.ts +++ b/packages/integration-tests/src/amplify_auth_credentials_factory.ts @@ -33,7 +33,7 @@ export class AmplifyAuthCredentialsFactory { */ constructor( private readonly cognitoIdentityProviderClient: CognitoIdentityProviderClient, - authConfig: NonNullable['auth']> + authConfig: NonNullable['auth']> ) { if (!authConfig.identity_pool_id) { throw new Error('Client config must have identity pool id.'); diff --git a/packages/integration-tests/src/test-project-setup/access_testing_project.ts b/packages/integration-tests/src/test-project-setup/access_testing_project.ts index b41d4bd1b12..e44010f82da 100644 --- a/packages/integration-tests/src/test-project-setup/access_testing_project.ts +++ b/packages/integration-tests/src/test-project-setup/access_testing_project.ts @@ -147,7 +147,7 @@ class AccessTestingProjectTestProject extends TestProjectBase { backendId: BackendIdentifier ): Promise { await super.assertPostDeployment(backendId); - const clientConfig = await generateClientConfig(backendId, '1.1'); + const clientConfig = await generateClientConfig(backendId, '1.2'); await this.assertDifferentCognitoInstanceCannotAssumeAmplifyRoles( clientConfig ); @@ -160,7 +160,7 @@ class AccessTestingProjectTestProject extends TestProjectBase { * I.e. roles not created by auth construct. */ private assertGenericIamRolesAccessToData = async ( - clientConfig: ClientConfigVersionTemplateType<'1.1'> + clientConfig: ClientConfigVersionTemplateType<'1.2'> ) => { if (!clientConfig.custom) { throw new Error('Client config is missing custom section'); @@ -262,7 +262,7 @@ class AccessTestingProjectTestProject extends TestProjectBase { * This asserts that authenticated and unauthenticated roles have relevant access to data API. */ private assertAmplifyAuthAccessToData = async ( - clientConfig: ClientConfigVersionTemplateType<'1.1'> + clientConfig: ClientConfigVersionTemplateType<'1.2'> ): Promise => { if (!clientConfig.auth) { throw new Error('Client config is missing auth section'); @@ -367,7 +367,7 @@ class AccessTestingProjectTestProject extends TestProjectBase { * unauthorized roles. I.e. it tests trust policy. */ private assertDifferentCognitoInstanceCannotAssumeAmplifyRoles = async ( - clientConfig: ClientConfigVersionTemplateType<'1.1'> + clientConfig: ClientConfigVersionTemplateType<'1.2'> ): Promise => { const simpleAuthUser = await this.createAuthenticatedSimpleAuthCognitoUser( clientConfig @@ -416,7 +416,7 @@ class AccessTestingProjectTestProject extends TestProjectBase { }; private createAuthenticatedSimpleAuthCognitoUser = async ( - clientConfig: ClientConfigVersionTemplateType<'1.1'> + clientConfig: ClientConfigVersionTemplateType<'1.2'> ): Promise => { if (!clientConfig.custom) { throw new Error('Client config is missing custom section'); @@ -496,7 +496,7 @@ class AccessTestingProjectTestProject extends TestProjectBase { }; private createAppSyncClient = ( - clientConfig: ClientConfigVersionTemplateType<'1.1'>, + clientConfig: ClientConfigVersionTemplateType<'1.2'>, credentials: IamCredentials ): ApolloClient => { if (!clientConfig.data?.url) { diff --git a/packages/integration-tests/src/test-project-setup/cdk/auth_cdk_project.ts b/packages/integration-tests/src/test-project-setup/cdk/auth_cdk_project.ts index 2beb33dff06..7010f5d2957 100644 --- a/packages/integration-tests/src/test-project-setup/cdk/auth_cdk_project.ts +++ b/packages/integration-tests/src/test-project-setup/cdk/auth_cdk_project.ts @@ -78,7 +78,7 @@ class AuthTestCdkProject extends TestCdkProjectBase { { stackName: this.stackName, }, - '1.1', //version of the config + '1.2', //version of the config awsClientProvider ); diff --git a/packages/integration-tests/src/test-projects/custom-outputs/amplify/backend.ts b/packages/integration-tests/src/test-projects/custom-outputs/amplify/backend.ts index 2ae7efb16f7..82b87ac6555 100644 --- a/packages/integration-tests/src/test-projects/custom-outputs/amplify/backend.ts +++ b/packages/integration-tests/src/test-projects/custom-outputs/amplify/backend.ts @@ -16,7 +16,7 @@ const sampleIdentityPoolId = 'test_identity_pool_id'; const sampleUserPoolClientId = 'test_user_pool_client_id'; backend.addOutput({ - version: '1.1', + version: '1.2', custom: { // test deploy time values restApiUrl: restApi.url, @@ -26,7 +26,7 @@ backend.addOutput({ }); backend.addOutput({ - version: '1.1', + version: '1.2', custom: { // test synth time values // and composition of config @@ -36,7 +36,7 @@ backend.addOutput({ const fakeCognitoUserPoolId = 'fakeCognitoUserPoolId'; backend.addOutput({ - version: '1.1', + version: '1.2', // test reserved key auth: { aws_region: sampleRegion, From d0a90b1ce792a7b731e167de3a32a313af72ff07 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 26 Sep 2024 13:11:24 -0700 Subject: [PATCH 021/199] Use message history instead of event payload for conversation handler (#2047) * Use message history instead of event payload for conversational route * refactor e2e * refactor gql requests * fallback * lint * add test for retriever * refactor that * todo comments * lint * refactor that * rename * process history * process history test * more tests * more tests --- .changeset/plenty-wombats-fry.md | 5 + packages/ai-constructs/API.md | 9 +- .../runtime/bedrock_converse_adapter.test.ts | 110 +++-- .../runtime/bedrock_converse_adapter.ts | 17 +- ...ersation_message_history_retriever.test.ts | 413 ++++++++++++++++++ .../conversation_message_history_retriever.ts | 239 ++++++++++ .../conversation_turn_executor.test.ts | 6 + .../conversation_turn_response_sender.test.ts | 131 ++---- .../conversation_turn_response_sender.ts | 45 +- .../event_tools_provider.test.ts | 12 + .../event-tools-provider/graphql_tool.test.ts | 98 +---- .../event-tools-provider/graphql_tool.ts | 50 +-- .../runtime/graphql_request_executor.test.ts | 119 +++++ .../runtime/graphql_request_executor.ts | 57 +++ .../src/conversation/runtime/types.ts | 12 +- .../conversation_handler_project.ts | 250 ++++++++--- .../amplify/data/resource.ts | 80 +++- 17 files changed, 1311 insertions(+), 342 deletions(-) create mode 100644 .changeset/plenty-wombats-fry.md create mode 100644 packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts create mode 100644 packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts create mode 100644 packages/ai-constructs/src/conversation/runtime/graphql_request_executor.test.ts create mode 100644 packages/ai-constructs/src/conversation/runtime/graphql_request_executor.ts diff --git a/.changeset/plenty-wombats-fry.md b/.changeset/plenty-wombats-fry.md new file mode 100644 index 00000000000..9908e3e6713 --- /dev/null +++ b/.changeset/plenty-wombats-fry.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': minor +--- + +Use message history instead of event payload for conversational route diff --git a/packages/ai-constructs/API.md b/packages/ai-constructs/API.md index 95ab27f8e12..8141429128e 100644 --- a/packages/ai-constructs/API.md +++ b/packages/ai-constructs/API.md @@ -91,7 +91,14 @@ type ConversationTurnEvent = { authorization: string; }; }; - messages: Array; + messages?: Array; + messageHistoryQuery: { + getQueryName: string; + getQueryInputTypeName: string; + listQueryName: string; + listQueryInputTypeName: string; + listQueryLimit?: number; + }; toolsConfiguration?: { dataTools?: Array { const commonEvent: Readonly = { conversationId: '', currentMessageId: '', graphqlApiEndpoint: '', - messages: [ - { - role: 'user', - content: [ - { - text: 'event message', - }, - ], - }, - ], + messageHistoryQuery: { + getQueryName: '', + getQueryInputTypeName: '', + listQueryName: '', + listQueryInputTypeName: '', + }, modelConfiguration: { modelId: 'testModelId', systemPrompt: 'testSystemPrompt', @@ -46,6 +48,27 @@ void describe('Bedrock converse adapter', () => { }, }; + const messages: Array = [ + { + role: 'user', + content: [ + { + text: 'event message', + }, + ], + }, + ]; + const messageHistoryRetriever = new ConversationMessageHistoryRetriever( + commonEvent + ); + const messageHistoryRetrieverMockGetEventMessages = mock.method( + messageHistoryRetriever, + 'getMessageHistory', + () => { + return Promise.resolve(messages); + } + ); + void it('calls bedrock to get conversation response', async () => { const event: ConversationTurnEvent = { ...commonEvent, @@ -78,7 +101,9 @@ void describe('Bedrock converse adapter', () => { const responseContent = await new BedrockConverseAdapter( event, [], - bedrockClient + bedrockClient, + undefined, + messageHistoryRetriever ).askBedrock(); assert.deepStrictEqual( @@ -90,7 +115,7 @@ void describe('Bedrock converse adapter', () => { const bedrockRequest = bedrockClientSendMock.mock.calls[0] .arguments[0] as unknown as ConverseCommand; const expectedBedrockInput: ConverseCommandInput = { - messages: event.messages as Array, + messages: messages as Array, modelId: event.modelConfiguration.modelId, inferenceConfig: event.modelConfiguration.inferenceConfiguration, system: [ @@ -211,7 +236,8 @@ void describe('Bedrock converse adapter', () => { event, [additionalTool], bedrockClient, - eventToolsProvider + eventToolsProvider, + messageHistoryRetriever ).askBedrock(); assert.deepStrictEqual( @@ -251,7 +277,7 @@ void describe('Bedrock converse adapter', () => { const bedrockRequest1 = bedrockClientSendMock.mock.calls[0] .arguments[0] as unknown as ConverseCommand; const expectedBedrockInput1: ConverseCommandInput = { - messages: event.messages as Array, + messages: messages as Array, ...expectedBedrockInputCommonProperties, }; assert.deepStrictEqual(bedrockRequest1.input, expectedBedrockInput1); @@ -264,7 +290,7 @@ void describe('Bedrock converse adapter', () => { ); const expectedBedrockInput2: ConverseCommandInput = { messages: [ - ...(event.messages as Array), + ...(messages as Array), additionalToolUseBedrockResponse.output?.message, { role: 'user', @@ -447,7 +473,9 @@ void describe('Bedrock converse adapter', () => { const responseContent = await new BedrockConverseAdapter( event, [tool], - bedrockClient + bedrockClient, + undefined, + messageHistoryRetriever ).askBedrock(); assert.deepStrictEqual( @@ -543,7 +571,9 @@ void describe('Bedrock converse adapter', () => { const responseContent = await new BedrockConverseAdapter( event, [tool], - bedrockClient + bedrockClient, + undefined, + messageHistoryRetriever ).askBedrock(); assert.deepStrictEqual( @@ -645,7 +675,9 @@ void describe('Bedrock converse adapter', () => { const responseContent = await new BedrockConverseAdapter( event, [additionalTool], - bedrockClient + bedrockClient, + undefined, + messageHistoryRetriever ).askBedrock(); assert.deepStrictEqual(responseContent, [clientToolUseBlock]); @@ -682,7 +714,7 @@ void describe('Bedrock converse adapter', () => { const bedrockRequest = bedrockClientSendMock.mock.calls[0] .arguments[0] as unknown as ConverseCommand; const expectedBedrockInput: ConverseCommandInput = { - messages: event.messages as Array, + messages: messages as Array, ...expectedBedrockInputCommonProperties, }; assert.deepStrictEqual(bedrockRequest.input, expectedBedrockInput); @@ -695,21 +727,27 @@ void describe('Bedrock converse adapter', () => { const fakeImagePayload = randomBytes(32); - event.messages = [ - { - role: 'user', - content: [ + messageHistoryRetrieverMockGetEventMessages.mock.mockImplementationOnce( + () => { + return Promise.resolve([ { - image: { - format: 'png', - source: { - bytes: fakeImagePayload.toString('base64'), + id: '', + conversationId: '', + role: 'user', + content: [ + { + image: { + format: 'png', + source: { + bytes: fakeImagePayload.toString('base64'), + }, + }, }, - }, + ], }, - ], - }, - ]; + ]); + } + ); const bedrockClient = new BedrockRuntimeClient(); const bedrockResponse: ConverseCommandOutput = { @@ -735,7 +773,13 @@ void describe('Bedrock converse adapter', () => { Promise.resolve(bedrockResponse) ); - await new BedrockConverseAdapter(event, [], bedrockClient).askBedrock(); + await new BedrockConverseAdapter( + event, + [], + bedrockClient, + undefined, + messageHistoryRetriever + ).askBedrock(); assert.strictEqual(bedrockClientSendMock.mock.calls.length, 1); const bedrockRequest = bedrockClientSendMock.mock.calls[0] diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts index 7c7a572387b..5ab89d09f92 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts @@ -14,6 +14,7 @@ import { ToolDefinition, } from './types.js'; import { ConversationTurnEventToolsProvider } from './event-tools-provider'; +import { ConversationMessageHistoryRetriever } from './conversation_message_history_retriever'; /** * This class is responsible for interacting with Bedrock Converse API @@ -36,7 +37,10 @@ export class BedrockConverseAdapter { private readonly bedrockClient: BedrockRuntimeClient = new BedrockRuntimeClient( { region: event.modelConfiguration.region } ), - eventToolsProvider = new ConversationTurnEventToolsProvider(event) + eventToolsProvider = new ConversationTurnEventToolsProvider(event), + private readonly messageHistoryRetriever = new ConversationMessageHistoryRetriever( + event + ) ) { this.executableTools = [ ...eventToolsProvider.getEventTools(), @@ -73,7 +77,8 @@ export class BedrockConverseAdapter { const { modelId, systemPrompt, inferenceConfiguration } = this.event.modelConfiguration; - const messages: Array = this.getEventMessagesAsBedrockMessages(); + const messages: Array = + await this.getEventMessagesAsBedrockMessages(); let bedrockResponse: ConverseCommandOutput; do { @@ -124,9 +129,13 @@ export class BedrockConverseAdapter { * 1. Makes a copy so that we don't mutate event. * 2. Decodes Base64 encoded images. */ - private getEventMessagesAsBedrockMessages = (): Array => { + private getEventMessagesAsBedrockMessages = async (): Promise< + Array + > => { const messages: Array = []; - for (const message of this.event.messages) { + const eventMessages = + await this.messageHistoryRetriever.getMessageHistory(); + for (const message of eventMessages) { const messageContent: Array = []; for (const contentElement of message.content) { if (typeof contentElement.image?.source?.bytes === 'string') { diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts new file mode 100644 index 00000000000..c53963f3f96 --- /dev/null +++ b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts @@ -0,0 +1,413 @@ +import { describe, it, mock } from 'node:test'; +import assert from 'node:assert'; +import { MutationResponseInput } from './conversation_turn_response_sender'; +import { ConversationMessage, ConversationTurnEvent } from './types'; +import { + GraphqlRequest, + GraphqlRequestExecutor, +} from './graphql_request_executor'; +import { + ConversationHistoryMessageItem, + ConversationMessageHistoryRetriever, + GetQueryOutput, + ListQueryOutput, +} from './conversation_message_history_retriever'; + +type TestCase = { + name: string; + mockListResponseMessages: Array; + mockGetCurrentMessage?: ConversationHistoryMessageItem; + expectedMessages: Array; +}; + +void describe('Conversation message history retriever', () => { + const event: ConversationTurnEvent = { + conversationId: 'testConversationId', + currentMessageId: 'testCurrentMessageId', + graphqlApiEndpoint: '', + messageHistoryQuery: { + getQueryName: 'testGetQueryName', + getQueryInputTypeName: 'testGetQueryInputTypeName', + listQueryName: 'testListQueryName', + listQueryInputTypeName: 'testListQueryInputTypeName', + }, + modelConfiguration: { modelId: '', systemPrompt: '' }, + request: { headers: { authorization: '' } }, + responseMutation: { + name: '', + inputTypeName: '', + selectionSet: '', + }, + }; + + const testCases: Array = [ + { + name: 'Retrieves message history that includes current message', + mockListResponseMessages: [ + { + id: 'someNonCurrentMessageId1', + conversationId: event.conversationId, + role: 'user', + content: [ + { + text: 'message1', + }, + ], + }, + { + id: 'someNonCurrentMessageId2', + associatedUserMessageId: 'someNonCurrentMessageId1', + conversationId: event.conversationId, + role: 'assistant', + content: [ + { + text: 'message2', + }, + ], + }, + { + id: event.currentMessageId, + conversationId: event.conversationId, + role: 'user', + content: [ + { + text: 'message3', + }, + ], + }, + ], + expectedMessages: [ + { + role: 'user', + content: [ + { + text: 'message1', + }, + ], + }, + { + role: 'assistant', + content: [ + { + text: 'message2', + }, + ], + }, + { + role: 'user', + content: [ + { + text: 'message3', + }, + ], + }, + ], + }, + { + name: 'Retrieves message history that does not include current message with fallback to get it directly', + mockListResponseMessages: [ + { + id: 'someNonCurrentMessageId1', + conversationId: event.conversationId, + role: 'user', + content: [ + { + text: 'message1', + }, + ], + }, + { + id: 'someNonCurrentMessageId2', + associatedUserMessageId: 'someNonCurrentMessageId1', + conversationId: event.conversationId, + role: 'assistant', + content: [ + { + text: 'message2', + }, + ], + }, + ], + mockGetCurrentMessage: { + id: event.currentMessageId, + conversationId: event.conversationId, + role: 'user', + content: [ + { + text: 'message3', + }, + ], + }, + expectedMessages: [ + { + role: 'user', + content: [ + { + text: 'message1', + }, + ], + }, + { + role: 'assistant', + content: [ + { + text: 'message2', + }, + ], + }, + { + role: 'user', + content: [ + { + text: 'message3', + }, + ], + }, + ], + }, + { + name: 'Re-orders delayed assistant responses', + mockListResponseMessages: [ + // Simulate that two first messages were sent without waiting for assistant response + { + id: 'userMessage1', + conversationId: event.conversationId, + role: 'user', + content: [ + { + text: 'userMessage1', + }, + ], + }, + { + id: 'userMessage2', + conversationId: event.conversationId, + role: 'user', + content: [ + { + text: 'userMessage2', + }, + ], + }, + // also simulate that responses came back out of order + { + id: 'assistantResponse2', + associatedUserMessageId: 'userMessage2', + conversationId: event.conversationId, + role: 'assistant', + content: [ + { + text: 'assistantResponse2', + }, + ], + }, + { + id: 'assistantResponse1', + associatedUserMessageId: 'userMessage1', + conversationId: event.conversationId, + role: 'assistant', + content: [ + { + text: 'assistantResponse1', + }, + ], + }, + { + id: event.currentMessageId, + conversationId: event.conversationId, + role: 'user', + content: [ + { + text: 'currentUserMessage', + }, + ], + }, + ], + expectedMessages: [ + { + role: 'user', + content: [ + { + text: 'userMessage1', + }, + ], + }, + { + role: 'assistant', + content: [ + { + text: 'assistantResponse1', + }, + ], + }, + { + role: 'user', + content: [ + { + text: 'userMessage2', + }, + ], + }, + { + role: 'assistant', + content: [ + { + text: 'assistantResponse2', + }, + ], + }, + { + role: 'user', + content: [ + { + text: 'currentUserMessage', + }, + ], + }, + ], + }, + { + name: 'Skips user message that does not have response yet', + mockListResponseMessages: [ + // Simulate that two first messages were sent without waiting for assistant response + // and none was responded to yet. + { + id: 'userMessage1', + conversationId: event.conversationId, + role: 'user', + content: [ + { + text: 'userMessage1', + }, + ], + }, + { + id: 'userMessage2', + conversationId: event.conversationId, + role: 'user', + content: [ + { + text: 'userMessage2', + }, + ], + }, + { + id: event.currentMessageId, + conversationId: event.conversationId, + role: 'user', + content: [ + { + text: 'currentUserMessage', + }, + ], + }, + ], + expectedMessages: [ + { + role: 'user', + content: [ + { + text: 'currentUserMessage', + }, + ], + }, + ], + }, + { + name: 'Injects aiContext', + mockListResponseMessages: [ + { + id: event.currentMessageId, + conversationId: event.conversationId, + role: 'user', + aiContext: { some: { ai: 'context' } }, + content: [ + { + text: 'currentUserMessage', + }, + ], + }, + ], + expectedMessages: [ + { + role: 'user', + content: [ + { + text: 'currentUserMessage', + }, + { + text: '{"some":{"ai":"context"}}', + }, + ], + }, + ], + }, + ]; + + for (const testCase of testCases) { + void it(testCase.name, async () => { + const graphqlRequestExecutor = new GraphqlRequestExecutor('', ''); + const executeGraphqlMock = mock.method( + graphqlRequestExecutor, + 'executeGraphql', + (request: GraphqlRequest) => { + if (request.query.match(/ListMessages/)) { + const mockListResponse: ListQueryOutput = { + data: { + [event.messageHistoryQuery.listQueryName]: { + // clone array + items: [...testCase.mockListResponseMessages], + }, + }, + }; + return Promise.resolve(mockListResponse); + } + if ( + request.query.match(/GetMessage/) && + testCase.mockGetCurrentMessage + ) { + const mockGetResponse: GetQueryOutput = { + data: { + [event.messageHistoryQuery.getQueryName]: + testCase.mockGetCurrentMessage, + }, + }; + return Promise.resolve(mockGetResponse); + } + throw new Error('The query is not mocked'); + } + ); + + const retriever = new ConversationMessageHistoryRetriever( + event, + graphqlRequestExecutor + ); + const messages = await retriever.getMessageHistory(); + + assert.strictEqual( + executeGraphqlMock.mock.calls.length, + testCase.mockGetCurrentMessage ? 2 : 1 + ); + const listRequest = executeGraphqlMock.mock.calls[0] + .arguments[0] as GraphqlRequest; + assert.match(listRequest.query, /ListMessages/); + assert.deepStrictEqual(listRequest.variables, { + filter: { + conversationId: { + eq: 'testConversationId', + }, + }, + limit: 1000, + }); + if (testCase.mockGetCurrentMessage) { + const getRequest = executeGraphqlMock.mock.calls[1] + .arguments[0] as GraphqlRequest; + assert.match(getRequest.query, /GetMessage/); + assert.deepStrictEqual(getRequest.variables, { + id: event.currentMessageId, + }); + } + assert.deepStrictEqual(messages, testCase.expectedMessages); + }); + } +}); diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts new file mode 100644 index 00000000000..223f372cf02 --- /dev/null +++ b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts @@ -0,0 +1,239 @@ +import { ConversationMessage, ConversationTurnEvent } from './types'; +import { GraphqlRequestExecutor } from './graphql_request_executor'; + +export type ConversationHistoryMessageItem = ConversationMessage & { + id: string; + conversationId: string; + associatedUserMessageId?: string; + aiContext?: unknown; +}; + +export type GetQueryInput = { + id: string; +}; + +export type GetQueryOutput = { + data: Record; +}; + +export type ListQueryInput = { + filter: { + conversationId: { + eq: string; + }; + }; + limit: number; +}; + +export type ListQueryOutput = { + data: Record< + string, + { + items: Array; + } + >; +}; + +/** + * These are all properties we have to pull. + * Unfortunately, GQL doesn't support wildcards. + * https://github.com/graphql/graphql-spec/issues/127 + */ +const messageItemSelectionSet = ` + id + conversationId + associatedUserMessageId + aiContext + role + content { + text + document { + source { + bytes + } + format + name + } + image { + format + source { + bytes + } + } + toolResult { + content { + document { + format + name + source { + bytes + } + } + image { + format + source { + bytes + } + } + json + text + } + status + toolUseId + } + toolUse { + input + name + toolUseId + } + } +`; + +/** + * This class is responsible for retrieving message history that belongs to conversation turn event. + * It queries AppSync to list messages that belong to conversation. + * Additionally, it looks up a current message in case it's missing in the list due to eventual consistency. + */ +export class ConversationMessageHistoryRetriever { + /** + * Creates conversation message history retriever. + */ + constructor( + private readonly event: ConversationTurnEvent, + private readonly graphqlRequestExecutor = new GraphqlRequestExecutor( + event.graphqlApiEndpoint, + event.request.headers.authorization + ) + ) {} + + getMessageHistory = async (): Promise> => { + if (this.event.messages?.length) { + // This is for backwards compatibility and should be removed with messages property. + return this.event.messages; + } + const messages = await this.listMessages(); + + let currentMessage = messages.find( + (m) => m.id === this.event.currentMessageId + ); + + // This is a fallback in case current message is not available in the message list. + // I.e. in a situation when freshly written message is not yet visible in + // eventually consistent reads. + if (!currentMessage) { + currentMessage = await this.getCurrentMessage(); + messages.push(currentMessage); + } + + // Index assistant messages by corresponding user message. + const assistantMessageByUserMessageId: Map< + string, + ConversationHistoryMessageItem + > = new Map(); + messages.forEach((message) => { + if (message.role === 'assistant' && message.associatedUserMessageId) { + assistantMessageByUserMessageId.set( + message.associatedUserMessageId, + message + ); + } + }); + + // Reconcile history and inject aiContext + return messages.reduce((acc, current) => { + // Bedrock expects that message history is user->assistant->user->assistant->... and so on. + // The chronological order doesn't assure this ordering if there were any concurrent messages sent. + // Therefore, conversation is ordered by user's messages only and corresponding assistant messages are inserted + // into right place regardless of their createdAt value. + // This algorithm assumes that GQL query returns messages sorted by createdAt. + if (current.role === 'assistant') { + // Initially, skip assistant messages, these might be out of chronological order. + return acc; + } + if ( + current.role === 'user' && + !assistantMessageByUserMessageId.has(current.id) && + current.id !== this.event.currentMessageId + ) { + // Skip user messages that didn't get answer from assistant yet. + // These might be still "in-flight", i.e. assistant is still working on them in separate invocation. + // Except current message, we want to process that one. + return acc; + } + const aiContext = current.aiContext; + const content = aiContext + ? [...current.content, { text: JSON.stringify(aiContext) }] + : current.content; + + acc.push({ role: current.role, content }); + + // Find and insert corresponding assistant message. + const correspondingAssistantMessage = assistantMessageByUserMessageId.get( + current.id + ); + if (correspondingAssistantMessage) { + acc.push({ + role: correspondingAssistantMessage.role, + content: correspondingAssistantMessage.content, + }); + } + return acc; + }, [] as Array); + }; + + private getCurrentMessage = + async (): Promise => { + const query = ` + query GetMessage($id: ${this.event.messageHistoryQuery.getQueryInputTypeName}!) { + ${this.event.messageHistoryQuery.getQueryName}(id: $id) { + ${messageItemSelectionSet} + } + } + `; + const variables: GetQueryInput = { + id: this.event.currentMessageId, + }; + + const response = await this.graphqlRequestExecutor.executeGraphql< + GetQueryInput, + GetQueryOutput + >({ + query, + variables, + }); + + return response.data[this.event.messageHistoryQuery.getQueryName]; + }; + + private listMessages = async (): Promise< + Array + > => { + const query = ` + query ListMessages($filter: ${this.event.messageHistoryQuery.listQueryInputTypeName}!, $limit: Int) { + ${this.event.messageHistoryQuery.listQueryName}(filter: $filter, limit: $limit) { + items { + ${messageItemSelectionSet} + } + } + } + `; + const variables: ListQueryInput = { + filter: { + conversationId: { + eq: this.event.conversationId, + }, + }, + limit: this.event.messageHistoryQuery.listQueryLimit ?? 1000, + }; + + const response = await this.graphqlRequestExecutor.executeGraphql< + ListQueryInput, + ListQueryOutput + >({ + query, + variables, + }); + + return response.data[this.event.messageHistoryQuery.listQueryName].items; + }; +} diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts index e9a06647500..8231b8daef9 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts @@ -12,6 +12,12 @@ void describe('Conversation turn executor', () => { currentMessageId: 'testCurrentMessageId', graphqlApiEndpoint: '', messages: [], + messageHistoryQuery: { + getQueryName: '', + getQueryInputTypeName: '', + listQueryName: '', + listQueryInputTypeName: '', + }, modelConfiguration: { modelId: '', systemPrompt: '' }, request: { headers: { authorization: '' } }, responseMutation: { diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts index c7201ca1b50..86805927607 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts @@ -1,9 +1,15 @@ import { describe, it, mock } from 'node:test'; import assert from 'node:assert'; -import { text } from 'node:stream/consumers'; -import { ConversationTurnResponseSender } from './conversation_turn_response_sender'; +import { + ConversationTurnResponseSender, + MutationResponseInput, +} from './conversation_turn_response_sender'; import { ConversationTurnEvent } from './types'; import { ContentBlock } from '@aws-sdk/client-bedrock-runtime'; +import { + GraphqlRequest, + GraphqlRequestExecutor, +} from './graphql_request_executor'; void describe('Conversation turn response sender', () => { const event: ConversationTurnEvent = { @@ -11,6 +17,12 @@ void describe('Conversation turn response sender', () => { currentMessageId: 'testCurrentMessageId', graphqlApiEndpoint: 'http://fake.endpoint/', messages: [], + messageHistoryQuery: { + getQueryName: '', + getQueryInputTypeName: '', + listQueryName: '', + listQueryInputTypeName: '', + }, modelConfiguration: { modelId: '', systemPrompt: '' }, request: { headers: { authorization: 'testToken' } }, responseMutation: { @@ -21,13 +33,18 @@ void describe('Conversation turn response sender', () => { }; void it('sends response back to appsync', async () => { - const fetchMock = mock.fn( - fetch, - (): Promise => + const graphqlRequestExecutor = new GraphqlRequestExecutor('', ''); + const executeGraphqlMock = mock.method( + graphqlRequestExecutor, + 'executeGraphql', + () => // Mock successful Appsync response - Promise.resolve(new Response('{}', { status: 200 })) + Promise.resolve() + ); + const sender = new ConversationTurnResponseSender( + event, + graphqlRequestExecutor ); - const sender = new ConversationTurnResponseSender(event, fetchMock); const response: Array = [ { text: 'block1', @@ -36,20 +53,10 @@ void describe('Conversation turn response sender', () => { ]; await sender.sendResponse(response); - assert.strictEqual(fetchMock.mock.calls.length, 1); - const request: Request = fetchMock.mock.calls[0].arguments[0] as Request; - assert.strictEqual(request.url, event.graphqlApiEndpoint); - assert.strictEqual(request.method, 'POST'); - assert.strictEqual( - request.headers.get('Content-Type'), - 'application/graphql' - ); - assert.strictEqual( - request.headers.get('Authorization'), - event.request.headers.authorization - ); - assert.ok(request.body); - assert.deepStrictEqual(JSON.parse(await text(request.body)), { + assert.strictEqual(executeGraphqlMock.mock.calls.length, 1); + const request = executeGraphqlMock.mock.calls[0] + .arguments[0] as GraphqlRequest; + assert.deepStrictEqual(request, { query: '\n' + ' mutation PublishModelResponse($input: testResponseMutationInputTypeName!) {\n' + @@ -73,73 +80,19 @@ void describe('Conversation turn response sender', () => { }); }); - void it('throws if response is not 2xx', async () => { - const fetchMock = mock.fn( - fetch, - (): Promise => - // Mock successful Appsync response - Promise.resolve( - new Response('Body with error', { - status: 400, - headers: { testHeaderKey: 'testHeaderValue' }, - }) - ) - ); - const sender = new ConversationTurnResponseSender(event, fetchMock); - const response: Array = []; - await assert.rejects( - () => sender.sendResponse(response), - (error: Error) => { - assert.strictEqual( - error.message, - // eslint-disable-next-line spellcheck/spell-checker - 'Assistant response mutation request was not successful, response headers={"content-type":"text/plain;charset=UTF-8","testheaderkey":"testHeaderValue"}, body=Body with error' - ); - return true; - } - ); - }); - - void it('throws if graphql returns errors', async () => { - const fetchMock = mock.fn( - fetch, - (): Promise => - // Mock successful Appsync response - Promise.resolve( - new Response( - JSON.stringify({ - errors: ['Some GQL error'], - }), - { - status: 200, - headers: { testHeaderKey: 'testHeaderValue' }, - } - ) - ) - ); - const sender = new ConversationTurnResponseSender(event, fetchMock); - const response: Array = []; - await assert.rejects( - () => sender.sendResponse(response), - (error: Error) => { - assert.strictEqual( - error.message, - // eslint-disable-next-line spellcheck/spell-checker - 'Assistant response mutation request was not successful, response headers={"content-type":"text/plain;charset=UTF-8","testheaderkey":"testHeaderValue"}, body={"errors":["Some GQL error"]}' - ); - return true; - } - ); - }); - void it('serializes tool use input to JSON', async () => { - const fetchMock = mock.fn( - fetch, - (): Promise => + const graphqlRequestExecutor = new GraphqlRequestExecutor('', ''); + const executeGraphqlMock = mock.method( + graphqlRequestExecutor, + 'executeGraphql', + () => // Mock successful Appsync response - Promise.resolve(new Response('{}', { status: 200 })) + Promise.resolve() + ); + const sender = new ConversationTurnResponseSender( + event, + graphqlRequestExecutor ); - const sender = new ConversationTurnResponseSender(event, fetchMock); const toolUseBlock: ContentBlock.ToolUseMember = { toolUse: { name: 'testTool', @@ -152,10 +105,10 @@ void describe('Conversation turn response sender', () => { const response: Array = [toolUseBlock]; await sender.sendResponse(response); - assert.strictEqual(fetchMock.mock.calls.length, 1); - const request: Request = fetchMock.mock.calls[0].arguments[0] as Request; - assert.ok(request.body); - assert.deepStrictEqual(JSON.parse(await text(request.body)), { + assert.strictEqual(executeGraphqlMock.mock.calls.length, 1); + const request = executeGraphqlMock.mock.calls[0] + .arguments[0] as GraphqlRequest; + assert.deepStrictEqual(request, { query: '\n' + ' mutation PublishModelResponse($input: testResponseMutationInputTypeName!) {\n' + diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts index 9fe979aac87..d3bb9accdce 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts @@ -1,7 +1,8 @@ import { ConversationTurnEvent } from './types.js'; import type { ContentBlock } from '@aws-sdk/client-bedrock-runtime'; +import { GraphqlRequestExecutor } from './graphql_request_executor'; -type MutationResponseInput = { +export type MutationResponseInput = { input: { conversationId: string; content: ContentBlock[]; @@ -19,30 +20,21 @@ export class ConversationTurnResponseSender { */ constructor( private readonly event: ConversationTurnEvent, - private readonly _fetch = fetch + private readonly graphqlRequestExecutor = new GraphqlRequestExecutor( + event.graphqlApiEndpoint, + event.request.headers.authorization + ) ) {} sendResponse = async (message: ContentBlock[]) => { - const request = this.createMutationRequest(message); - const res = await this._fetch(request); - const responseHeaders: Record = {}; - res.headers.forEach((value, key) => (responseHeaders[key] = value)); - if (!res.ok) { - const body = await res.text(); - throw new Error( - `Assistant response mutation request was not successful, response headers=${JSON.stringify( - responseHeaders - )}, body=${body}` - ); - } - const body = await res.json(); - if (body && typeof body === 'object' && 'errors' in body) { - throw new Error( - `Assistant response mutation request was not successful, response headers=${JSON.stringify( - responseHeaders - )}, body=${JSON.stringify(body)}` - ); - } + const { query, variables } = this.createMutationRequest(message); + await this.graphqlRequestExecutor.executeGraphql< + MutationResponseInput, + void + >({ + query, + variables, + }); }; private createMutationRequest = (content: ContentBlock[]) => { @@ -70,13 +62,6 @@ export class ConversationTurnResponseSender { associatedUserMessageId: this.event.currentMessageId, }, }; - return new Request(this.event.graphqlApiEndpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/graphql', - Authorization: this.event.request.headers.authorization, - }, - body: JSON.stringify({ query, variables }), - }); + return { query, variables }; }; } diff --git a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.test.ts b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.test.ts index 12bea0403e3..8b3db55ea73 100644 --- a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.test.ts @@ -12,6 +12,12 @@ void describe('events tool provider', () => { currentMessageId: '', graphqlApiEndpoint: '', messages: [], + messageHistoryQuery: { + getQueryName: '', + getQueryInputTypeName: '', + listQueryName: '', + listQueryInputTypeName: '', + }, modelConfiguration: { modelId: '', systemPrompt: '' }, request: { headers: { authorization: '' } }, responseMutation: { @@ -62,6 +68,12 @@ void describe('events tool provider', () => { currentMessageId: '', graphqlApiEndpoint: '', messages: [], + messageHistoryQuery: { + getQueryName: '', + getQueryInputTypeName: '', + listQueryName: '', + listQueryInputTypeName: '', + }, modelConfiguration: { modelId: '', systemPrompt: '' }, request: { headers: { authorization: '' } }, responseMutation: { diff --git a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.test.ts b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.test.ts index 9989429ef5d..6d556e4a2e7 100644 --- a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.test.ts @@ -1,14 +1,20 @@ import { describe, it, mock } from 'node:test'; import assert from 'node:assert'; -import { text } from 'node:stream/consumers'; import { GraphQlTool } from './graphql_tool'; +import { + GraphqlRequest, + GraphqlRequestExecutor, +} from '../graphql_request_executor'; +import { DocumentType } from '@smithy/types'; void describe('GraphQl tool', () => { const graphQlEndpoint = 'http://test.endpoint/'; const query = 'testQuery'; const accessToken = 'testAccessToken'; - const createGraphQlTool = (fetchMock: typeof fetch): GraphQlTool => { + const createGraphQlTool = ( + graphqlRequestExecutor: GraphqlRequestExecutor + ): GraphQlTool => { return new GraphQlTool( 'testName', 'testDescription', @@ -16,7 +22,7 @@ void describe('GraphQl tool', () => { graphQlEndpoint, query, accessToken, - fetchMock + graphqlRequestExecutor ); }; @@ -24,28 +30,21 @@ void describe('GraphQl tool', () => { const testResponse = { test: 'response', }; - const fetchMock = mock.fn( - fetch, - (): Promise => + const graphqlRequestExecutor = new GraphqlRequestExecutor('', ''); + const executeGraphqlMock = mock.method( + graphqlRequestExecutor, + 'executeGraphql', + () => // Mock successful Appsync response - Promise.resolve( - new Response(JSON.stringify(testResponse), { status: 200 }) - ) + Promise.resolve(testResponse) ); - const tool = createGraphQlTool(fetchMock); + const tool = createGraphQlTool(graphqlRequestExecutor); const toolResult = await tool.execute({ test: 'input' }); - assert.strictEqual(fetchMock.mock.calls.length, 1); - const request: Request = fetchMock.mock.calls[0].arguments[0] as Request; - assert.strictEqual(request.url, graphQlEndpoint); - assert.strictEqual(request.method, 'POST'); - assert.strictEqual( - request.headers.get('Content-Type'), - 'application/graphql' - ); - assert.strictEqual(request.headers.get('Authorization'), accessToken); - assert.ok(request.body); - assert.deepStrictEqual(JSON.parse(await text(request.body)), { + assert.strictEqual(executeGraphqlMock.mock.calls.length, 1); + const request = executeGraphqlMock.mock.calls[0] + .arguments[0] as GraphqlRequest; + assert.deepStrictEqual(request, { query: 'testQuery', variables: { test: 'input', @@ -57,61 +56,4 @@ void describe('GraphQl tool', () => { }, }); }); - - void it('throws if response is not 2xx', async () => { - const fetchMock = mock.fn( - fetch, - (): Promise => - // Mock successful Appsync response - Promise.resolve( - new Response('Body with error', { - status: 400, - headers: { testHeaderKey: 'testHeaderValue' }, - }) - ) - ); - const tool = createGraphQlTool(fetchMock); - await assert.rejects( - () => tool.execute({ test: 'input' }), - (error: Error) => { - assert.strictEqual( - error.message, - // eslint-disable-next-line spellcheck/spell-checker - 'GraphQl tool \'testName\' failed, response headers={"content-type":"text/plain;charset=UTF-8","testheaderkey":"testHeaderValue"}, body=Body with error' - ); - return true; - } - ); - }); - - void it('throws if graphql returns errors', async () => { - const fetchMock = mock.fn( - fetch, - (): Promise => - // Mock successful Appsync response - Promise.resolve( - new Response( - JSON.stringify({ - errors: ['Some GQL error'], - }), - { - status: 200, - headers: { testHeaderKey: 'testHeaderValue' }, - } - ) - ) - ); - const tool = createGraphQlTool(fetchMock); - await assert.rejects( - () => tool.execute({ test: 'input' }), - (error: Error) => { - assert.strictEqual( - error.message, - // eslint-disable-next-line spellcheck/spell-checker - 'GraphQl tool \'testName\' failed, response headers={"content-type":"text/plain;charset=UTF-8","testheaderkey":"testHeaderValue"}, body={"errors":["Some GQL error"]}' - ); - return true; - } - ); - }); }); diff --git a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts index a6f9cce9492..6173e3ac63c 100644 --- a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts +++ b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts @@ -4,6 +4,7 @@ import type { ToolResultContentBlock, } from '@aws-sdk/client-bedrock-runtime'; import { DocumentType } from '@smithy/types'; +import { GraphqlRequestExecutor } from '../graphql_request_executor'; /** * A tool that use GraphQl queries. @@ -16,10 +17,13 @@ export class GraphQlTool implements ExecutableTool { public name: string, public description: string, public inputSchema: ToolInputSchema, - private readonly graphQlEndpoint: string, + readonly graphQlEndpoint: string, private readonly query: string, - private readonly accessToken: string, - private readonly _fetch = fetch + readonly accessToken: string, + private readonly graphqlRequestExecutor = new GraphqlRequestExecutor( + graphQlEndpoint, + accessToken + ) ) {} execute = async ( @@ -29,37 +33,13 @@ export class GraphQlTool implements ExecutableTool { throw Error(`GraphQl tool '${this.name}' requires input to execute.`); } - const options: RequestInit = { - method: 'POST', - headers: { - 'Content-Type': 'application/graphql', - Authorization: this.accessToken, - }, - body: JSON.stringify({ query: this.query, variables: input }), - }; - - const req = new Request(this.graphQlEndpoint, options); - const res = await this._fetch(req); - - const responseHeaders: Record = {}; - res.headers.forEach((value, key) => (responseHeaders[key] = value)); - if (!res.ok) { - const body = await res.text(); - throw new Error( - `GraphQl tool '${this.name}' failed, response headers=${JSON.stringify( - responseHeaders - )}, body=${body}` - ); - } - const body = await res.json(); - if (body && typeof body === 'object' && 'errors' in body) { - throw new Error( - `GraphQl tool '${this.name}' failed, response headers=${JSON.stringify( - responseHeaders - )}, body=${JSON.stringify(body)}` - ); - } - - return { json: body as DocumentType }; + const response = await this.graphqlRequestExecutor.executeGraphql< + DocumentType, + DocumentType + >({ + query: this.query, + variables: input, + }); + return { json: response as DocumentType }; }; } diff --git a/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.test.ts b/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.test.ts new file mode 100644 index 00000000000..17820e1afff --- /dev/null +++ b/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.test.ts @@ -0,0 +1,119 @@ +import { describe, it, mock } from 'node:test'; +import assert from 'node:assert'; +import { text } from 'node:stream/consumers'; +import { GraphqlRequestExecutor } from './graphql_request_executor'; + +void describe('Graphql executor test', () => { + const graphqlEndpoint = 'http://fake.endpoint/'; + const accessToken = 'testToken'; + + void it('sends request to appsync', async () => { + const fetchMock = mock.fn( + fetch, + (): Promise => + // Mock successful Appsync response + Promise.resolve(new Response('{}', { status: 200 })) + ); + const executor = new GraphqlRequestExecutor( + graphqlEndpoint, + accessToken, + fetchMock + ); + const query = 'testQuery'; + const variables = { + testVariableKey: 'testVariableValue', + }; + await executor.executeGraphql({ + query, + variables, + }); + + assert.strictEqual(fetchMock.mock.calls.length, 1); + const request: Request = fetchMock.mock.calls[0].arguments[0] as Request; + assert.strictEqual(request.url, graphqlEndpoint); + assert.strictEqual(request.method, 'POST'); + assert.strictEqual( + request.headers.get('Content-Type'), + 'application/graphql' + ); + assert.strictEqual(request.headers.get('Authorization'), accessToken); + assert.ok(request.body); + assert.deepStrictEqual(JSON.parse(await text(request.body)), { + query: 'testQuery', + variables: { testVariableKey: 'testVariableValue' }, + }); + }); + + void it('throws if response is not 2xx', async () => { + const fetchMock = mock.fn( + fetch, + (): Promise => + // Mock successful Appsync response + Promise.resolve( + new Response('Body with error', { + status: 400, + headers: { testHeaderKey: 'testHeaderValue' }, + }) + ) + ); + const executor = new GraphqlRequestExecutor( + graphqlEndpoint, + accessToken, + fetchMock + ); + const query = 'testQuery'; + const variables = { + testVariableKey: 'testVariableValue', + }; + await assert.rejects( + () => executor.executeGraphql({ query, variables }), + (error: Error) => { + assert.strictEqual( + error.message, + // eslint-disable-next-line spellcheck/spell-checker + 'GraphQL request failed, response headers={"content-type":"text/plain;charset=UTF-8","testheaderkey":"testHeaderValue"}, body=Body with error' + ); + return true; + } + ); + }); + + void it('throws if graphql returns errors', async () => { + const fetchMock = mock.fn( + fetch, + (): Promise => + // Mock successful Appsync response + Promise.resolve( + new Response( + JSON.stringify({ + errors: ['Some GQL error'], + }), + { + status: 200, + headers: { testHeaderKey: 'testHeaderValue' }, + } + ) + ) + ); + const executor = new GraphqlRequestExecutor( + graphqlEndpoint, + accessToken, + fetchMock + ); + const query = 'testQuery'; + const variables = { + testVariableKey: 'testVariableValue', + }; + await assert.rejects( + () => executor.executeGraphql({ query, variables }), + (error: Error) => { + assert.strictEqual( + error.message, + // eslint-disable-next-line spellcheck/spell-checker + 'GraphQL request failed, response headers={"content-type":"text/plain;charset=UTF-8","testheaderkey":"testHeaderValue"}, body={"errors":["Some GQL error"]}' + ); + return true; + } + ); + }); +}); diff --git a/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.ts b/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.ts new file mode 100644 index 00000000000..17d759deaee --- /dev/null +++ b/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.ts @@ -0,0 +1,57 @@ +export type GraphqlRequest = { + query: string; + variables: TVariables; +}; + +/** + * This class is responsible for executing GraphQL requests. + * Serializing query and it's inputs, adding authorization headers, + * inspecting response for errors and de-serializing output. + */ +export class GraphqlRequestExecutor { + /** + * Creates GraphQL request executor. + */ + constructor( + private readonly graphQlEndpoint: string, + private readonly accessToken: string, + private readonly _fetch = fetch + ) {} + + executeGraphql = async ( + request: GraphqlRequest + ): Promise => { + const httpRequest = new Request(this.graphQlEndpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/graphql', + Authorization: this.accessToken, + }, + body: JSON.stringify({ + query: request.query, + variables: request.variables, + }), + }); + + const res = await this._fetch(httpRequest); + const responseHeaders: Record = {}; + res.headers.forEach((value, key) => (responseHeaders[key] = value)); + if (!res.ok) { + const body = await res.text(); + throw new Error( + `GraphQL request failed, response headers=${JSON.stringify( + responseHeaders + )}, body=${body}` + ); + } + const body = await res.json(); + if (body && typeof body === 'object' && 'errors' in body) { + throw new Error( + `GraphQL request failed, response headers=${JSON.stringify( + responseHeaders + )}, body=${JSON.stringify(body)}` + ); + } + return body as TReturn; + }; +} diff --git a/packages/ai-constructs/src/conversation/runtime/types.ts b/packages/ai-constructs/src/conversation/runtime/types.ts index 3cab0c9925e..5794323fb5c 100644 --- a/packages/ai-constructs/src/conversation/runtime/types.ts +++ b/packages/ai-constructs/src/conversation/runtime/types.ts @@ -57,7 +57,17 @@ export type ConversationTurnEvent = { authorization: string; }; }; - messages: Array; + /** + * @deprecated This field is going to be removed in upcoming releases. + */ + messages?: Array; + messageHistoryQuery: { + getQueryName: string; + getQueryInputTypeName: string; + listQueryName: string; + listQueryInputTypeName: string; + listQueryLimit?: number; + }; toolsConfiguration?: { dataTools?: Array< ToolDefinition & { diff --git a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts index 9c1c52b5a78..1ab7ab21754 100644 --- a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts +++ b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts @@ -7,7 +7,10 @@ import { AmplifyClient } from '@aws-sdk/client-amplify'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; import { InvokeCommand, LambdaClient } from '@aws-sdk/client-lambda'; import { DeployedResourcesFinder } from '../find_deployed_resource.js'; -import { ConversationTurnEvent } from '@aws-amplify/ai-constructs/conversation/runtime'; +import { + ConversationMessage, + ConversationTurnEvent, +} from '@aws-amplify/ai-constructs/conversation/runtime'; import { randomUUID } from 'crypto'; import { generateClientConfig } from '@aws-amplify/client-config'; import { AmplifyAuthCredentialsFactory } from '../amplify_auth_credentials_factory.js'; @@ -48,6 +51,12 @@ type ConversationTurnAppSyncResponse = { content: string; }; +type CreateConversationMessageChatInput = ConversationMessage & { + conversationId: string; + id: string; + associatedUserMessageId?: string; +}; + const commonEventProperties = { responseMutation: { name: 'createConversationMessageAssistantResponse', @@ -56,12 +65,17 @@ const commonEventProperties = { 'id', 'conversationId', 'content', - 'sender', 'owner', 'createdAt', 'updatedAt', ].join('\n'), }, + messageHistoryQuery: { + getQueryName: 'getConversationMessageChat', + getQueryInputTypeName: 'ID', + listQueryName: 'listConversationMessageChats', + listQueryInputTypeName: 'ModelConversationMessageChatFilterInput', + }, modelConfiguration: { modelId: bedrockModelId, systemPrompt: 'You are helpful bot.', @@ -189,7 +203,28 @@ class ConversationHandlerTestProject extends TestProjectBase { backendId, authenticatedUserCredentials.accessToken, clientConfig.data.url, - apolloClient + apolloClient, + // Does not use message history lookup. + // This case should be removed when event.messages field is removed. + false + ); + + await this.assertDefaultConversationHandlerCanExecuteTurn( + backendId, + authenticatedUserCredentials.accessToken, + clientConfig.data.url, + apolloClient, + true + ); + + await this.assertDefaultConversationHandlerCanExecuteTurn( + backendId, + authenticatedUserCredentials.accessToken, + clientConfig.data.url, + apolloClient, + true, + // Simulate eventual consistency + true ); await this.assertCustomConversationHandlerCanExecuteTurn( @@ -217,7 +252,16 @@ class ConversationHandlerTestProject extends TestProjectBase { backendId, authenticatedUserCredentials.accessToken, clientConfig.data.url, - apolloClient + apolloClient, + false + ); + + await this.assertDefaultConversationHandlerCanExecuteTurnWithImage( + backendId, + authenticatedUserCredentials.accessToken, + clientConfig.data.url, + apolloClient, + true ); } @@ -225,7 +269,9 @@ class ConversationHandlerTestProject extends TestProjectBase { backendId: BackendIdentifier, accessToken: string, graphqlApiEndpoint: string, - apolloClient: ApolloClient + apolloClient: ApolloClient, + useMessageHistory: boolean, + withoutMessageAvailableInTheMessageList = false ): Promise => { const defaultConversationHandlerFunction = ( await this.resourceFinder.findByBackendIdentifier( @@ -235,26 +281,51 @@ class ConversationHandlerTestProject extends TestProjectBase { ) )[0]; - // send event - const event: ConversationTurnEvent = { + const message: CreateConversationMessageChatInput = { + id: randomUUID().toString(), conversationId: randomUUID().toString(), - currentMessageId: randomUUID().toString(), - graphqlApiEndpoint: graphqlApiEndpoint, - messages: [ + role: 'user', + content: [ { - role: 'user', - content: [ - { - text: 'What is the value of PI?', - }, - ], + text: 'What is the value of PI?', }, ], + }; + + // send event + const event: ConversationTurnEvent = { + conversationId: message.conversationId, + currentMessageId: message.id, + graphqlApiEndpoint: graphqlApiEndpoint, request: { headers: { authorization: accessToken }, }, ...commonEventProperties, }; + + if (useMessageHistory) { + if (withoutMessageAvailableInTheMessageList) { + // This tricks conversation handler to think that message is not available in the list. + // I.e. it simulates eventually consistency read at list operation where item is not yet visible. + // In this case handler should fall back to lookup by current message id. + message.conversationId = randomUUID().toString(); + } + await this.insertMessage(apolloClient, message); + } else { + event.messageHistoryQuery = { + getQueryName: '', + getQueryInputTypeName: '', + listQueryName: '', + listQueryInputTypeName: '', + }; + event.messages = [ + { + role: message.role, + content: message.content, + }, + ]; + } + const response = await this.executeConversationTurn( event, defaultConversationHandlerFunction, @@ -267,7 +338,8 @@ class ConversationHandlerTestProject extends TestProjectBase { backendId: BackendIdentifier, accessToken: string, graphqlApiEndpoint: string, - apolloClient: ApolloClient + apolloClient: ApolloClient, + useMessageHistory: boolean ): Promise => { const defaultConversationHandlerFunction = ( await this.resourceFinder.findByBackendIdentifier( @@ -291,32 +363,49 @@ class ConversationHandlerTestProject extends TestProjectBase { const imageSource = await fs.readFile(imagePath, 'base64'); - // send event - const event: ConversationTurnEvent = { + const message: CreateConversationMessageChatInput = { + id: randomUUID().toString(), conversationId: randomUUID().toString(), - currentMessageId: randomUUID().toString(), - graphqlApiEndpoint: graphqlApiEndpoint, - messages: [ + role: 'user', + content: [ { - role: 'user', - content: [ - { - text: 'What is on the attached image?', - }, - { - image: { - format: 'png', - source: { bytes: imageSource }, - }, - }, - ], + text: 'What is on the attached image?', + }, + { + image: { + format: 'png', + source: { bytes: imageSource }, + }, }, ], + }; + + // send event + const event: ConversationTurnEvent = { + conversationId: message.conversationId, + currentMessageId: message.id, + graphqlApiEndpoint: graphqlApiEndpoint, request: { headers: { authorization: accessToken }, }, ...commonEventProperties, }; + if (useMessageHistory) { + await this.insertMessage(apolloClient, message); + } else { + event.messageHistoryQuery = { + getQueryName: '', + getQueryInputTypeName: '', + listQueryName: '', + listQueryInputTypeName: '', + }; + event.messages = [ + { + role: message.role, + content: message.content, + }, + ]; + } const response = await this.executeConversationTurn( event, defaultConversationHandlerFunction, @@ -341,21 +430,23 @@ class ConversationHandlerTestProject extends TestProjectBase { ) )[0]; - // send event - const event: ConversationTurnEvent = { + const message: CreateConversationMessageChatInput = { conversationId: randomUUID().toString(), - currentMessageId: randomUUID().toString(), - graphqlApiEndpoint: graphqlApiEndpoint, - messages: [ + id: randomUUID().toString(), + role: 'user', + content: [ { - role: 'user', - content: [ - { - text: 'What is the temperature in Seattle?', - }, - ], + text: 'What is the temperature in Seattle?', }, ], + }; + await this.insertMessage(apolloClient, message); + + // send event + const event: ConversationTurnEvent = { + conversationId: message.conversationId, + currentMessageId: message.id, + graphqlApiEndpoint: graphqlApiEndpoint, request: { headers: { authorization: accessToken }, }, @@ -414,21 +505,23 @@ class ConversationHandlerTestProject extends TestProjectBase { ) )[0]; - // send event - const event: ConversationTurnEvent = { + const message: CreateConversationMessageChatInput = { conversationId: randomUUID().toString(), - currentMessageId: randomUUID().toString(), - graphqlApiEndpoint: graphqlApiEndpoint, - messages: [ + id: randomUUID().toString(), + role: 'user', + content: [ { - role: 'user', - content: [ - { - text: 'What is the temperature in Seattle?', - }, - ], + text: 'What is the temperature in Seattle?', }, ], + }; + await this.insertMessage(apolloClient, message); + + // send event + const event: ConversationTurnEvent = { + conversationId: message.conversationId, + currentMessageId: message.id, + graphqlApiEndpoint: graphqlApiEndpoint, request: { headers: { authorization: accessToken }, }, @@ -482,21 +575,23 @@ class ConversationHandlerTestProject extends TestProjectBase { ) )[0]; - // send event - const event: ConversationTurnEvent = { + const message: CreateConversationMessageChatInput = { conversationId: randomUUID().toString(), - currentMessageId: randomUUID().toString(), - graphqlApiEndpoint: graphqlApiEndpoint, - messages: [ + id: randomUUID().toString(), + role: 'user', + content: [ { - role: 'user', - content: [ - { - text: 'What is the temperature in Seattle?', - }, - ], + text: 'What is the temperature in Seattle?', }, ], + }; + await this.insertMessage(apolloClient, message); + + // send event + const event: ConversationTurnEvent = { + conversationId: message.conversationId, + currentMessageId: message.id, + graphqlApiEndpoint: graphqlApiEndpoint, request: { headers: { authorization: accessToken }, }, @@ -535,10 +630,9 @@ class ConversationHandlerTestProject extends TestProjectBase { }>({ query: gql` query ListMessages { - listConversationMessageAssistantResponses { + listConversationMessageAssistantResponses(limit: 1000) { items { conversationId - sender id updatedAt createdAt @@ -557,4 +651,22 @@ class ConversationHandlerTestProject extends TestProjectBase { assert.ok(response); return response; }; + + private insertMessage = async ( + apolloClient: ApolloClient, + message: CreateConversationMessageChatInput + ): Promise => { + await apolloClient.mutate({ + mutation: gql` + mutation InsertMessage($input: CreateConversationMessageChatInput!) { + createConversationMessageChat(input: $input) { + id + } + } + `, + variables: { + input: message, + }, + }); + }; } diff --git a/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts b/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts index 2ef65c955ba..f6985743100 100644 --- a/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts +++ b/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts @@ -19,13 +19,89 @@ const schema = a.schema({ ) ), - // This schema mocks expected model where conversation responses are supposed to be recorded. + // These schemas below mock models normally generated by conversational routes. + MockConversationParticipantRole: a.enum(['user', 'assistant']), + + MockDocumentBlockSource: a.customType({ + bytes: a.string(), + }), + + MockDocumentBlock: a.customType({ + format: a.string().required(), + name: a.string().required(), + source: a.ref('MockDocumentBlockSource').required(), + }), + + MockImageBlockSource: a.customType({ + bytes: a.string(), + }), + + MockImageBlock: a.customType({ + format: a.string().required(), + source: a.ref('MockImageBlockSource').required(), + }), + + MockToolResultContentBlock: a.customType({ + document: a.ref('MockDocumentBlock'), + image: a.ref('MockImageBlock'), + json: a.json(), + text: a.string(), + }), + + MockToolResultBlock: a.customType({ + toolUseId: a.string().required(), + status: a.string(), + content: a.ref('MockToolResultContentBlock').array().required(), + }), + + MockToolUseBlock: a.customType({ + toolUseId: a.string().required(), + name: a.string().required(), + input: a.json().required(), + }), + + MockContentBlock: a.customType({ + text: a.string(), + document: a.ref('MockDocumentBlock'), + image: a.ref('MockImageBlock'), + toolResult: a.ref('MockToolResultBlock'), + toolUse: a.ref('MockToolUseBlock'), + }), + + MockToolInputSchema: a.customType({ + json: a.json(), + }), + + MockToolSpecification: a.customType({ + name: a.string().required(), + description: a.string(), + inputSchema: a.ref('MockToolInputSchema').required(), + }), + + MockTool: a.customType({ + toolSpec: a.ref('MockToolSpecification'), + }), + + MockToolConfiguration: a.customType({ + tools: a.ref('MockTool').array(), + }), + ConversationMessageAssistantResponse: a .model({ conversationId: a.id(), associatedUserMessageId: a.id(), content: a.string(), - sender: a.enum(['user', 'assistant']), + }) + .authorization((allow) => [allow.authenticated(), allow.owner()]), + + ConversationMessageChat: a + .model({ + conversationId: a.id(), + associatedUserMessageId: a.id(), + role: a.ref('MockConversationParticipantRole'), + content: a.ref('MockContentBlock').array(), + aiContext: a.json(), + toolConfiguration: a.ref('MockToolConfiguration'), }) .authorization((allow) => [allow.authenticated(), allow.owner()]), }); From a8c5e0828c26fe7e3dd93ea448916920570d70e2 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 26 Sep 2024 13:35:53 -0700 Subject: [PATCH 022/199] Increase timeout while waiting for bucked delete. (#2061) * Increase timeout while waiting for bucked delete. * Create khaki-trainers-turn.md --- .changeset/khaki-trainers-turn.md | 2 ++ .../src/test-project-setup/data_storage_auth_with_triggers.ts | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 .changeset/khaki-trainers-turn.md diff --git a/.changeset/khaki-trainers-turn.md b/.changeset/khaki-trainers-turn.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/khaki-trainers-turn.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts index 588f5c7f56c..813e5284689 100644 --- a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts +++ b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts @@ -364,10 +364,10 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { /** * There is some eventual consistency between deleting a bucket and when HeadBucket returns NotFound - * So we are polling HeadBucket until it returns NotFound or until we time out (after 30 seconds) + * So we are polling HeadBucket until it returns NotFound or until we time out (after 60 seconds) */ private waitForBucketDeletion = async (bucketName: string): Promise => { - const TIMEOUT_MS = 1000 * 30; // 30 seconds + const TIMEOUT_MS = 1000 * 60; // 60 seconds const startTime = Date.now(); while (Date.now() - startTime < TIMEOUT_MS) { From 970d5556acae10d7d11eaf241f618c7e9af39e7a Mon Sep 17 00:00:00 2001 From: Leena <81589006+ShadowCat567@users.noreply.github.com> Date: Thu, 26 Sep 2024 18:27:35 -0400 Subject: [PATCH 023/199] Windows line endings in scripts tests (#2059) * strips \r from output of api usage generator tests * added comment to explain change * altered comment * adjust comment again * another change to comment --------- Co-authored-by: Vieltojarvi --- .../api-changes-validator/api_usage_generator.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/components/api-changes-validator/api_usage_generator.test.ts b/scripts/components/api-changes-validator/api_usage_generator.test.ts index d48de411c74..b4e001010d2 100644 --- a/scripts/components/api-changes-validator/api_usage_generator.test.ts +++ b/scripts/components/api-changes-validator/api_usage_generator.test.ts @@ -353,7 +353,11 @@ void describe('Api usage generator', () => { 'samplePackageName', apiReportAST ).generate(); - assert.strictEqual(apiUsage.trim(), testCase.expectedApiUsage.trim()); + assert.strictEqual( + // .replace() removes EOL differences between Windows and other OS so output matches for all + apiUsage.replace(/[\r]/g, '').trim(), + testCase.expectedApiUsage.trim() + ); }); } }); From b7ac6a303a85a306d375b4ef55a375e4aac45ab6 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 27 Sep 2024 09:32:51 -0700 Subject: [PATCH 024/199] update e2e test for storage access outputs (#2063) * update backend output test for storage access outputs * move assertion to post deployment --- .changeset/healthy-planes-live.md | 2 ++ .../src/test-e2e/backend_output.test.ts | 6 ++++- .../data_storage_auth_with_triggers.ts | 26 +++++++++++++++++++ .../amplify/auth/resource.ts | 1 + .../amplify/storage/resource.ts | 8 ++++++ 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 .changeset/healthy-planes-live.md diff --git a/.changeset/healthy-planes-live.md b/.changeset/healthy-planes-live.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/healthy-planes-live.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/test-e2e/backend_output.test.ts b/packages/integration-tests/src/test-e2e/backend_output.test.ts index 88397deba46..50c4715686f 100644 --- a/packages/integration-tests/src/test-e2e/backend_output.test.ts +++ b/packages/integration-tests/src/test-e2e/backend_output.test.ts @@ -24,6 +24,11 @@ import { DataStorageAuthWithTriggerTestProjectCreator } from '../test-project-se import { SQSClient } from '@aws-sdk/client-sqs'; import { setupDeployedBackendClient } from '../test-project-setup/setup_deployed_backend_client.js'; +/** + * This E2E test is to check whether current (aka latest) repository content introduces breaking changes + * for our deployed backend client to read outputs. + */ + // Different root test dir to avoid race conditions with e2e deployment tests const rootTestDir = fileURLToPath( new URL('../e2e-outputs-tests', import.meta.url) @@ -83,7 +88,6 @@ void describe( await testProject.deploy(branchBackendIdentifier, sharedSecretsEnv); await testProject.assertPostDeployment(branchBackendIdentifier); - await testProject.assertDeployedClientOutputs(branchBackendIdentifier); }); } diff --git a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts index 813e5284689..09520c2a4ea 100644 --- a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts +++ b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts @@ -23,6 +23,7 @@ import { SQSClient, } from '@aws-sdk/client-sqs'; import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; +import isMatch from 'lodash.ismatch'; /** * Creates test projects with data, storage, and auth categories. @@ -298,6 +299,31 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { ); assert.ok(fileContent.includes('newKey: string;')); // Env var added via addEnvironment assert.ok(fileContent.includes('TEST_SECRET: string;')); // Env var added via defineFunction + + // assert storage access paths are correct in stack outputs + const outputsObject = JSON.parse( + await fs.readFile( + path.join(this.projectDirPath, 'amplify_outputs.json'), + 'utf-8' + ) + ); + assert.ok( + isMatch(outputsObject.storage.buckets[0].paths, { + 'public/*': { + guest: ['get', 'list'], + authenticated: ['get', 'list', 'write'], + groupsAdmins: ['get', 'list', 'write', 'delete'], + }, + 'protected/*': { + authenticated: ['get', 'list'], + groupsAdmins: ['get', 'list', 'write', 'delete'], + }, + 'protected/${cognito-identity.amazonaws.com:sub}/*': { + // eslint-disable-next-line spellcheck/spell-checker + entityidentity: ['get', 'list', 'write', 'delete'], + }, + }) + ); } private getUpdateReplacementDefinition = (suffix: string) => ({ diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts index e5ff3baa417..097a822ddb2 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts @@ -24,4 +24,5 @@ export const auth = defineAuth({ triggers: { postConfirmation: defaultNodeFunc, }, + groups: ['Admins'], }); diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/storage/resource.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/storage/resource.ts index cfd30953e2a..3af6c5fecf8 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/storage/resource.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/storage/resource.ts @@ -16,6 +16,14 @@ export const storage = defineStorage({ 'public/*': [ allow.resource(defaultNodeFunc).to(['read', 'write']), allow.resource(node16Func).to(['read', 'write']), + allow.guest.to(['read']), + allow.authenticated.to(['read', 'write']), + allow.groups(['Admins']).to(['read', 'write', 'delete']), + ], + 'protected/{entity_id}/*': [ + allow.authenticated.to(['read']), + allow.entity('identity').to(['read', 'write', 'delete']), + allow.groups(['Admins']).to(['read', 'write', 'delete']), ], }), }); From 42959a8017b30b962ecd1205cdc7d6db695bf49c Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Tue, 1 Oct 2024 12:00:00 -0700 Subject: [PATCH 025/199] Use cloud trail events to detect bucket deletion (#2069) * Use cloud trail events to detect bucket deletion * improve that. * improve that. * more imporvement --- .changeset/tasty-pens-fly.md | 2 + package-lock.json | 1204 ++++++++++++----- packages/integration-tests/package.json | 1 + .../src/test-e2e/backend_output.test.ts | 3 + .../data_storage_auth_with_triggers.ts | 73 +- .../test_project_creator.ts | 3 + 6 files changed, 934 insertions(+), 352 deletions(-) create mode 100644 .changeset/tasty-pens-fly.md diff --git a/.changeset/tasty-pens-fly.md b/.changeset/tasty-pens-fly.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/tasty-pens-fly.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/package-lock.json b/package-lock.json index ba819efd320..fef34649860 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6847,6 +6847,554 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/@aws-sdk/client-cloudtrail": { + "version": "3.658.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudtrail/-/client-cloudtrail-3.658.1.tgz", + "integrity": "sha512-OWc5A0zRntybmYsogI+9MjKLbbAz57Mg6gQuyxJJO0d1njKGZfaYn+fYXfI5wEHe4InydbFQuiZOD0LUvXtQMw==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.658.1", + "@aws-sdk/client-sts": "3.658.1", + "@aws-sdk/core": "3.658.1", + "@aws-sdk/credential-provider-node": "3.658.1", + "@aws-sdk/middleware-host-header": "3.654.0", + "@aws-sdk/middleware-logger": "3.654.0", + "@aws-sdk/middleware-recursion-detection": "3.654.0", + "@aws-sdk/middleware-user-agent": "3.654.0", + "@aws-sdk/region-config-resolver": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@aws-sdk/util-user-agent-browser": "3.654.0", + "@aws-sdk/util-user-agent-node": "3.654.0", + "@smithy/config-resolver": "^3.0.8", + "@smithy/core": "^2.4.6", + "@smithy/fetch-http-handler": "^3.2.8", + "@smithy/hash-node": "^3.0.6", + "@smithy/invalid-dependency": "^3.0.6", + "@smithy/middleware-content-length": "^3.0.8", + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.21", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/node-http-handler": "^3.2.3", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.5", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", + "@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.21", + "@smithy/util-defaults-mode-node": "^3.0.21", + "@smithy/util-endpoints": "^2.1.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/client-sso": { + "version": "3.658.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.658.1.tgz", + "integrity": "sha512-lOuaBtqPTYGn6xpXlQF4LsNDsQ8Ij2kOdnk+i69Kp6yS76TYvtUuukyLL5kx8zE1c8WbYtxj9y8VNw9/6uKl7Q==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.658.1", + "@aws-sdk/middleware-host-header": "3.654.0", + "@aws-sdk/middleware-logger": "3.654.0", + "@aws-sdk/middleware-recursion-detection": "3.654.0", + "@aws-sdk/middleware-user-agent": "3.654.0", + "@aws-sdk/region-config-resolver": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@aws-sdk/util-user-agent-browser": "3.654.0", + "@aws-sdk/util-user-agent-node": "3.654.0", + "@smithy/config-resolver": "^3.0.8", + "@smithy/core": "^2.4.6", + "@smithy/fetch-http-handler": "^3.2.8", + "@smithy/hash-node": "^3.0.6", + "@smithy/invalid-dependency": "^3.0.6", + "@smithy/middleware-content-length": "^3.0.8", + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.21", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/node-http-handler": "^3.2.3", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.5", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", + "@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.21", + "@smithy/util-defaults-mode-node": "^3.0.21", + "@smithy/util-endpoints": "^2.1.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.658.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.658.1.tgz", + "integrity": "sha512-RGcZAI3qEA05JszPKwa0cAyp8rnS1nUvs0Sqw4hqLNQ1kD7b7V6CPjRXe7EFQqCOMvM4kGqx0+cEEVTOmBsFLw==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.658.1", + "@aws-sdk/credential-provider-node": "3.658.1", + "@aws-sdk/middleware-host-header": "3.654.0", + "@aws-sdk/middleware-logger": "3.654.0", + "@aws-sdk/middleware-recursion-detection": "3.654.0", + "@aws-sdk/middleware-user-agent": "3.654.0", + "@aws-sdk/region-config-resolver": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@aws-sdk/util-user-agent-browser": "3.654.0", + "@aws-sdk/util-user-agent-node": "3.654.0", + "@smithy/config-resolver": "^3.0.8", + "@smithy/core": "^2.4.6", + "@smithy/fetch-http-handler": "^3.2.8", + "@smithy/hash-node": "^3.0.6", + "@smithy/invalid-dependency": "^3.0.6", + "@smithy/middleware-content-length": "^3.0.8", + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.21", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/node-http-handler": "^3.2.3", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.5", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", + "@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.21", + "@smithy/util-defaults-mode-node": "^3.0.21", + "@smithy/util-endpoints": "^2.1.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.658.1" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/client-sts": { + "version": "3.658.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.658.1.tgz", + "integrity": "sha512-yw9hc5blTnbT1V6mR7Cx9HGc9KQpcLQ1QXj8rntiJi6tIYu3aFNVEyy81JHL7NsuBSeQulJTvHO3y6r3O0sfRg==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.658.1", + "@aws-sdk/core": "3.658.1", + "@aws-sdk/credential-provider-node": "3.658.1", + "@aws-sdk/middleware-host-header": "3.654.0", + "@aws-sdk/middleware-logger": "3.654.0", + "@aws-sdk/middleware-recursion-detection": "3.654.0", + "@aws-sdk/middleware-user-agent": "3.654.0", + "@aws-sdk/region-config-resolver": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@aws-sdk/util-user-agent-browser": "3.654.0", + "@aws-sdk/util-user-agent-node": "3.654.0", + "@smithy/config-resolver": "^3.0.8", + "@smithy/core": "^2.4.6", + "@smithy/fetch-http-handler": "^3.2.8", + "@smithy/hash-node": "^3.0.6", + "@smithy/invalid-dependency": "^3.0.6", + "@smithy/middleware-content-length": "^3.0.8", + "@smithy/middleware-endpoint": "^3.1.3", + "@smithy/middleware-retry": "^3.0.21", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/node-http-handler": "^3.2.3", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.5", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", + "@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.21", + "@smithy/util-defaults-mode-node": "^3.0.21", + "@smithy/util-endpoints": "^2.1.2", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/core": { + "version": "3.658.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.658.1.tgz", + "integrity": "sha512-vJVMoMcSKXK2gBRSu9Ywwv6wQ7tXH8VL1fqB1uVxgCqBZ3IHfqNn4zvpMPWrwgO2/3wv7XFyikGQ5ypPTCw4jA==", + "dev": true, + "dependencies": { + "@smithy/core": "^2.4.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/property-provider": "^3.1.6", + "@smithy/protocol-http": "^4.1.3", + "@smithy/signature-v4": "^4.1.4", + "@smithy/smithy-client": "^3.3.5", + "@smithy/types": "^3.4.2", + "@smithy/util-middleware": "^3.0.6", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.654.0.tgz", + "integrity": "sha512-kogsx3Ql81JouHS7DkheCDU9MYAvK0AokxjcshDveGmf7BbgbWCA8Fnb9wjQyNDaOXNvkZu8Z8rgkX91z324/w==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.658.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.658.1.tgz", + "integrity": "sha512-4ubkJjEVCZflxkZnV1JDQv8P2pburxk1LrEp55telfJRzXrnowzBKwuV2ED0QMNC448g2B3VCaffS+Ct7c4IWQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/fetch-http-handler": "^3.2.8", + "@smithy/node-http-handler": "^3.2.3", + "@smithy/property-provider": "^3.1.6", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.5", + "@smithy/types": "^3.4.2", + "@smithy/util-stream": "^3.1.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.658.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.658.1.tgz", + "integrity": "sha512-2uwOamQg5ppwfegwen1ddPu5HM3/IBSnaGlaKLFhltkdtZ0jiqTZWUtX2V+4Q+buLnT0hQvLS/frQ+7QUam+0Q==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.654.0", + "@aws-sdk/credential-provider-http": "3.658.1", + "@aws-sdk/credential-provider-process": "3.654.0", + "@aws-sdk/credential-provider-sso": "3.658.1", + "@aws-sdk/credential-provider-web-identity": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@smithy/credential-provider-imds": "^3.2.3", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.658.1" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.658.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.658.1.tgz", + "integrity": "sha512-XwxW6N+uPXPYAuyq+GfOEdfL/MZGAlCSfB5gEWtLBFmFbikhmEuqfWtI6CD60OwudCUOh6argd21BsJf8o1SJA==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.654.0", + "@aws-sdk/credential-provider-http": "3.658.1", + "@aws-sdk/credential-provider-ini": "3.658.1", + "@aws-sdk/credential-provider-process": "3.654.0", + "@aws-sdk/credential-provider-sso": "3.658.1", + "@aws-sdk/credential-provider-web-identity": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@smithy/credential-provider-imds": "^3.2.3", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.654.0.tgz", + "integrity": "sha512-PmQoo8sZ9Q2Ow8OMzK++Z9lI7MsRUG7sNq3E72DVA215dhtTICTDQwGlXH2AAmIp7n+G9LLRds+4wo2ehG4mkg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.658.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.658.1.tgz", + "integrity": "sha512-YOagVEsZEk9DmgJEBg+4MBXrPcw/tYas0VQ5OVBqC5XHNbi2OBGJqgmjVPesuu393E7W0VQxtJFDS00O1ewQgA==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sso": "3.658.1", + "@aws-sdk/token-providers": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.654.0.tgz", + "integrity": "sha512-6a2g9gMtZToqSu+CusjNK5zvbLJahQ9di7buO3iXgbizXpLXU1rnawCpWxwslMpT5fLgMSKDnKDrr6wdEk7jSw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.654.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.654.0.tgz", + "integrity": "sha512-rxGgVHWKp8U2ubMv+t+vlIk7QYUaRCHaVpmUlJv0Wv6Q0KeO9a42T9FxHphjOTlCGQOLcjCreL9CF8Qhtb4mdQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/middleware-logger": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.654.0.tgz", + "integrity": "sha512-OQYb+nWlmASyXfRb989pwkJ9EVUMP1CrKn2eyTk3usl20JZmKo2Vjis6I0tLUkMSxMhnBJJlQKyWkRpD/u1FVg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.654.0.tgz", + "integrity": "sha512-gKSomgltKVmsT8sC6W7CrADZ4GHwX9epk3GcH6QhebVO3LA9LRbkL3TwOPUXakxxOLLUTYdOZLIOtFf7iH00lg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.654.0.tgz", + "integrity": "sha512-liCcqPAyRsr53cy2tYu4qeH4MMN0eh9g6k56XzI5xd4SghXH5YWh4qOYAlQ8T66ZV4nPMtD8GLtLXGzsH8moFg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.654.0.tgz", + "integrity": "sha512-ydGOrXJxj3x0sJhsXyTmvJVLAE0xxuTWFJihTl67RtaO7VRNtd82I3P3bwoMMaDn5WpmV5mPo8fEUDRlBm3fPg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/types": "^3.4.2", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/token-providers": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.654.0.tgz", + "integrity": "sha512-D8GeJYmvbfWkQDtTB4owmIobSMexZel0fOoetwvgCQ/7L8VPph3Q2bn1TRRIXvH7wdt6DcDxA3tKMHPBkT3GlA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/property-provider": "^3.1.6", + "@smithy/shared-ini-file-loader": "^3.1.7", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.654.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/types": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.654.0.tgz", + "integrity": "sha512-VWvbED3SV+10QJIcmU/PKjsKilsTV16d1I7/on4bvD/jo1qGeMXqLDBSen3ks/tuvXZF/mFc7ZW/W2DiLVtO7A==", + "dev": true, + "dependencies": { + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/util-endpoints": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.654.0.tgz", + "integrity": "sha512-i902fcBknHs0Irgdpi62+QMvzxE+bczvILXigYrlHL4+PiEnlMVpni5L5W1qCkNZXf8AaMrSBuR1NZAGp6UOUw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", + "@smithy/util-endpoints": "^2.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.654.0.tgz", + "integrity": "sha512-ykYAJqvnxLt7wfrqya28wuH3/7NdrwzfiFd7NqEVQf7dXVxL5RPEpD7DxjcyQo3DsHvvdUvGZVaQhozycn1pzA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.654.0.tgz", + "integrity": "sha512-a0ojjdBN6pqv6gB4H/QPPSfhs7mFtlVwnmKCM/QrTaFzN0U810PJ1BST3lBx5sa23I5jWHGaoFY+5q65C3clLQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@smithy/node-config-provider": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz", + "integrity": "sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==", + "dev": true, + "dependencies": { + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudtrail/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.8.tgz", + "integrity": "sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==", + "dev": true, + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@aws-sdk/client-cloudwatch-logs": { "version": "3.650.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.650.0.tgz", @@ -16131,12 +16679,11 @@ "license": "(Unlicense OR Apache-2.0)" }, "node_modules/@smithy/abort-controller": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.2.tgz", - "integrity": "sha512-b5g+PNujlfqIib9BjkNB108NyO5aZM/RXjfOCXRCqXQ1oPnIkfvdORrztbGgCZdPe/BN/MKDlrGA7PafKPM2jw==", - "license": "Apache-2.0", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.5.tgz", + "integrity": "sha512-DhNPnqTqPoG8aZ5dWkFOgsuY+i0GQ3CI6hMmvCoduNsnU9gUZWZBwGfDQsTTB7NvFPkom1df7jMIJWU90kuXXg==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16163,15 +16710,14 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.6.tgz", - "integrity": "sha512-j7HuVNoRd8EhcFp0MzcUb4fG40C7BcyshH+fAd3Jhd8bINNFvEQYBrZoS/SK6Pun9WPlfoI8uuU2SMz8DsEGlA==", - "license": "Apache-2.0", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.9.tgz", + "integrity": "sha512-5d9oBf40qC7n2xUoHmntKLdqsyTMMo/r49+eqSIjJ73eDfEtljAxEhzIQ3bkgXJtR3xiv7YzMT/3FF3ORkjWdg==", "dependencies": { - "@smithy/node-config-provider": "^3.1.5", - "@smithy/types": "^3.4.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.4", + "@smithy/util-middleware": "^3.0.7", "tslib": "^2.6.2" }, "engines": { @@ -16179,14 +16725,13 @@ } }, "node_modules/@smithy/config-resolver/node_modules/@smithy/node-config-provider": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.5.tgz", - "integrity": "sha512-dq/oR3/LxgCgizVk7in7FGTm0w9a3qM4mg3IIXLTCHeW3fV+ipssSvBZ2bvEx1+asfQJTyCnVLeYf7JKfd9v3Q==", - "license": "Apache-2.0", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz", + "integrity": "sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==", "dependencies": { - "@smithy/property-provider": "^3.1.4", - "@smithy/shared-ini-file-loader": "^3.1.5", - "@smithy/types": "^3.4.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16194,32 +16739,30 @@ } }, "node_modules/@smithy/config-resolver/node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.5.tgz", - "integrity": "sha512-6jxsJ4NOmY5Du4FD0enYegNJl4zTSuKLiChIMqIkh+LapxiP7lmz5lYUNLE9/4cvA65mbBmtdzZ8yxmcqM5igg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.4.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.1.tgz", - "integrity": "sha512-7cts7/Oni7aCHebHGiBeWoz5z+vmH+Vx2Z/UW3XtXMslcxI3PEwBZxNinepwZjixS3n12fPc247PHWmjU7ndsQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-endpoint": "^3.1.1", - "@smithy/middleware-retry": "^3.0.16", - "@smithy/middleware-serde": "^3.0.4", - "@smithy/protocol-http": "^4.1.1", - "@smithy/smithy-client": "^3.3.0", - "@smithy/types": "^3.4.0", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.8.tgz", + "integrity": "sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.7.tgz", + "integrity": "sha512-goqMjX+IoVEnHZjYuzu8xwoZjoteMiLXsPHuXPBkWsGwu0o9c3nTjqkUlP1Ez/V8E501aOU7CJ3INk8mQcW2gw==", + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.22", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.4", + "@smithy/util-middleware": "^3.0.7", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -16228,15 +16771,14 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.1.tgz", - "integrity": "sha512-4z/oTWpRF2TqQI3aCM89/PWu3kim58XU4kOCTtuTJnoaS4KT95cPWMxbQfTN2vzcOe96SOKO8QouQW/+ESB1fQ==", - "license": "Apache-2.0", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.4.tgz", + "integrity": "sha512-S9bb0EIokfYEuar4kEbLta+ivlKCWOCFsLZuilkNy9i0uEUEHSi47IFLPaxqqCl+0ftKmcOTHayY5nQhAuq7+w==", "dependencies": { - "@smithy/node-config-provider": "^3.1.5", - "@smithy/property-provider": "^3.1.4", - "@smithy/types": "^3.4.0", - "@smithy/url-parser": "^3.0.4", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", "tslib": "^2.6.2" }, "engines": { @@ -16244,14 +16786,13 @@ } }, "node_modules/@smithy/credential-provider-imds/node_modules/@smithy/node-config-provider": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.5.tgz", - "integrity": "sha512-dq/oR3/LxgCgizVk7in7FGTm0w9a3qM4mg3IIXLTCHeW3fV+ipssSvBZ2bvEx1+asfQJTyCnVLeYf7JKfd9v3Q==", - "license": "Apache-2.0", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz", + "integrity": "sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==", "dependencies": { - "@smithy/property-provider": "^3.1.4", - "@smithy/shared-ini-file-loader": "^3.1.5", - "@smithy/types": "^3.4.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16259,12 +16800,11 @@ } }, "node_modules/@smithy/credential-provider-imds/node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.5.tgz", - "integrity": "sha512-6jxsJ4NOmY5Du4FD0enYegNJl4zTSuKLiChIMqIkh+LapxiP7lmz5lYUNLE9/4cvA65mbBmtdzZ8yxmcqM5igg==", - "license": "Apache-2.0", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.8.tgz", + "integrity": "sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16339,14 +16879,13 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.5.tgz", - "integrity": "sha512-DjRtGmK8pKQMIo9+JlAKUt14Z448bg8nAN04yKIvlrrpmpRSG57s5d2Y83npks1r4gPtTRNbAFdQCoj9l3P2KQ==", - "license": "Apache-2.0", + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", + "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", "dependencies": { - "@smithy/protocol-http": "^4.1.1", - "@smithy/querystring-builder": "^3.0.4", - "@smithy/types": "^3.4.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", "@smithy/util-base64": "^3.0.0", "tslib": "^2.6.2" } @@ -16364,12 +16903,11 @@ } }, "node_modules/@smithy/hash-node": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.4.tgz", - "integrity": "sha512-6FgTVqEfCr9z/7+Em8BwSkJKA2y3krf1em134x3yr2NHWVCo2KYI8tcA53cjeO47y41jwF84ntsEE0Pe6pNKlg==", - "license": "Apache-2.0", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.7.tgz", + "integrity": "sha512-SAGHN+QkrwcHFjfWzs/czX94ZEjPJ0CrWJS3M43WswDXVEuP4AVy9gJ3+AF6JQHZD13bojmuf/Ap/ItDeZ+Qfw==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -16393,12 +16931,11 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.4.tgz", - "integrity": "sha512-MJBUrojC4SEXi9aJcnNOE3oNAuYNphgCGFXscaCj2TA/59BTcXhzHACP8jnnEU3n4yir/NSLKzxqez0T4x4tjA==", - "license": "Apache-2.0", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.7.tgz", + "integrity": "sha512-Bq00GsAhHeYSuZX8Kpu4sbI9agH2BNYnqUmmbTGWOhki9NVsWn2jFr896vvoTMH8KAjNX/ErC/8t5QHuEXG+IA==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" } }, @@ -16426,13 +16963,12 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.6.tgz", - "integrity": "sha512-AFyHCfe8rumkJkz+hCOVJmBagNBj05KypyDwDElA4TgMSA4eYDZRjVePFZuyABrJZFDc7uVj3dpFIDCEhf59SA==", - "license": "Apache-2.0", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.9.tgz", + "integrity": "sha512-t97PidoGElF9hTtLCrof32wfWMqC5g2SEJNxaVH3NjlatuNGsdxXRYO/t+RPnxA15RpYiS0f+zG7FuE2DeGgjA==", "dependencies": { - "@smithy/protocol-http": "^4.1.1", - "@smithy/types": "^3.4.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16440,17 +16976,16 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.1.tgz", - "integrity": "sha512-Irv+soW8NKluAtFSEsF8O3iGyLxa5oOevJb/e1yNacV9H7JP/yHyJuKST5YY2ORS1+W34VR8EuUrOF+K29Pl4g==", - "license": "Apache-2.0", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.4.tgz", + "integrity": "sha512-/ChcVHekAyzUbyPRI8CzPPLj6y8QRAfJngWcLMgsWxKVzw/RzBV69mSOzJYDD3pRwushA1+5tHtPF8fjmzBnrQ==", "dependencies": { - "@smithy/middleware-serde": "^3.0.4", - "@smithy/node-config-provider": "^3.1.5", - "@smithy/shared-ini-file-loader": "^3.1.5", - "@smithy/types": "^3.4.0", - "@smithy/url-parser": "^3.0.4", - "@smithy/util-middleware": "^3.0.4", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-middleware": "^3.0.7", "tslib": "^2.6.2" }, "engines": { @@ -16458,14 +16993,13 @@ } }, "node_modules/@smithy/middleware-endpoint/node_modules/@smithy/node-config-provider": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.5.tgz", - "integrity": "sha512-dq/oR3/LxgCgizVk7in7FGTm0w9a3qM4mg3IIXLTCHeW3fV+ipssSvBZ2bvEx1+asfQJTyCnVLeYf7JKfd9v3Q==", - "license": "Apache-2.0", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz", + "integrity": "sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==", "dependencies": { - "@smithy/property-provider": "^3.1.4", - "@smithy/shared-ini-file-loader": "^3.1.5", - "@smithy/types": "^3.4.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16473,12 +17007,11 @@ } }, "node_modules/@smithy/middleware-endpoint/node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.5.tgz", - "integrity": "sha512-6jxsJ4NOmY5Du4FD0enYegNJl4zTSuKLiChIMqIkh+LapxiP7lmz5lYUNLE9/4cvA65mbBmtdzZ8yxmcqM5igg==", - "license": "Apache-2.0", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.8.tgz", + "integrity": "sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16486,18 +17019,17 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.16.tgz", - "integrity": "sha512-08kI36p1yB4CWO3Qi+UQxjzobt8iQJpnruF0K5BkbZmA/N/sJ51A1JJGJ36GgcbFyPfWw2FU48S5ZoqXt0h0jw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.5", - "@smithy/protocol-http": "^4.1.1", - "@smithy/service-error-classification": "^3.0.4", - "@smithy/smithy-client": "^3.3.0", - "@smithy/types": "^3.4.0", - "@smithy/util-middleware": "^3.0.4", - "@smithy/util-retry": "^3.0.4", + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.22.tgz", + "integrity": "sha512-svEN7O2Tf7BoaBkPzX/8AE2Bv7p16d9/ulFAD1Gmn5g19iMqNk1WIkMxAY7SpB9/tVtUwKx0NaIsBRl88gumZA==", + "dependencies": { + "@smithy/node-config-provider": "^3.1.8", + "@smithy/protocol-http": "^4.1.4", + "@smithy/service-error-classification": "^3.0.7", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -16506,14 +17038,13 @@ } }, "node_modules/@smithy/middleware-retry/node_modules/@smithy/node-config-provider": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.5.tgz", - "integrity": "sha512-dq/oR3/LxgCgizVk7in7FGTm0w9a3qM4mg3IIXLTCHeW3fV+ipssSvBZ2bvEx1+asfQJTyCnVLeYf7JKfd9v3Q==", - "license": "Apache-2.0", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz", + "integrity": "sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==", "dependencies": { - "@smithy/property-provider": "^3.1.4", - "@smithy/shared-ini-file-loader": "^3.1.5", - "@smithy/types": "^3.4.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16521,12 +17052,11 @@ } }, "node_modules/@smithy/middleware-retry/node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.5.tgz", - "integrity": "sha512-6jxsJ4NOmY5Du4FD0enYegNJl4zTSuKLiChIMqIkh+LapxiP7lmz5lYUNLE9/4cvA65mbBmtdzZ8yxmcqM5igg==", - "license": "Apache-2.0", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.8.tgz", + "integrity": "sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16547,12 +17077,11 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.4.tgz", - "integrity": "sha512-1lPDB2O6IJ50Ucxgn7XrvZXbbuI48HmPCcMTuSoXT1lDzuTUfIuBjgAjpD8YLVMfnrjdepi/q45556LA51Pubw==", - "license": "Apache-2.0", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.7.tgz", + "integrity": "sha512-VytaagsQqtH2OugzVTq4qvjkLNbWehHfGcGr0JLJmlDRrNCeZoWkWsSOw1nhS/4hyUUWF/TLGGml4X/OnEep5g==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16560,12 +17089,11 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.4.tgz", - "integrity": "sha512-sLMRjtMCqtVcrOqaOZ10SUnlFE25BSlmLsi4bRSGFD7dgR54eqBjfqkVkPBQyrKBortfGM0+2DJoUPcGECR+nQ==", - "license": "Apache-2.0", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.7.tgz", + "integrity": "sha512-EyTbMCdqS1DoeQsO4gI7z2Gzq1MoRFAeS8GkFYIwbedB7Lp5zlLHJdg+56tllIIG5Hnf9ZWX48YKSHlsKvugGA==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16613,15 +17141,14 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.0.tgz", - "integrity": "sha512-5TFqaABbiY7uJMKbqR4OARjwI/l4TRoysDJ75pLpVQyO3EcmeloKYwDGyCtgB9WJniFx3BMkmGCB9+j+QiB+Ww==", - "license": "Apache-2.0", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.4.tgz", + "integrity": "sha512-49reY3+JgLMFNm7uTAKBWiKCA6XSvkNp9FqhVmusm2jpVnHORYFeFZ704LShtqWfjZW/nhX+7Iexyb6zQfXYIQ==", "dependencies": { - "@smithy/abort-controller": "^3.1.2", - "@smithy/protocol-http": "^4.1.1", - "@smithy/querystring-builder": "^3.0.4", - "@smithy/types": "^3.4.0", + "@smithy/abort-controller": "^3.1.5", + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16629,12 +17156,11 @@ } }, "node_modules/@smithy/property-provider": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.4.tgz", - "integrity": "sha512-BmhefQbfkSl9DeU0/e6k9N4sT5bya5etv2epvqLUz3eGyfRBhtQq60nDkc1WPp4c+KWrzK721cUc/3y0f2psPQ==", - "license": "Apache-2.0", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.7.tgz", + "integrity": "sha512-QfzLi1GPMisY7bAM5hOUqBdGYnY5S2JAlr201pghksrQv139f8iiiMalXtjczIP5f6owxFn3MINLNUNvUkgtPw==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16642,12 +17168,11 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.1.tgz", - "integrity": "sha512-Fm5+8LkeIus83Y8jTL1XHsBGP8sPvE1rEVyKf/87kbOPTbzEDMcgOlzcmYXat2h+nC3wwPtRy8hFqtJS71+Wow==", - "license": "Apache-2.0", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.4.tgz", + "integrity": "sha512-MlWK8eqj0JlpZBnWmjQLqmFp71Ug00P+m72/1xQB3YByXD4zZ+y9N4hYrR0EDmrUCZIkyATWHOXFgtavwGDTzQ==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16655,12 +17180,11 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.4.tgz", - "integrity": "sha512-NEoPAsZPdpfVbF98qm8i5k1XMaRKeEnO47CaL5ja6Y1Z2DgJdwIJuJkTJypKm/IKfp8gc0uimIFLwhml8+/pAw==", - "license": "Apache-2.0", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.7.tgz", + "integrity": "sha512-65RXGZZ20rzqqxTsChdqSpbhA6tdt5IFNgG6o7e1lnPVLCe6TNWQq4rTl4N87hTDD8mV4IxJJnvyE7brbnRkQw==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "@smithy/util-uri-escape": "^3.0.0", "tslib": "^2.6.2" }, @@ -16669,12 +17193,11 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.4.tgz", - "integrity": "sha512-7CHPXffFcakFzhO0OZs/rn6fXlTHrSDdLhIT6/JIk1u2bvwguTL3fMCc1+CfcbXA7TOhjWXu3TcB1EGMqJQwHg==", - "license": "Apache-2.0", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.7.tgz", + "integrity": "sha512-Fouw4KJVWqqUVIu1gZW8BH2HakwLz6dvdrAhXeXfeymOBrZw+hcqaWs+cS1AZPVp4nlbeIujYrKA921ZW2WMPA==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16682,12 +17205,11 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.4.tgz", - "integrity": "sha512-KciDHHKFVTb9A1KlJHBt2F26PBaDtoE23uTZy5qRvPzHPqrooXFi6fmx98lJb3Jl38PuUTqIuCUmmY3pacuMBQ==", - "license": "Apache-2.0", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.7.tgz", + "integrity": "sha512-91PRkTfiBf9hxkIchhRKJfl1rsplRDyBnmyFca3y0Z3x/q0JJN480S83LBd8R6sBCkm2bBbqw2FHp0Mbh+ecSA==", "dependencies": { - "@smithy/types": "^3.4.0" + "@smithy/types": "^3.5.0" }, "engines": { "node": ">=16.0.0" @@ -16719,16 +17241,15 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.1.tgz", - "integrity": "sha512-SH9J9be81TMBNGCmjhrgMWu4YSpQ3uP1L06u/K9SDrE2YibUix1qxedPCxEQu02At0P0SrYDjvz+y91vLG0KRQ==", - "license": "Apache-2.0", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.0.tgz", + "integrity": "sha512-LafbclHNKnsorMgUkKm7Tk7oJ7xizsZ1VwqhGKqoCIrXh4fqDDp73fK99HOEEgcsQbtemmeY/BPv0vTVYYUNEQ==", "dependencies": { "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.1", - "@smithy/types": "^3.4.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.4", + "@smithy/util-middleware": "^3.0.7", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -16738,16 +17259,15 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.3.0.tgz", - "integrity": "sha512-H32nVo8tIX82kB0xI2LBrIcj8jx/3/ITotNLbeG1UL0b3b440YPR/hUvqjFJiaB24pQrMjRbU8CugqH5sV0hkw==", - "license": "Apache-2.0", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.3.6.tgz", + "integrity": "sha512-qdH+mvDHgq1ss6mocyIl2/VjlWXew7pGwZQydwYJczEc22HZyX3k8yVPV9aZsbYbssHPvMDRA5rfBDrjQUbIIw==", "dependencies": { - "@smithy/middleware-endpoint": "^3.1.1", - "@smithy/middleware-stack": "^3.0.4", - "@smithy/protocol-http": "^4.1.1", - "@smithy/types": "^3.4.0", - "@smithy/util-stream": "^3.1.4", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "@smithy/util-stream": "^3.1.9", "tslib": "^2.6.2" }, "engines": { @@ -16755,10 +17275,9 @@ } }, "node_modules/@smithy/types": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.4.0.tgz", - "integrity": "sha512-0shOWSg/pnFXPcsSU8ZbaJ4JBHZJPPzLCJxafJvbMVFo9l1w81CqpgUqjlKGNHVrVB7fhIs+WS82JDTyzaLyLA==", - "license": "Apache-2.0", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.5.0.tgz", + "integrity": "sha512-QN0twHNfe8mNJdH9unwsCK13GURU7oEAZqkBI+rsvpv1jrmserO+WnLE7jidR9W/1dxwZ0u/CB01mV2Gms/K2Q==", "dependencies": { "tslib": "^2.6.2" }, @@ -16767,13 +17286,12 @@ } }, "node_modules/@smithy/url-parser": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.4.tgz", - "integrity": "sha512-XdXfObA8WrloavJYtDuzoDhJAYc5rOt+FirFmKBRKaihu7QtU/METAxJgSo7uMK6hUkx0vFnqxV75urtRaLkLg==", - "license": "Apache-2.0", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.7.tgz", + "integrity": "sha512-70UbSSR8J97c1rHZOWhl+VKiZDqHWxs/iW8ZHrHp5fCCPLSBE7GcUlUvKSle3Ca+J9LLbYCj/A79BxztBvAfpA==", "dependencies": { - "@smithy/querystring-parser": "^3.0.4", - "@smithy/types": "^3.4.0", + "@smithy/querystring-parser": "^3.0.7", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" } }, @@ -16838,14 +17356,13 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.16.tgz", - "integrity": "sha512-Os8ddfNBe7hmc5UMWZxygIHCyAqY0aWR8Wnp/aKbti3f8Df/r0J9ttMZIxeMjsFgtVjEryB0q7SGcwBsHk8WEw==", - "license": "Apache-2.0", + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.22.tgz", + "integrity": "sha512-WKzUxNsOun5ETwEOrvooXeI1mZ8tjDTOcN4oruELWHhEYDgQYWwxZupURVyovcv+h5DyQT/DzK5nm4ZoR/Tw5Q==", "dependencies": { - "@smithy/property-provider": "^3.1.4", - "@smithy/smithy-client": "^3.3.0", - "@smithy/types": "^3.4.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -16854,17 +17371,16 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.16.tgz", - "integrity": "sha512-rNhFIYRtrOrrhRlj6RL8jWA6/dcwrbGYAmy8+OAHjjzQ6zdzUBB1P+3IuJAgwWN6Y5GxI+mVXlM/pOjaoIgHow==", - "license": "Apache-2.0", + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.22.tgz", + "integrity": "sha512-hUsciOmAq8fsGwqg4+pJfNRmrhfqMH4Y9UeGcgeUl88kPAoYANFATJqCND+O4nUvwp5TzsYwGpqpcBKyA8LUUg==", "dependencies": { - "@smithy/config-resolver": "^3.0.6", - "@smithy/credential-provider-imds": "^3.2.1", - "@smithy/node-config-provider": "^3.1.5", - "@smithy/property-provider": "^3.1.4", - "@smithy/smithy-client": "^3.3.0", - "@smithy/types": "^3.4.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16872,14 +17388,13 @@ } }, "node_modules/@smithy/util-defaults-mode-node/node_modules/@smithy/node-config-provider": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.5.tgz", - "integrity": "sha512-dq/oR3/LxgCgizVk7in7FGTm0w9a3qM4mg3IIXLTCHeW3fV+ipssSvBZ2bvEx1+asfQJTyCnVLeYf7JKfd9v3Q==", - "license": "Apache-2.0", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz", + "integrity": "sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==", "dependencies": { - "@smithy/property-provider": "^3.1.4", - "@smithy/shared-ini-file-loader": "^3.1.5", - "@smithy/types": "^3.4.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16887,12 +17402,11 @@ } }, "node_modules/@smithy/util-defaults-mode-node/node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.5.tgz", - "integrity": "sha512-6jxsJ4NOmY5Du4FD0enYegNJl4zTSuKLiChIMqIkh+LapxiP7lmz5lYUNLE9/4cvA65mbBmtdzZ8yxmcqM5igg==", - "license": "Apache-2.0", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.8.tgz", + "integrity": "sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16900,13 +17414,12 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.0.tgz", - "integrity": "sha512-ilS7/0jcbS2ELdg0fM/4GVvOiuk8/U3bIFXUW25xE1Vh1Ol4DP6vVHQKqM40rCMizCLmJ9UxK+NeJrKlhI3HVA==", - "license": "Apache-2.0", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.3.tgz", + "integrity": "sha512-34eACeKov6jZdHqS5hxBMJ4KyWKztTMulhuQ2UdOoP6vVxMLrOKUqIXAwJe/wiWMhXhydLW664B02CNpQBQ4Aw==", "dependencies": { - "@smithy/node-config-provider": "^3.1.5", - "@smithy/types": "^3.4.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16914,14 +17427,13 @@ } }, "node_modules/@smithy/util-endpoints/node_modules/@smithy/node-config-provider": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.5.tgz", - "integrity": "sha512-dq/oR3/LxgCgizVk7in7FGTm0w9a3qM4mg3IIXLTCHeW3fV+ipssSvBZ2bvEx1+asfQJTyCnVLeYf7JKfd9v3Q==", - "license": "Apache-2.0", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz", + "integrity": "sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==", "dependencies": { - "@smithy/property-provider": "^3.1.4", - "@smithy/shared-ini-file-loader": "^3.1.5", - "@smithy/types": "^3.4.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16929,12 +17441,11 @@ } }, "node_modules/@smithy/util-endpoints/node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.5.tgz", - "integrity": "sha512-6jxsJ4NOmY5Du4FD0enYegNJl4zTSuKLiChIMqIkh+LapxiP7lmz5lYUNLE9/4cvA65mbBmtdzZ8yxmcqM5igg==", - "license": "Apache-2.0", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.8.tgz", + "integrity": "sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16954,12 +17465,11 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.4.tgz", - "integrity": "sha512-uSXHTBhstb1c4nHdmQEdkNMv9LiRNaJ/lWV2U/GO+5F236YFpdPw+hyWI9Zc0Rp9XKzwD9kVZvhZmEgp0UCVnA==", - "license": "Apache-2.0", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.7.tgz", + "integrity": "sha512-OVA6fv/3o7TMJTpTgOi1H5OTwnuUa8hzRzhSFDtZyNxi6OZ70L/FHattSmhE212I7b6WSOJAAmbYnvcjTHOJCA==", "dependencies": { - "@smithy/types": "^3.4.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16967,13 +17477,12 @@ } }, "node_modules/@smithy/util-retry": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.4.tgz", - "integrity": "sha512-JJr6g0tO1qO2tCQyK+n3J18r34ZpvatlFN5ULcLranFIBZPxqoivb77EPyNTVwTGMEvvq2qMnyjm4jMIxjdLFg==", - "license": "Apache-2.0", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.7.tgz", + "integrity": "sha512-nh1ZO1vTeo2YX1plFPSe/OXaHkLAHza5jpokNiiKX2M5YpNUv6RxGJZhpfmiR4jSvVHCjIDmILjrxKmP+/Ghug==", "dependencies": { - "@smithy/service-error-classification": "^3.0.4", - "@smithy/types": "^3.4.0", + "@smithy/service-error-classification": "^3.0.7", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -16981,14 +17490,13 @@ } }, "node_modules/@smithy/util-stream": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.4.tgz", - "integrity": "sha512-txU3EIDLhrBZdGfon6E9V6sZz/irYnKFMblz4TLVjyq8hObNHNS2n9a2t7GIrl7d85zgEPhwLE0gANpZsvpsKg==", - "license": "Apache-2.0", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.9.tgz", + "integrity": "sha512-7YAR0Ub3MwTMjDfjnup4qa6W8gygZMxikBhFMPESi6ASsl/rZJhwLpF/0k9TuezScCojsM0FryGdz4LZtjKPPQ==", "dependencies": { - "@smithy/fetch-http-handler": "^3.2.5", - "@smithy/node-http-handler": "^3.2.0", - "@smithy/types": "^3.4.0", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/types": "^3.5.0", "@smithy/util-base64": "^3.0.0", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-hex-encoding": "^3.0.0", @@ -23297,6 +23805,7 @@ "version": "11.2.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -23971,6 +24480,7 @@ "version": "9.0.6", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.6.tgz", "integrity": "sha512-G95ivKpy+EvVAnAab4fVa4YGYn24J1SpEktnJX7JJ45Bd7xqME/SCplFzYFmTbrkwZbQ4xJK1xMTUYBkN6pWsQ==", + "dev": true, "license": "MIT", "funding": { "type": "opencollective", @@ -30147,6 +30657,7 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, "license": "MIT", "bin": { "uuid": "dist/bin/uuid" @@ -30930,17 +31441,17 @@ } }, "packages/ampx": { - "version": "0.2.1", + "version": "0.2.2", "license": "Apache-2.0" }, "packages/auth-construct": { "name": "@aws-amplify/auth-construct", - "version": "1.3.0", + "version": "1.3.1", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", - "@aws-amplify/backend-output-storage": "^1.1.1", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/plugin-types": "^1.2.2", "@aws-sdk/util-arn-parser": "^3.568.0" }, "peerDependencies": { @@ -30950,20 +31461,20 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.2.1", + "version": "1.3.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-auth": "^1.1.2", - "@aws-amplify/backend-data": "^1.1.3", - "@aws-amplify/backend-function": "^1.4.0", + "@aws-amplify/backend-auth": "^1.2.0", + "@aws-amplify/backend-data": "^1.1.4", + "@aws-amplify/backend-function": "^1.5.0", "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/backend-output-storage": "^1.1.1", - "@aws-amplify/backend-secret": "^1.0.1", - "@aws-amplify/backend-storage": "^1.1.1", - "@aws-amplify/client-config": "^1.3.0", + "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/backend-secret": "^1.1.2", + "@aws-amplify/backend-storage": "^1.2.0", + "@aws-amplify/client-config": "^1.3.1", "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.3.0", "@aws-sdk/client-amplify": "^3.624.0", "lodash.snakecase": "^4.1.1" }, @@ -30996,15 +31507,15 @@ }, "packages/backend-auth": { "name": "@aws-amplify/backend-auth", - "version": "1.1.4", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/auth-construct": "^1.3.0", - "@aws-amplify/backend-output-storage": "^1.1.1", - "@aws-amplify/plugin-types": "^1.2.1" + "@aws-amplify/auth-construct": "^1.3.1", + "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/plugin-types": "^1.3.0" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.4", + "@aws-amplify/backend-platform-test-stubs": "^0.3.5", "@aws-amplify/platform-core": "^1.0.6" }, "peerDependencies": { @@ -31014,17 +31525,17 @@ }, "packages/backend-data": { "name": "@aws-amplify/backend-data", - "version": "1.1.3", + "version": "1.1.4", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", - "@aws-amplify/backend-output-storage": "^1.1.1", + "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/data-construct": "^1.10.1", "@aws-amplify/data-schema-types": "^1.1.1", - "@aws-amplify/plugin-types": "^1.2.1" + "@aws-amplify/plugin-types": "^1.2.2" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.4", + "@aws-amplify/backend-platform-test-stubs": "^0.3.5", "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/platform-core": "^1.0.7" }, @@ -31035,11 +31546,11 @@ }, "packages/backend-deployer": { "name": "@aws-amplify/backend-deployer", - "version": "1.1.2", + "version": "1.1.4", "license": "Apache-2.0", "dependencies": { "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "execa": "^8.0.1", "tsx": "^4.6.1" }, @@ -31050,16 +31561,16 @@ }, "packages/backend-function": { "name": "@aws-amplify/backend-function", - "version": "1.4.0", + "version": "1.5.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", - "@aws-amplify/backend-output-storage": "^1.1.1", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/plugin-types": "^1.3.0", "execa": "^8.0.1" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.4", + "@aws-amplify/backend-platform-test-stubs": "^0.3.5", "@aws-amplify/platform-core": "^1.1.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", @@ -31097,12 +31608,12 @@ }, "packages/backend-output-storage": { "name": "@aws-amplify/backend-output-storage", - "version": "1.1.1", + "version": "1.1.2", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.2.1" + "@aws-amplify/plugin-types": "^1.2.2" }, "peerDependencies": { "aws-cdk-lib": "^2.152.0" @@ -31110,21 +31621,21 @@ }, "packages/backend-platform-test-stubs": { "name": "@aws-amplify/backend-platform-test-stubs", - "version": "0.3.4", + "version": "0.3.5", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "aws-cdk-lib": "^2.152.0", "constructs": "^10.0.0" } }, "packages/backend-secret": { "name": "@aws-amplify/backend-secret", - "version": "1.1.1", + "version": "1.1.3", "license": "Apache-2.0", "dependencies": { "@aws-amplify/platform-core": "^1.0.5", - "@aws-amplify/plugin-types": "^1.1.1", + "@aws-amplify/plugin-types": "^1.2.2", "@aws-sdk/client-ssm": "^3.624.0" }, "devDependencies": { @@ -31133,15 +31644,15 @@ }, "packages/backend-storage": { "name": "@aws-amplify/backend-storage", - "version": "1.1.2", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/backend-output-storage": "^1.1.1", - "@aws-amplify/plugin-types": "^1.2.1" + "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/plugin-types": "^1.3.0" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.4", + "@aws-amplify/backend-platform-test-stubs": "^0.3.5", "@aws-amplify/platform-core": "^1.0.6" }, "peerDependencies": { @@ -31151,21 +31662,21 @@ }, "packages/cli": { "name": "@aws-amplify/backend-cli", - "version": "1.2.6", + "version": "1.2.8", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.1", + "@aws-amplify/backend-deployer": "^1.1.3", "@aws-amplify/backend-output-schemas": "^1.1.0", - "@aws-amplify/backend-secret": "^1.1.0", - "@aws-amplify/cli-core": "^1.1.2", - "@aws-amplify/client-config": "^1.2.1", - "@aws-amplify/deployed-backend-client": "^1.3.0", - "@aws-amplify/form-generator": "^1.0.1", - "@aws-amplify/model-generator": "^1.0.5", + "@aws-amplify/backend-secret": "^1.1.2", + "@aws-amplify/cli-core": "^1.1.3", + "@aws-amplify/client-config": "^1.3.1", + "@aws-amplify/deployed-backend-client": "^1.4.1", + "@aws-amplify/form-generator": "^1.0.3", + "@aws-amplify/model-generator": "^1.0.8", "@aws-amplify/platform-core": "^1.0.5", - "@aws-amplify/plugin-types": "^1.2.1", - "@aws-amplify/sandbox": "^1.2.0", - "@aws-amplify/schema-generator": "^1.2.1", + "@aws-amplify/plugin-types": "^1.3.0", + "@aws-amplify/sandbox": "^1.2.2", + "@aws-amplify/schema-generator": "^1.2.4", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", @@ -31198,7 +31709,7 @@ }, "packages/cli-core": { "name": "@aws-amplify/cli-core", - "version": "1.1.2", + "version": "1.1.3", "license": "Apache-2.0", "dependencies": { "@aws-amplify/platform-core": "^1.0.5", @@ -31305,14 +31816,14 @@ }, "packages/client-config": { "name": "@aws-amplify/client-config", - "version": "1.3.0", + "version": "1.3.2", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/deployed-backend-client": "^1.4.0", - "@aws-amplify/model-generator": "^1.0.5", + "@aws-amplify/deployed-backend-client": "^1.4.1", + "@aws-amplify/model-generator": "^1.0.7", "@aws-amplify/platform-core": "^1.0.7", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "zod": "^3.22.2" }, "devDependencies": { @@ -31327,12 +31838,12 @@ } }, "packages/create-amplify": { - "version": "1.0.5", + "version": "1.0.6", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/cli-core": "^1.1.1", + "@aws-amplify/cli-core": "^1.1.3", "@aws-amplify/platform-core": "^1.0.3", - "@aws-amplify/plugin-types": "^1.1.0", + "@aws-amplify/plugin-types": "^1.2.2", "execa": "^8.0.1", "kleur": "^4.1.5", "yargs": "^17.7.2" @@ -31442,12 +31953,12 @@ }, "packages/deployed-backend-client": { "name": "@aws-amplify/deployed-backend-client", - "version": "1.4.0", + "version": "1.4.1", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", "@aws-amplify/platform-core": "^1.0.5", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "zod": "^3.22.2" }, "peerDependencies": { @@ -31470,7 +31981,7 @@ }, "packages/form-generator": { "name": "@aws-amplify/form-generator", - "version": "1.0.1", + "version": "1.0.3", "license": "Apache-2.0", "dependencies": { "@aws-amplify/appsync-modelgen-plugin": "^2.11.0", @@ -31490,24 +32001,25 @@ }, "packages/integration-tests": { "name": "@aws-amplify/integration-tests", - "version": "0.5.8", + "version": "0.5.9", "license": "Apache-2.0", "devDependencies": { "@apollo/client": "^3.10.1", "@aws-amplify/ai-constructs": "^0.1.0", - "@aws-amplify/auth-construct": "^1.2.2", - "@aws-amplify/backend": "^1.2.1", + "@aws-amplify/auth-construct": "^1.3.1", + "@aws-amplify/backend": "^1.2.2", "@aws-amplify/backend-ai": "^0.1.0", - "@aws-amplify/backend-secret": "^1.0.1", - "@aws-amplify/client-config": "^1.1.3", + "@aws-amplify/backend-secret": "^1.1.2", + "@aws-amplify/client-config": "^1.3.1", "@aws-amplify/data-schema": "^1.0.0", - "@aws-amplify/deployed-backend-client": "^1.3.0", + "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "@aws-sdk/client-accessanalyzer": "^3.624.0", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-bedrock-runtime": "^3.622.0", "@aws-sdk/client-cloudformation": "^3.624.0", + "@aws-sdk/client-cloudtrail": "^3.624.0", "@aws-sdk/client-cognito-identity": "^3.624.0", "@aws-sdk/client-cognito-identity-provider": "^3.624.0", "@aws-sdk/client-iam": "^3.624.0", @@ -31549,15 +32061,15 @@ }, "packages/model-generator": { "name": "@aws-amplify/model-generator", - "version": "1.0.6", + "version": "1.0.8", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", - "@aws-amplify/deployed-backend-client": "^1.3.0", + "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/graphql-generator": "^0.4.0", "@aws-amplify/graphql-types-generator": "^3.6.0", "@aws-amplify/platform-core": "^1.0.5", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.3.0", "@aws-sdk/client-appsync": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/credential-providers": "^3.624.0", @@ -31603,7 +32115,7 @@ }, "packages/plugin-types": { "name": "@aws-amplify/plugin-types", - "version": "1.2.1", + "version": "1.3.0", "license": "Apache-2.0", "devDependencies": { "execa": "^5.1.1" @@ -31732,16 +32244,16 @@ }, "packages/sandbox": { "name": "@aws-amplify/sandbox", - "version": "1.2.1", + "version": "1.2.2", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.0", - "@aws-amplify/backend-secret": "^1.1.1", - "@aws-amplify/cli-core": "^1.1.2", - "@aws-amplify/client-config": "^1.1.3", - "@aws-amplify/deployed-backend-client": "^1.3.0", + "@aws-amplify/backend-deployer": "^1.1.3", + "@aws-amplify/backend-secret": "^1.1.2", + "@aws-amplify/cli-core": "^1.1.3", + "@aws-amplify/client-config": "^1.3.1", + "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.2.2", "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", @@ -31765,7 +32277,7 @@ }, "packages/schema-generator": { "name": "@aws-amplify/schema-generator", - "version": "1.2.2", + "version": "1.2.4", "license": "Apache-2.0", "dependencies": { "@aws-amplify/graphql-schema-generator": "^0.9.4", diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index cfd7db14626..cf0b8a57c5d 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -19,6 +19,7 @@ "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-bedrock-runtime": "^3.622.0", "@aws-sdk/client-cloudformation": "^3.624.0", + "@aws-sdk/client-cloudtrail": "^3.624.0", "@aws-sdk/client-cognito-identity": "^3.624.0", "@aws-sdk/client-cognito-identity-provider": "^3.624.0", "@aws-sdk/client-iam": "^3.624.0", diff --git a/packages/integration-tests/src/test-e2e/backend_output.test.ts b/packages/integration-tests/src/test-e2e/backend_output.test.ts index 50c4715686f..0cde32198c3 100644 --- a/packages/integration-tests/src/test-e2e/backend_output.test.ts +++ b/packages/integration-tests/src/test-e2e/backend_output.test.ts @@ -23,6 +23,7 @@ import { DeployedResourcesFinder } from '../find_deployed_resource.js'; import { DataStorageAuthWithTriggerTestProjectCreator } from '../test-project-setup/data_storage_auth_with_triggers.js'; import { SQSClient } from '@aws-sdk/client-sqs'; import { setupDeployedBackendClient } from '../test-project-setup/setup_deployed_backend_client.js'; +import { CloudTrailClient } from '@aws-sdk/client-cloudtrail'; /** * This E2E test is to check whether current (aka latest) repository content introduces breaking changes @@ -39,6 +40,7 @@ void describe( { concurrency: testConcurrencyLevel }, () => { const cfnClient = new CloudFormationClient(e2eToolingClientConfig); + const cloudTrailClient = new CloudTrailClient(e2eToolingClientConfig); const amplifyClient = new AmplifyClient(e2eToolingClientConfig); const secretClient = getSecretClient(e2eToolingClientConfig); const lambdaClient = new LambdaClient(e2eToolingClientConfig); @@ -55,6 +57,7 @@ void describe( s3Client, iamClient, sqsClient, + cloudTrailClient, resourceFinder ); diff --git a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts index 09520c2a4ea..27077d8359a 100644 --- a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts +++ b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts @@ -22,6 +22,10 @@ import { ReceiveMessageCommand, SQSClient, } from '@aws-sdk/client-sqs'; +import { + CloudTrailClient, + LookupEventsCommand, +} from '@aws-sdk/client-cloudtrail'; import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; import isMatch from 'lodash.ismatch'; @@ -44,6 +48,7 @@ export class DataStorageAuthWithTriggerTestProjectCreator private readonly s3Client: S3Client, private readonly iamClient: IAMClient, private readonly sqsClient: SQSClient, + private readonly cloudTrailClient: CloudTrailClient, private readonly resourceFinder: DeployedResourcesFinder ) {} @@ -62,6 +67,7 @@ export class DataStorageAuthWithTriggerTestProjectCreator this.s3Client, this.iamClient, this.sqsClient, + this.cloudTrailClient, this.resourceFinder ); await fs.cp( @@ -128,6 +134,7 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { private readonly s3Client: S3Client, private readonly iamClient: IAMClient, private readonly sqsClient: SQSClient, + private readonly cloudTrailClient: CloudTrailClient, private readonly resourceFinder: DeployedResourcesFinder ) { super( @@ -390,21 +397,42 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { /** * There is some eventual consistency between deleting a bucket and when HeadBucket returns NotFound - * So we are polling HeadBucket until it returns NotFound or until we time out (after 60 seconds) + * So we are polling HeadBucket and CloudTrail events + * until it returns NotFound or until we time out (after 3 minutes) */ private waitForBucketDeletion = async (bucketName: string): Promise => { - const TIMEOUT_MS = 1000 * 60; // 60 seconds + // Poll for 3 minutes. + // If HeadBucket doesn't become eventually consistent then + // there's at least pretty good chance that BucketDelete event + // managed to arrive at CloudTrail. + const TIMEOUT_MS = 1000 * 60 * 3; const startTime = Date.now(); - while (Date.now() - startTime < TIMEOUT_MS) { + let elapsedTimeMs = 0; + let pollingIntervalMs = 1000; + do { const bucketExists = await this.checkBucketExists(bucketName); if (!bucketExists) { // bucket has been deleted return; } - // wait a second before polling again - await new Promise((resolve) => setTimeout(resolve, 1000)); - } + // Start querying Cloud Trail after a minute. + // So that we don't burn down request quota unnecessarily. + if (elapsedTimeMs >= 1000 * 60) { + // Bump polling interval to wait 10 seconds before polling again. + // Cloud trail has low TPS quota. + pollingIntervalMs = 10000; + const deleteBucketEventArrived = + await this.checkIfDeleteBucketEventArrived(bucketName); + if (deleteBucketEventArrived) { + // bucket has been deleted + return; + } + } + + await new Promise((resolve) => setTimeout(resolve, pollingIntervalMs)); + elapsedTimeMs = Date.now() - startTime; + } while (elapsedTimeMs < TIMEOUT_MS); assert.fail(`Timed out waiting for ${bucketName} to be deleted`); }; @@ -421,6 +449,39 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { } }; + private checkIfDeleteBucketEventArrived = async ( + bucketName: string + ): Promise => { + try { + const lookupEventsResponse = await this.cloudTrailClient.send( + new LookupEventsCommand({ + LookupAttributes: [ + { + AttributeKey: 'EventName', + AttributeValue: 'DeleteBucket', + }, + { + AttributeKey: 'ResourceType', + AttributeValue: 'AWS::S3::Bucket', + }, + { + AttributeKey: 'ResourceName', + AttributeValue: bucketName, + }, + ], + }) + ); + return (lookupEventsResponse.Events?.length ?? 0) > 0; + } catch (err) { + if (err instanceof Error && err.name === 'ThrottlingException') { + // This is a best effort check. + // If we get throttled pretend that we haven't seen event yet. + return false; + } + throw err; + } + }; + private assertRolesDoNotExist = async (roleNames: string[]) => { const TIMEOUT_MS = 1000 * 60 * 5; // IAM Role stabilization can take up to 2 minutes and we are waiting in between each GetRole call to avoid throttling const startTime = Date.now(); diff --git a/packages/integration-tests/src/test-project-setup/test_project_creator.ts b/packages/integration-tests/src/test-project-setup/test_project_creator.ts index 4f8ad607a23..95cf523aa4e 100644 --- a/packages/integration-tests/src/test-project-setup/test_project_creator.ts +++ b/packages/integration-tests/src/test-project-setup/test_project_creator.ts @@ -16,6 +16,7 @@ import { CognitoIdentityClient } from '@aws-sdk/client-cognito-identity'; import { STSClient } from '@aws-sdk/client-sts'; import { AmplifyClient } from '@aws-sdk/client-amplify'; import { SQSClient } from '@aws-sdk/client-sqs'; +import { CloudTrailClient } from '@aws-sdk/client-cloudtrail'; export type TestProjectCreator = { readonly name: string; @@ -29,6 +30,7 @@ export const getTestProjectCreators = (): TestProjectCreator[] => { const testProjectCreators: TestProjectCreator[] = []; const cfnClient = new CloudFormationClient(e2eToolingClientConfig); + const cloudTrailClient = new CloudTrailClient(e2eToolingClientConfig); const amplifyClient = new AmplifyClient(e2eToolingClientConfig); const cognitoIdentityClient = new CognitoIdentityClient( e2eToolingClientConfig @@ -52,6 +54,7 @@ export const getTestProjectCreators = (): TestProjectCreator[] => { s3Client, iamClient, sqsClient, + cloudTrailClient, resourceFinder ), new MinimalWithTypescriptIdiomTestProjectCreator(cfnClient, amplifyClient), From 27202b41269d029e7efc392d9f6340da36f12389 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 19:19:18 +0000 Subject: [PATCH 026/199] Version Packages (#2060) Co-authored-by: github-actions[bot] --- .changeset/healthy-planes-live.md | 2 -- .changeset/khaki-trainers-turn.md | 2 -- .changeset/plenty-wombats-fry.md | 5 ----- .changeset/seven-rabbits-poke.md | 2 -- .changeset/short-rats-kneel.md | 2 -- .changeset/tasty-pens-fly.md | 2 -- .changeset/two-sloths-clap.md | 10 ---------- packages/ai-constructs/CHANGELOG.md | 6 ++++++ packages/ai-constructs/package.json | 2 +- packages/backend-ai/CHANGELOG.md | 9 +++++++++ packages/backend-ai/package.json | 6 +++--- packages/backend-output-schemas/CHANGELOG.md | 6 ++++++ packages/backend-output-schemas/package.json | 2 +- packages/backend-storage/CHANGELOG.md | 8 ++++++++ packages/backend-storage/package.json | 4 ++-- packages/backend/CHANGELOG.md | 10 ++++++++++ packages/backend/package.json | 8 ++++---- packages/cli/CHANGELOG.md | 9 +++++++++ packages/cli/package.json | 6 +++--- packages/client-config/CHANGELOG.md | 11 +++++++++++ packages/client-config/package.json | 4 ++-- packages/integration-tests/CHANGELOG.md | 6 ++++++ packages/integration-tests/package.json | 10 +++++----- 23 files changed, 86 insertions(+), 46 deletions(-) delete mode 100644 .changeset/healthy-planes-live.md delete mode 100644 .changeset/khaki-trainers-turn.md delete mode 100644 .changeset/plenty-wombats-fry.md delete mode 100644 .changeset/seven-rabbits-poke.md delete mode 100644 .changeset/short-rats-kneel.md delete mode 100644 .changeset/tasty-pens-fly.md delete mode 100644 .changeset/two-sloths-clap.md diff --git a/.changeset/healthy-planes-live.md b/.changeset/healthy-planes-live.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/healthy-planes-live.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/khaki-trainers-turn.md b/.changeset/khaki-trainers-turn.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/khaki-trainers-turn.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/plenty-wombats-fry.md b/.changeset/plenty-wombats-fry.md deleted file mode 100644 index 9908e3e6713..00000000000 --- a/.changeset/plenty-wombats-fry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': minor ---- - -Use message history instead of event payload for conversational route diff --git a/.changeset/seven-rabbits-poke.md b/.changeset/seven-rabbits-poke.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/seven-rabbits-poke.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/short-rats-kneel.md b/.changeset/short-rats-kneel.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/short-rats-kneel.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/tasty-pens-fly.md b/.changeset/tasty-pens-fly.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/tasty-pens-fly.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/two-sloths-clap.md b/.changeset/two-sloths-clap.md deleted file mode 100644 index 35f1fcf7c65..00000000000 --- a/.changeset/two-sloths-clap.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@aws-amplify/client-config': minor -'@aws-amplify/backend-output-schemas': patch -'@aws-amplify/integration-tests': patch -'@aws-amplify/backend-storage': patch -'@aws-amplify/backend': patch -'@aws-amplify/backend-cli': patch ---- - -add storage access rules to outputs diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index fe7736ec0e9..afddd6721fc 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/ai-constructs +## 0.2.0 + +### Minor Changes + +- d0a90b1: Use message history instead of event payload for conversational route + ## 0.1.4 ### Patch Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index 693bcf24cde..0f7b7458db1 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "0.1.4", + "version": "0.2.0", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index 6ea84e7fdad..ab8d66df297 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,14 @@ # @aws-amplify/backend-ai +## 0.1.2 + +### Patch Changes + +- Updated dependencies [d0a90b1] +- Updated dependencies [d538ecc] + - @aws-amplify/ai-constructs@0.2.0 + - @aws-amplify/backend-output-schemas@1.2.1 + ## 0.1.1 ### Patch Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index d758fedfa3d..62d03369991 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "0.1.1", + "version": "0.1.2", "type": "module", "publishConfig": { "access": "public" @@ -22,8 +22,8 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.1.4", - "@aws-amplify/backend-output-schemas": "^1.1.0", + "@aws-amplify/ai-constructs": "^0.2.0", + "@aws-amplify/backend-output-schemas": "^1.2.1", "@aws-amplify/backend-output-storage": "^1.0.2", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.0.1" diff --git a/packages/backend-output-schemas/CHANGELOG.md b/packages/backend-output-schemas/CHANGELOG.md index ff823df4ef7..1b23c4492bf 100644 --- a/packages/backend-output-schemas/CHANGELOG.md +++ b/packages/backend-output-schemas/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/backend-output-schemas +## 1.2.1 + +### Patch Changes + +- d538ecc: add storage access rules to outputs + ## 1.2.0 ### Minor Changes diff --git a/packages/backend-output-schemas/package.json b/packages/backend-output-schemas/package.json index e72ee388c35..935afd17758 100644 --- a/packages/backend-output-schemas/package.json +++ b/packages/backend-output-schemas/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-output-schemas", - "version": "1.2.0", + "version": "1.2.1", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/backend-storage/CHANGELOG.md b/packages/backend-storage/CHANGELOG.md index 117fdf5186d..e5eb78f3b45 100644 --- a/packages/backend-storage/CHANGELOG.md +++ b/packages/backend-storage/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-storage +## 1.2.1 + +### Patch Changes + +- d538ecc: add storage access rules to outputs +- Updated dependencies [d538ecc] + - @aws-amplify/backend-output-schemas@1.2.1 + ## 1.2.0 ### Minor Changes diff --git a/packages/backend-storage/package.json b/packages/backend-storage/package.json index 913852c427f..ac19d604725 100644 --- a/packages/backend-storage/package.json +++ b/packages/backend-storage/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-storage", - "version": "1.2.0", + "version": "1.2.1", "type": "module", "publishConfig": { "access": "public" @@ -19,7 +19,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.2.0", + "@aws-amplify/backend-output-schemas": "^1.2.1", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/plugin-types": "^1.3.0" }, diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 47fbc092bc8..aedc9875027 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/backend +## 1.3.1 + +### Patch Changes + +- d538ecc: add storage access rules to outputs +- Updated dependencies [d538ecc] + - @aws-amplify/client-config@1.4.0 + - @aws-amplify/backend-output-schemas@1.2.1 + - @aws-amplify/backend-storage@1.2.1 + ## 1.3.0 ### Minor Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index d2c31853fb4..cc14951bbc0 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.3.0", + "version": "1.3.1", "type": "module", "publishConfig": { "access": "public" @@ -29,11 +29,11 @@ "@aws-amplify/backend-auth": "^1.2.0", "@aws-amplify/backend-function": "^1.5.0", "@aws-amplify/backend-data": "^1.1.4", - "@aws-amplify/backend-output-schemas": "^1.2.0", + "@aws-amplify/backend-output-schemas": "^1.2.1", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/backend-storage": "^1.2.0", - "@aws-amplify/client-config": "^1.3.1", + "@aws-amplify/backend-storage": "^1.2.1", + "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.3.0", "@aws-sdk/client-amplify": "^3.624.0", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index a28a6bf3749..6a1e1bfa24f 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,14 @@ # @aws-amplify/backend-cli +## 1.2.9 + +### Patch Changes + +- d538ecc: add storage access rules to outputs +- Updated dependencies [d538ecc] + - @aws-amplify/client-config@1.4.0 + - @aws-amplify/backend-output-schemas@1.2.1 + ## 1.2.8 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 84f45f654e7..a0559a1dc41 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-cli", - "version": "1.2.8", + "version": "1.2.9", "description": "Command line interface for various Amplify tools", "bin": { "ampx": "lib/ampx.js", @@ -32,10 +32,10 @@ "homepage": "https://github.com/aws-amplify/amplify-backend#readme", "dependencies": { "@aws-amplify/backend-deployer": "^1.1.3", - "@aws-amplify/backend-output-schemas": "^1.1.0", + "@aws-amplify/backend-output-schemas": "^1.2.1", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.1.3", - "@aws-amplify/client-config": "^1.3.1", + "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/form-generator": "^1.0.3", "@aws-amplify/model-generator": "^1.0.8", diff --git a/packages/client-config/CHANGELOG.md b/packages/client-config/CHANGELOG.md index 4f6f163ebb1..e459d5953bf 100644 --- a/packages/client-config/CHANGELOG.md +++ b/packages/client-config/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/client-config +## 1.4.0 + +### Minor Changes + +- d538ecc: add storage access rules to outputs + +### Patch Changes + +- Updated dependencies [d538ecc] + - @aws-amplify/backend-output-schemas@1.2.1 + ## 1.3.2 ### Patch Changes diff --git a/packages/client-config/package.json b/packages/client-config/package.json index 242634a8c4e..d5d44aba63b 100644 --- a/packages/client-config/package.json +++ b/packages/client-config/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/client-config", - "version": "1.3.2", + "version": "1.4.0", "type": "module", "publishConfig": { "access": "public" @@ -24,7 +24,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.2.0", + "@aws-amplify/backend-output-schemas": "^1.2.1", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/model-generator": "^1.0.7", "@aws-amplify/platform-core": "^1.0.7", diff --git a/packages/integration-tests/CHANGELOG.md b/packages/integration-tests/CHANGELOG.md index e9486358715..24616d36960 100644 --- a/packages/integration-tests/CHANGELOG.md +++ b/packages/integration-tests/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/integration-tests +## 0.5.10 + +### Patch Changes + +- d538ecc: add storage access rules to outputs + ## 0.5.9 ### Patch Changes diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index cf0b8a57c5d..3f3b56c6d27 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -1,16 +1,16 @@ { "name": "@aws-amplify/integration-tests", "private": true, - "version": "0.5.9", + "version": "0.5.10", "type": "module", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.1.0", + "@aws-amplify/ai-constructs": "^0.2.0", "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend": "^1.2.2", - "@aws-amplify/backend-ai": "^0.1.0", + "@aws-amplify/backend": "^1.3.1", + "@aws-amplify/backend-ai": "^0.1.2", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/client-config": "^1.3.1", + "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/platform-core": "^1.1.0", From 300a72d7105558dfc1bb0dfa2d11092ac3a462e5 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 3 Oct 2024 12:53:54 -0700 Subject: [PATCH 027/199] Infer executable tool input type from input schema (#2070) * Infer executable tool input type from input schema * Infer executable tool input type from input schema * tests * npm install * that is better * fix that --- .changeset/eleven-mails-scream.md | 6 + package-lock.json | 66 ++++--- packages/ai-constructs/API.md | 32 ++-- packages/ai-constructs/package.json | 6 +- .../runtime/bedrock_converse_adapter.test.ts | 9 +- .../runtime/bedrock_converse_adapter.ts | 5 +- .../runtime/conversation_turn_executor.ts | 7 +- .../event_tools_provider.test.ts | 14 +- .../event-tools-provider/graphql_tool.ts | 17 +- .../runtime/executable_tool_factory.test.ts | 164 ++++++++++++++++++ .../runtime/executable_tool_factory.ts | 32 ++++ .../src/conversation/runtime/index.ts | 8 +- .../src/conversation/runtime/types.ts | 24 +-- packages/backend-ai/API.md | 13 +- packages/backend-ai/package.json | 1 - .../src/conversation/runtime/index.ts | 23 ++- .../custom_handler.ts | 40 +++-- 17 files changed, 375 insertions(+), 92 deletions(-) create mode 100644 .changeset/eleven-mails-scream.md create mode 100644 packages/ai-constructs/src/conversation/runtime/executable_tool_factory.test.ts create mode 100644 packages/ai-constructs/src/conversation/runtime/executable_tool_factory.ts diff --git a/.changeset/eleven-mails-scream.md b/.changeset/eleven-mails-scream.md new file mode 100644 index 00000000000..590877bfccb --- /dev/null +++ b/.changeset/eleven-mails-scream.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/ai-constructs': minor +'@aws-amplify/backend-ai': minor +--- + +Infer executable tool input type from input schema diff --git a/package-lock.json b/package-lock.json index fef34649860..9bca614a8d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25458,6 +25458,18 @@ "dev": true, "license": "(AFL-2.1 OR BSD-3-Clause)" }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -30068,6 +30080,11 @@ "node": ">=0.10.0" } }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==" + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -31428,12 +31445,16 @@ }, "packages/ai-constructs": { "name": "@aws-amplify/ai-constructs", - "version": "0.1.4", + "version": "0.2.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/plugin-types": "^1.0.1", "@aws-sdk/client-bedrock-runtime": "^3.622.0", - "@smithy/types": "^3.3.0" + "@smithy/types": "^3.3.0", + "json-schema-to-ts": "^3.1.1" + }, + "devDependencies": { + "typescript": "^5.0.0" }, "peerDependencies": { "aws-cdk-lib": "^2.152.0", @@ -31461,17 +31482,17 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.3.0", + "version": "1.3.1", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-auth": "^1.2.0", "@aws-amplify/backend-data": "^1.1.4", "@aws-amplify/backend-function": "^1.5.0", - "@aws-amplify/backend-output-schemas": "^1.2.0", + "@aws-amplify/backend-output-schemas": "^1.2.1", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/backend-storage": "^1.2.0", - "@aws-amplify/client-config": "^1.3.1", + "@aws-amplify/backend-storage": "^1.2.1", + "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.3.0", @@ -31490,17 +31511,16 @@ }, "packages/backend-ai": { "name": "@aws-amplify/backend-ai", - "version": "0.1.1", + "version": "0.1.2", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.1.4", - "@aws-amplify/backend-output-schemas": "^1.1.0", + "@aws-amplify/ai-constructs": "^0.2.0", + "@aws-amplify/backend-output-schemas": "^1.2.1", "@aws-amplify/backend-output-storage": "^1.0.2", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.0.1" }, "peerDependencies": { - "@smithy/types": "^3.3.0", "aws-cdk-lib": "^2.152.0", "constructs": "^10.0.0" } @@ -31597,7 +31617,7 @@ }, "packages/backend-output-schemas": { "name": "@aws-amplify/backend-output-schemas", - "version": "1.2.0", + "version": "1.2.1", "license": "Apache-2.0", "devDependencies": { "@aws-amplify/plugin-types": "^1.2.0" @@ -31644,10 +31664,10 @@ }, "packages/backend-storage": { "name": "@aws-amplify/backend-storage", - "version": "1.2.0", + "version": "1.2.1", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.2.0", + "@aws-amplify/backend-output-schemas": "^1.2.1", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/plugin-types": "^1.3.0" }, @@ -31662,14 +31682,14 @@ }, "packages/cli": { "name": "@aws-amplify/backend-cli", - "version": "1.2.8", + "version": "1.2.9", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-deployer": "^1.1.3", - "@aws-amplify/backend-output-schemas": "^1.1.0", + "@aws-amplify/backend-output-schemas": "^1.2.1", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.1.3", - "@aws-amplify/client-config": "^1.3.1", + "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/form-generator": "^1.0.3", "@aws-amplify/model-generator": "^1.0.8", @@ -31816,10 +31836,10 @@ }, "packages/client-config": { "name": "@aws-amplify/client-config", - "version": "1.3.2", + "version": "1.4.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.2.0", + "@aws-amplify/backend-output-schemas": "^1.2.1", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/model-generator": "^1.0.7", "@aws-amplify/platform-core": "^1.0.7", @@ -32001,16 +32021,16 @@ }, "packages/integration-tests": { "name": "@aws-amplify/integration-tests", - "version": "0.5.9", + "version": "0.5.10", "license": "Apache-2.0", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.1.0", + "@aws-amplify/ai-constructs": "^0.2.0", "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend": "^1.2.2", - "@aws-amplify/backend-ai": "^0.1.0", + "@aws-amplify/backend": "^1.3.1", + "@aws-amplify/backend-ai": "^0.1.2", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/client-config": "^1.3.1", + "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/platform-core": "^1.1.0", diff --git a/packages/ai-constructs/API.md b/packages/ai-constructs/API.md index 8141429128e..e90b8ccf16f 100644 --- a/packages/ai-constructs/API.md +++ b/packages/ai-constructs/API.md @@ -9,8 +9,8 @@ import * as bedrock from '@aws-sdk/client-bedrock-runtime'; import { Construct } from 'constructs'; import { FunctionResources } from '@aws-amplify/plugin-types'; +import * as jsonSchemaToTypeScript from 'json-schema-to-ts'; import { ResourceProvider } from '@aws-amplify/plugin-types'; -import * as smithy from '@smithy/types'; declare namespace __export__conversation { export { @@ -25,10 +25,12 @@ declare namespace __export__conversation__runtime { ConversationMessage, ConversationMessageContentBlock, ConversationTurnEvent, + createExecutableTool, ExecutableTool, + FromJSONSchema, + JSONSchema, handleConversationTurnEvent, ToolDefinition, - ToolExecutionInput, ToolInputSchema, ToolResultContentBlock } @@ -111,28 +113,36 @@ type ConversationTurnEvent = { }; }; +// @public +const createExecutableTool: >(name: string, description: string, inputSchema: ToolInputSchema, handler: (input: TToolInput) => Promise) => ExecutableTool; + // @public (undocumented) -type ExecutableTool = ToolDefinition & { - execute: (input: ToolExecutionInput | undefined) => Promise; +type ExecutableTool> = ToolDefinition & { + execute: (input: TToolInput) => Promise; }; +// @public (undocumented) +type FromJSONSchema = jsonSchemaToTypeScript.FromSchema; + // @public const handleConversationTurnEvent: (event: ConversationTurnEvent, props?: { - tools?: Array; + tools?: Array>; }) => Promise; // @public (undocumented) -type ToolDefinition = { +type JSONSchema = jsonSchemaToTypeScript.JSONSchema; + +// @public (undocumented) +type ToolDefinition = { name: string; description: string; - inputSchema: ToolInputSchema; + inputSchema: ToolInputSchema; }; // @public (undocumented) -type ToolExecutionInput = smithy.DocumentType; - -// @public (undocumented) -type ToolInputSchema = bedrock.ToolInputSchema; +type ToolInputSchema = { + json: TJSONSchema; +}; // @public (undocumented) type ToolResultContentBlock = bedrock.ToolResultContentBlock; diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index 0f7b7458db1..2d5668775fa 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -28,7 +28,11 @@ "dependencies": { "@aws-amplify/plugin-types": "^1.0.1", "@aws-sdk/client-bedrock-runtime": "^3.622.0", - "@smithy/types": "^3.3.0" + "@smithy/types": "^3.3.0", + "json-schema-to-ts": "^3.1.1" + }, + "devDependencies": { + "typescript": "^5.0.0" }, "peerDependencies": { "aws-cdk-lib": "^2.152.0", diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts index 3b27b1a7d20..81330612f44 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts @@ -14,6 +14,7 @@ import { ConverseCommandOutput, Message, ToolConfiguration, + ToolInputSchema, ToolResultContentBlock, } from '@aws-sdk/client-bedrock-runtime'; import { ConversationTurnEventToolsProvider } from './event-tools-provider'; @@ -252,14 +253,14 @@ void describe('Bedrock converse adapter', () => { toolSpec: { name: eventTool.name, description: eventTool.description, - inputSchema: eventTool.inputSchema, + inputSchema: eventTool.inputSchema as ToolInputSchema, }, }, { toolSpec: { name: additionalTool.name, description: additionalTool.description, - inputSchema: additionalTool.inputSchema, + inputSchema: additionalTool.inputSchema as ToolInputSchema, }, }, ], @@ -689,14 +690,14 @@ void describe('Bedrock converse adapter', () => { toolSpec: { name: additionalTool.name, description: additionalTool.description, - inputSchema: additionalTool.inputSchema, + inputSchema: additionalTool.inputSchema as ToolInputSchema, }, }, { toolSpec: { name: clientTool.name, description: clientTool.description, - inputSchema: clientTool.inputSchema, + inputSchema: clientTool.inputSchema as ToolInputSchema, }, }, ], diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts index 5ab89d09f92..a2185ea0cc4 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts @@ -7,6 +7,7 @@ import { Message, Tool, ToolConfiguration, + ToolInputSchema, } from '@aws-sdk/client-bedrock-runtime'; import { ConversationTurnEvent, @@ -171,7 +172,9 @@ export class BedrockConverseAdapter { toolSpec: { name: t.name, description: t.description, - inputSchema: t.inputSchema, + // We have to cast to bedrock type as we're using different types to describe JSON schema in our API. + // These types are runtime compatible. + inputSchema: t.inputSchema as ToolInputSchema, }, }; }), diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts index 79f87a05cf4..60613c6f28d 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts @@ -1,5 +1,5 @@ import { ConversationTurnResponseSender } from './conversation_turn_response_sender.js'; -import { ConversationTurnEvent, ExecutableTool } from './types.js'; +import { ConversationTurnEvent, ExecutableTool, JSONSchema } from './types.js'; import { BedrockConverseAdapter } from './bedrock_converse_adapter.js'; /** @@ -54,7 +54,10 @@ export class ConversationTurnExecutor { */ export const handleConversationTurnEvent = async ( event: ConversationTurnEvent, - props?: { tools?: Array } + // This is by design, so that tools with different input types can be added + // to single arrays. Downstream code doesn't use these types. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + props?: { tools?: Array> } ): Promise => { await new ConversationTurnExecutor(event, props?.tools ?? []).execute(); }; diff --git a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.test.ts b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.test.ts index 8b3db55ea73..c35fa7adcc5 100644 --- a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.test.ts @@ -36,14 +36,17 @@ void describe('events tool provider', () => { description: 'toolDescription1', inputSchema: { json: { - tool1: 'value1', + type: 'object', + properties: { + tool1Property: { type: 'string' }, + }, }, }, graphqlRequestInputDescriptor: { queryName: 'queryName1', selectionSet: 'selection1', propertyTypes: { - property1: 'type1', + tool1Property: 'type1', }, }, }; @@ -52,14 +55,17 @@ void describe('events tool provider', () => { description: 'toolDescription2', inputSchema: { json: { - tool1: 'value2', + type: 'object', + properties: { + tool2Property: { type: 'string' }, + }, }, }, graphqlRequestInputDescriptor: { queryName: 'queryName2', selectionSet: 'selection2', propertyTypes: { - property1: 'type2', + tool2Property: 'type2', }, }, }; diff --git a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts index 6173e3ac63c..14212ccd240 100644 --- a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts +++ b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts @@ -1,22 +1,19 @@ -import { ExecutableTool } from '../types'; -import type { - ToolInputSchema, - ToolResultContentBlock, -} from '@aws-sdk/client-bedrock-runtime'; +import { ExecutableTool, JSONSchema, ToolInputSchema } from '../types'; +import type { ToolResultContentBlock } from '@aws-sdk/client-bedrock-runtime'; import { DocumentType } from '@smithy/types'; import { GraphqlRequestExecutor } from '../graphql_request_executor'; /** * A tool that use GraphQl queries. */ -export class GraphQlTool implements ExecutableTool { +export class GraphQlTool implements ExecutableTool { /** * Creates GraphQl Tool */ constructor( public name: string, public description: string, - public inputSchema: ToolInputSchema, + public inputSchema: ToolInputSchema, readonly graphQlEndpoint: string, private readonly query: string, readonly accessToken: string, @@ -27,19 +24,19 @@ export class GraphQlTool implements ExecutableTool { ) {} execute = async ( - input: DocumentType | undefined + input: unknown | undefined ): Promise => { if (!input) { throw Error(`GraphQl tool '${this.name}' requires input to execute.`); } const response = await this.graphqlRequestExecutor.executeGraphql< - DocumentType, + unknown, DocumentType >({ query: this.query, variables: input, }); - return { json: response as DocumentType }; + return { json: response }; }; } diff --git a/packages/ai-constructs/src/conversation/runtime/executable_tool_factory.test.ts b/packages/ai-constructs/src/conversation/runtime/executable_tool_factory.test.ts new file mode 100644 index 00000000000..b5ae5f701e7 --- /dev/null +++ b/packages/ai-constructs/src/conversation/runtime/executable_tool_factory.test.ts @@ -0,0 +1,164 @@ +import { describe, it, mock } from 'node:test'; +import assert from 'node:assert'; +import ts from 'typescript'; +import path from 'path'; +import { createExecutableTool } from './executable_tool_factory'; +import { ToolResultContentBlock } from './types'; + +/** + * This function compiles a TypeScript snippet in memory. + * Inspired by https://stackoverflow.com/questions/53733138/how-do-i-type-check-a-snippet-of-typescript-code-in-memory + */ +const compileInMemory = (rootDir: string, text: string) => { + const options = ts.getDefaultCompilerOptions(); + options.strict = true; + const inMemoryFilePath = path.resolve(path.join(rootDir, '__dummy-file.ts')); + const textAst = ts.createSourceFile( + inMemoryFilePath, + text, + options.target || ts.ScriptTarget.Latest + ); + const host = ts.createCompilerHost(options, true); + + const overrideIfInMemoryFile = ( + methodName: 'getSourceFile' | 'readFile' | 'fileExists', + inMemoryValue: unknown + ) => { + // This is intentional, we don't care about function signature, we just + // want to intercept it. + // eslint-disable-next-line @typescript-eslint/ban-types + const originalMethod: Function = host[methodName]; + mock.method(host, methodName, (...args: unknown[]) => { + // resolve the path because typescript will normalize it + // to forward slashes on windows + const filePath = path.resolve(args[0] as string); + if (filePath === inMemoryFilePath) return inMemoryValue; + return originalMethod.apply(host, args); + }); + }; + + overrideIfInMemoryFile('getSourceFile', textAst); + overrideIfInMemoryFile('readFile', text); + overrideIfInMemoryFile('fileExists', true); + + const program = ts.createProgram({ + options, + rootNames: [inMemoryFilePath], + host, + }); + + return ts.getPreEmitDiagnostics(program, textAst); +}; + +void describe('Executable Tool Factory', () => { + void it('creates a functional executable tool', async () => { + const toolName = 'testToolName'; + const toolDescription = 'testToolDescription'; + const inputSchema = { + type: 'object', + properties: { + testProperty: { type: 'string' }, + }, + required: ['testProperty'], + } as const; + type TypeMatchingSchema = { + testProperty: string; + }; + const tool = createExecutableTool( + toolName, + toolDescription, + { json: inputSchema }, + async (input) => { + const inputText: string = input.testProperty; + return { + text: inputText, + } satisfies ToolResultContentBlock; + } + ); + assert.strictEqual(tool.name, toolName); + assert.strictEqual(tool.description, toolDescription); + assert.deepStrictEqual(tool.inputSchema.json, inputSchema); + const input1: TypeMatchingSchema = { + testProperty: 'testPropertyValue1', + }; + const output1 = await tool.execute(input1); + assert.strictEqual(output1.text, input1.testProperty); + const input2: TypeMatchingSchema = { + testProperty: 'testPropertyValue2', + }; + const output2 = await tool.execute(input2); + assert.strictEqual(output2.text, input2.testProperty); + }); + + void it('fails compilation if unknown property is used', () => { + const sourceSnippet = ` + import { createExecutableTool } from './executable_tool_factory'; + import { ToolResultContentBlock } from './types'; + + const inputSchema = { + type: 'object', + properties: { + testProperty: { type: 'string' }, + }, + required: ['testProperty'], + } as const; + + createExecutableTool( + 'testName', + 'testDescription', + { json: inputSchema }, + async (input) => { + // This should trigger compiler as properties not in schema are 'unknown'. + const someNonExistingPropertyValue: string = input.someNonExistingProperty; + return { + text: 'testResultText', + } satisfies ToolResultContentBlock; + } + ); +`; + + const diagnostics = compileInMemory(__dirname, sourceSnippet); + assert.strictEqual(1, diagnostics.length); + // Properties not in schema are 'unknown'. + assert.strictEqual( + diagnostics[0].messageText, + "Type 'unknown' is not assignable to type 'string'." + ); + }); + + void it('allows overriding input type', () => { + const sourceSnippet = ` + import { createExecutableTool } from './executable_tool_factory'; + import { ToolResultContentBlock } from './types'; + + const inputSchema = { + type: 'object', + properties: { + testProperty: { type: 'string' }, + }, + required: ['testProperty'], + } as const; + + type OverrideInputType = { + someOverriddenProperty: string + } + + createExecutableTool( + 'testName', + 'testDescription', + { json: inputSchema }, + async (input) => { + // This should not trigger compiler because type is overridden. + const someOverriddenPropertyValue: string = input.someOverriddenProperty; + return { + text: 'testResultText', + } satisfies ToolResultContentBlock; + } + ); +`; + + const diagnostics = compileInMemory(__dirname, sourceSnippet); + // Assert that compiler is happy. + assert.strictEqual(0, diagnostics.length); + }); +}); diff --git a/packages/ai-constructs/src/conversation/runtime/executable_tool_factory.ts b/packages/ai-constructs/src/conversation/runtime/executable_tool_factory.ts new file mode 100644 index 00000000000..8b5186ba4d2 --- /dev/null +++ b/packages/ai-constructs/src/conversation/runtime/executable_tool_factory.ts @@ -0,0 +1,32 @@ +import { + ExecutableTool, + FromJSONSchema, + JSONSchema, + ToolInputSchema, +} from './types'; +import * as bedrock from '@aws-sdk/client-bedrock-runtime'; + +/** + * Creates an executable tool. + */ +export const createExecutableTool: < + TJSONSchema extends JSONSchema = JSONSchema, + TToolInput = FromJSONSchema +>( + name: string, + description: string, + inputSchema: ToolInputSchema, + handler: (input: TToolInput) => Promise +) => ExecutableTool = ( + name, + description, + inputSchema, + handler +) => { + return { + name, + description, + inputSchema, + execute: handler, + }; +}; diff --git a/packages/ai-constructs/src/conversation/runtime/index.ts b/packages/ai-constructs/src/conversation/runtime/index.ts index 72f1300087e..632d100cbfd 100644 --- a/packages/ai-constructs/src/conversation/runtime/index.ts +++ b/packages/ai-constructs/src/conversation/runtime/index.ts @@ -3,22 +3,26 @@ import { ConversationMessageContentBlock, ConversationTurnEvent, ExecutableTool, + FromJSONSchema, + JSONSchema, ToolDefinition, - ToolExecutionInput, ToolInputSchema, ToolResultContentBlock, } from './types.js'; import { handleConversationTurnEvent } from './conversation_turn_executor.js'; +import { createExecutableTool } from './executable_tool_factory.js'; export { ConversationMessage, ConversationMessageContentBlock, ConversationTurnEvent, + createExecutableTool, ExecutableTool, + FromJSONSchema, + JSONSchema, handleConversationTurnEvent, ToolDefinition, - ToolExecutionInput, ToolInputSchema, ToolResultContentBlock, }; diff --git a/packages/ai-constructs/src/conversation/runtime/types.ts b/packages/ai-constructs/src/conversation/runtime/types.ts index 5794323fb5c..0e68a60237b 100644 --- a/packages/ai-constructs/src/conversation/runtime/types.ts +++ b/packages/ai-constructs/src/conversation/runtime/types.ts @@ -1,15 +1,18 @@ import * as bedrock from '@aws-sdk/client-bedrock-runtime'; -import * as smithy from '@smithy/types'; +import * as jsonSchemaToTypeScript from 'json-schema-to-ts'; /* Notice: This file contains types that are exposed publicly. Therefore, we avoid eager introduction of types that wouldn't be useful for public API consumer and potentially pollute syntax assist in IDEs. */ - -export type ToolInputSchema = bedrock.ToolInputSchema; +export type JSONSchema = jsonSchemaToTypeScript.JSONSchema; +export type FromJSONSchema = + jsonSchemaToTypeScript.FromSchema; +export type ToolInputSchema = { + json: TJSONSchema; +}; export type ToolResultContentBlock = bedrock.ToolResultContentBlock; -export type ToolExecutionInput = smithy.DocumentType; export type ConversationMessage = { role: 'user' | 'assistant'; @@ -25,10 +28,10 @@ export type ConversationMessageContentBlock = }; }; -export type ToolDefinition = { +export type ToolDefinition = { name: string; description: string; - inputSchema: ToolInputSchema; + inputSchema: ToolInputSchema; }; // Customers are not expected to create events themselves, therefore @@ -82,8 +85,9 @@ export type ConversationTurnEvent = { }; }; -export type ExecutableTool = ToolDefinition & { - execute: ( - input: ToolExecutionInput | undefined - ) => Promise; +export type ExecutableTool< + TJSONSchema extends JSONSchema = JSONSchema, + TToolInput = FromJSONSchema +> = ToolDefinition & { + execute: (input: TToolInput) => Promise; }; diff --git a/packages/backend-ai/API.md b/packages/backend-ai/API.md index 623d8380f5e..16afe92398c 100644 --- a/packages/backend-ai/API.md +++ b/packages/backend-ai/API.md @@ -5,7 +5,6 @@ ```ts import { ConstructFactory } from '@aws-amplify/plugin-types'; -import { DocumentType } from '@smithy/types'; import { FunctionResources } from '@aws-amplify/plugin-types'; import { ResourceProvider } from '@aws-amplify/plugin-types'; import * as runtime from '@aws-amplify/ai-constructs/conversation/runtime'; @@ -23,7 +22,8 @@ declare namespace __export__conversation__runtime { ToolResultContentBlock, ExecutableTool, ConversationTurnEvent, - handleConversationTurnEvent + handleConversationTurnEvent, + createExecutableTool } } export { __export__conversation__runtime } @@ -31,6 +31,9 @@ export { __export__conversation__runtime } // @public (undocumented) type ConversationTurnEvent = runtime.ConversationTurnEvent; +// @public (undocumented) +const createExecutableTool: >(name: string, description: string, inputSchema: runtime.ToolInputSchema, handler: (input: TToolInput) => Promise) => ExecutableTool; + // @public const defineConversationHandlerFunction: (props: DefineConversationHandlerFunctionProps) => ConstructFactory>; @@ -47,13 +50,13 @@ type DefineConversationHandlerFunctionProps = { }; // @public (undocumented) -type ExecutableTool = runtime.ToolDefinition & { - execute: (input: DocumentType | undefined) => Promise; +type ExecutableTool> = runtime.ToolDefinition & { + execute: (input: TToolInput) => Promise; }; // @public (undocumented) const handleConversationTurnEvent: (event: ConversationTurnEvent, props?: { - tools?: Array; + tools?: Array>; }) => Promise; // @public (undocumented) diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 62d03369991..20e47e789a8 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -29,7 +29,6 @@ "@aws-amplify/plugin-types": "^1.0.1" }, "peerDependencies": { - "@smithy/types": "^3.3.0", "aws-cdk-lib": "^2.152.0", "constructs": "^10.0.0" } diff --git a/packages/backend-ai/src/conversation/runtime/index.ts b/packages/backend-ai/src/conversation/runtime/index.ts index e758247893f..1b26bb981d6 100644 --- a/packages/backend-ai/src/conversation/runtime/index.ts +++ b/packages/backend-ai/src/conversation/runtime/index.ts @@ -1,17 +1,32 @@ import * as runtime from '@aws-amplify/ai-constructs/conversation/runtime'; -import { DocumentType } from '@smithy/types'; // Re-export types useful for lambda runtime customization. // Some of these types are partially re-defined so that their member use // symbols from same package. export type ToolResultContentBlock = runtime.ToolResultContentBlock; -export type ExecutableTool = runtime.ToolDefinition & { - execute: (input: DocumentType | undefined) => Promise; +export type ExecutableTool< + TJSONSchema extends runtime.JSONSchema = runtime.JSONSchema, + TToolInput = runtime.FromJSONSchema +> = runtime.ToolDefinition & { + execute: (input: TToolInput) => Promise; }; export type ConversationTurnEvent = runtime.ConversationTurnEvent; export const handleConversationTurnEvent: ( event: ConversationTurnEvent, - props?: { tools?: Array } + // This is by design, so that tools with different input types can be added + // to single arrays. Downstream code doesn't use these types. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + props?: { tools?: Array> } ) => Promise = runtime.handleConversationTurnEvent; + +export const createExecutableTool: < + TJSONSchema extends runtime.JSONSchema = runtime.JSONSchema, + TToolInput = runtime.FromJSONSchema +>( + name: string, + description: string, + inputSchema: runtime.ToolInputSchema, + handler: (input: TToolInput) => Promise +) => ExecutableTool = runtime.createExecutableTool; diff --git a/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts b/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts index 53d2944f5b7..99f67fbf3c1 100644 --- a/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts +++ b/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts @@ -1,24 +1,36 @@ import { ConversationTurnEvent, - ExecutableTool, - ToolResultContentBlock, + createExecutableTool, handleConversationTurnEvent, } from '@aws-amplify/backend-ai/conversation/runtime'; import { expectedTemperatureInProgrammaticToolScenario } from '../constants.js'; -const thermometer: ExecutableTool = { - name: 'thermometer', - description: 'Returns current temperature in Seattle', - execute: (): Promise => { - return Promise.resolve({ - // We use this value in test assertion. - // LLM uses tool to get temperature and serves this value in final response. - // We're matching number only as LLM may translate unit to something more descriptive. - text: `${expectedTemperatureInProgrammaticToolScenario}F`, - }); +const thermometerInputSchema = { + type: 'object', + properties: { + city: { type: 'string' }, }, - inputSchema: { json: { type: 'object' } }, -}; + required: ['city'], +} as const; + +const thermometer = createExecutableTool( + 'thermometer', + 'Returns current temperature in cities', + { + json: thermometerInputSchema, + }, + (input) => { + if (input.city === 'Seattle') { + return Promise.resolve({ + // We use this value in test assertion. + // LLM uses tool to get temperature and serves this value in final response. + // We're matching number only as LLM may translate unit to something more descriptive. + text: `${expectedTemperatureInProgrammaticToolScenario}F`, + }); + } + throw new Error(`Unknown city ${input.city}`); + } +); /** * Handler with simple tool. From 0a5e51c126bac996eb4171a99e22110447e27762 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Fri, 4 Oct 2024 10:05:35 -0700 Subject: [PATCH 028/199] Stream conversation logs in sandbox (#2073) * Stream conversation logs in sandbox * fix that * refactor that * and that * this works * Undo the hack * more tests * that * Update packages/sandbox/src/lambda_function_log_streamer.ts Co-authored-by: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> * use different output * Revert "Undo the hack" This reverts commit 3e8b590a7a9d3957d0bb2a31ab5dcfa5b0c25e1a. * use different outputs * use different outputs * Revert "Revert "Undo the hack"" This reverts commit 3c2d41710cc01de7900d1867cfc551d3be1dcb78. * fix that --------- Co-authored-by: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> --- .changeset/eleven-snails-move.md | 9 + .prettierignore | 4 +- package-lock.json | 5 +- packages/ai-constructs/API.md | 3 + packages/ai-constructs/package.json | 3 + .../conversation_handler_construct.test.ts | 51 ++++++ .../conversation_handler_construct.ts | 44 ++++- .../runtime/bedrock_converse_adapter.ts | 14 +- .../conversation_turn_executor.test.ts | 6 + .../runtime/conversation_turn_executor.ts | 1 + .../conversation_turn_response_sender.ts | 11 +- packages/ai-constructs/tsconfig.json | 7 +- .../src/conversation/factory.test.ts | 4 +- .../backend-ai/src/conversation/factory.ts | 27 +-- packages/backend-output-schemas/API.md | 60 +++++++ .../src/ai/conversation/index.ts | 14 ++ .../src/ai/conversation/v1.ts | 8 + packages/backend-output-schemas/src/index.ts | 16 ++ packages/sandbox/package.json | 2 - .../src/lambda_function_log_streamer.test.ts | 156 ++++++++++++------ .../src/lambda_function_log_streamer.ts | 109 +++--------- .../sandbox/src/sandbox_singleton_factory.ts | 3 - 22 files changed, 375 insertions(+), 182 deletions(-) create mode 100644 .changeset/eleven-snails-move.md create mode 100644 packages/backend-output-schemas/src/ai/conversation/index.ts create mode 100644 packages/backend-output-schemas/src/ai/conversation/v1.ts diff --git a/.changeset/eleven-snails-move.md b/.changeset/eleven-snails-move.md new file mode 100644 index 00000000000..1ee858b3764 --- /dev/null +++ b/.changeset/eleven-snails-move.md @@ -0,0 +1,9 @@ +--- +'@aws-amplify/ai-constructs': minor +'@aws-amplify/backend-ai': minor +'@aws-amplify/backend-output-schemas': minor +'@aws-amplify/backend': patch +'@aws-amplify/sandbox': patch +--- + +Stream conversation logs in sandbox diff --git a/.prettierignore b/.prettierignore index c4daa53d7c2..d7e58b0dd3d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ # Ignore artifacts: +.amplify build coverage bin @@ -10,6 +11,7 @@ verdaccio-cache expected-cdk-out .changeset/pre.json concurrent_workspace_script_cache.json +packages/integration-tests/src/e2e-tests scripts/components/api-changes-validator/test-resources/working-directory /test-projects -testDir \ No newline at end of file +testDir diff --git a/package-lock.json b/package-lock.json index 9bca614a8d1..4c4620c988c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31448,12 +31448,15 @@ "version": "0.2.0", "license": "Apache-2.0", "dependencies": { + "@aws-amplify/backend-output-schemas": "^1.2.1", + "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.0.1", "@aws-sdk/client-bedrock-runtime": "^3.622.0", "@smithy/types": "^3.3.0", "json-schema-to-ts": "^3.1.1" }, "devDependencies": { + "@aws-amplify/backend-output-storage": "^1.1.2", "typescript": "^5.0.0" }, "peerDependencies": { @@ -32274,13 +32277,11 @@ "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/platform-core": "^1.0.6", "@aws-amplify/plugin-types": "^1.2.2", - "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "@aws-sdk/credential-providers": "^3.624.0", "@aws-sdk/types": "^3.609.0", - "@aws-sdk/util-arn-parser": "^3.568.0", "@parcel/watcher": "^2.4.1", "debounce-promise": "^3.1.2", "glob": "^10.2.7", diff --git a/packages/ai-constructs/API.md b/packages/ai-constructs/API.md index e90b8ccf16f..04b61c0baf3 100644 --- a/packages/ai-constructs/API.md +++ b/packages/ai-constructs/API.md @@ -6,6 +6,8 @@ /// +import { AIConversationOutput } from '@aws-amplify/backend-output-schemas'; +import { BackendOutputStorageStrategy } from '@aws-amplify/plugin-types'; import * as bedrock from '@aws-sdk/client-bedrock-runtime'; import { Construct } from 'constructs'; import { FunctionResources } from '@aws-amplify/plugin-types'; @@ -51,6 +53,7 @@ type ConversationHandlerFunctionProps = { modelId: string; region?: string; }>; + outputStorageStrategy?: BackendOutputStorageStrategy; }; // @public (undocumented) diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index 2d5668775fa..e4d3fede0f3 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -26,12 +26,15 @@ }, "license": "Apache-2.0", "dependencies": { + "@aws-amplify/backend-output-schemas": "^1.2.1", + "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.0.1", "@aws-sdk/client-bedrock-runtime": "^3.622.0", "@smithy/types": "^3.3.0", "json-schema-to-ts": "^3.1.1" }, "devDependencies": { + "@aws-amplify/backend-output-storage": "^1.1.2", "typescript": "^5.0.0" }, "peerDependencies": { diff --git a/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts b/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts index 07cfa8404ad..93b87e8ea3d 100644 --- a/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts +++ b/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts @@ -4,6 +4,7 @@ import { App, Stack } from 'aws-cdk-lib'; import { ConversationHandlerFunction } from './conversation_handler_construct'; import { Template } from 'aws-cdk-lib/assertions'; import path from 'path'; +import { StackMetadataBackendOutputStorageStrategy } from '@aws-amplify/backend-output-storage'; void describe('Conversation Handler Function construct', () => { void it('creates handler with log group with JWT token redacting policy', () => { @@ -140,6 +141,56 @@ void describe('Conversation Handler Function construct', () => { }); }); + void it('does not store output if output strategy is absent', () => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [ + { + modelId: 'testModelId', + }, + ], + outputStorageStrategy: undefined, + }); + const template = Template.fromStack(stack); + const output = template.findOutputs( + 'definedConversationHandlers' + ).definedConversationHandlers; + assert.ok(!output); + }); + + void it('stores output if output strategy is present', () => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [ + { + modelId: 'testModelId', + }, + ], + outputStorageStrategy: new StackMetadataBackendOutputStorageStrategy( + stack + ), + }); + const template = Template.fromStack(stack); + const outputValue = template.findOutputs('definedConversationHandlers') + .definedConversationHandlers.Value; + assert.deepStrictEqual(outputValue, { + 'Fn::Join': [ + '', + [ + '["', + { + /* eslint-disable spellcheck/spell-checker */ + Ref: 'conversationHandlerconversationHandlerFunction45BC2E1F', + /* eslint-enable spellcheck/spell-checker */ + }, + '"]', + ], + ], + }); + }); + void it('throws if entry is not absolute', () => { const app = new App(); const stack = new Stack(app); diff --git a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts index 57b33f779f8..b3e32d894d1 100644 --- a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts +++ b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts @@ -1,7 +1,15 @@ -import { FunctionResources, ResourceProvider } from '@aws-amplify/plugin-types'; -import { Duration, Stack } from 'aws-cdk-lib'; +import { + BackendOutputStorageStrategy, + FunctionResources, + ResourceProvider, +} from '@aws-amplify/plugin-types'; +import { Duration, Stack, Tags } from 'aws-cdk-lib'; import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam'; -import { CfnFunction, Runtime as LambdaRuntime } from 'aws-cdk-lib/aws-lambda'; +import { + CfnFunction, + Runtime as LambdaRuntime, + LoggingFormat, +} from 'aws-cdk-lib/aws-lambda'; import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; import { CustomDataIdentifier, @@ -11,6 +19,11 @@ import { } from 'aws-cdk-lib/aws-logs'; import { Construct } from 'constructs'; import path from 'path'; +import { TagName } from '@aws-amplify/platform-core'; +import { + AIConversationOutput, + aiConversationOutputKey, +} from '@aws-amplify/backend-output-schemas'; const resourcesRoot = path.normalize(path.join(__dirname, 'runtime')); const defaultHandlerFilePath = path.join(resourcesRoot, 'default_handler.js'); @@ -21,6 +34,10 @@ export type ConversationHandlerFunctionProps = { modelId: string; region?: string; }>; + /** + * @internal + */ + outputStorageStrategy?: BackendOutputStorageStrategy; }; /** @@ -53,6 +70,8 @@ export class ConversationHandlerFunction throw new Error('Entry must be absolute path'); } + Tags.of(this).add(TagName.FRIENDLY_NAME, id); + const conversationHandler = new NodejsFunction( this, `conversationHandlerFunction`, @@ -67,6 +86,7 @@ export class ConversationHandlerFunction // For custom entry we do bundle SDK as we can't control version customer is coding against. bundleAwsSDK: !!this.props.entry, }, + loggingFormat: LoggingFormat.JSON, logGroup: new LogGroup(this, 'conversationHandlerFunctionLogGroup', { retention: RetentionDays.INFINITE, dataProtectionPolicy: new DataProtectionPolicy({ @@ -105,5 +125,23 @@ export class ConversationHandlerFunction ) as CfnFunction, }, }; + + this.storeOutput(this.props.outputStorageStrategy); } + + /** + * Append conversation handler to defined functions. + */ + private storeOutput = ( + outputStorageStrategy: + | BackendOutputStorageStrategy + | undefined + ): void => { + outputStorageStrategy?.appendToBackendOutputList(aiConversationOutputKey, { + version: '1', + payload: { + definedConversationHandlers: this.resources.lambda.functionName, + }, + }); + }; } diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts index a2185ea0cc4..721c9e09d6a 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts @@ -41,7 +41,8 @@ export class BedrockConverseAdapter { eventToolsProvider = new ConversationTurnEventToolsProvider(event), private readonly messageHistoryRetriever = new ConversationMessageHistoryRetriever( event - ) + ), + private readonly logger = console ) { this.executableTools = [ ...eventToolsProvider.getEventTools(), @@ -91,9 +92,16 @@ export class BedrockConverseAdapter { inferenceConfig: inferenceConfiguration, toolConfig, }; + this.logger.info('Sending Bedrock Converse request'); + this.logger.debug('Bedrock Converse request:', converseCommandInput); bedrockResponse = await this.bedrockClient.send( new ConverseCommand(converseCommandInput) ); + this.logger.info( + `Received Bedrock Converse response, requestId=${bedrockResponse.$metadata.requestId}`, + bedrockResponse.usage + ); + this.logger.debug('Bedrock Converse response:', bedrockResponse); if (bedrockResponse.output?.message) { messages.push(bedrockResponse.output?.message); } @@ -194,7 +202,11 @@ export class BedrockConverseAdapter { ); } try { + this.logger.info(`Invoking tool ${tool.name}`); + this.logger.debug('Tool input:', toolUseBlock.toolUse.input); const toolResponse = await tool.execute(toolUseBlock.toolUse.input); + this.logger.info(`Received response from ${tool.name} tool`); + this.logger.debug(toolResponse); return { role: 'user', content: [ diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts index 8231b8daef9..d30be4d7dfb 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts @@ -47,9 +47,11 @@ void describe('Conversation turn executor', () => { const consoleErrorMock = mock.fn(); const consoleLogMock = mock.fn(); + const consoleDebugMock = mock.fn(); const consoleMock = { error: consoleErrorMock, log: consoleLogMock, + debug: consoleDebugMock, } as unknown as Console; await new ConversationTurnExecutor( @@ -100,9 +102,11 @@ void describe('Conversation turn executor', () => { const consoleErrorMock = mock.fn(); const consoleLogMock = mock.fn(); + const consoleDebugMock = mock.fn(); const consoleMock = { error: consoleErrorMock, log: consoleLogMock, + debug: consoleDebugMock, } as unknown as Console; await assert.rejects( @@ -164,9 +168,11 @@ void describe('Conversation turn executor', () => { const consoleErrorMock = mock.fn(); const consoleLogMock = mock.fn(); + const consoleDebugMock = mock.fn(); const consoleMock = { error: consoleErrorMock, log: consoleLogMock, + debug: consoleDebugMock, } as unknown as Console; await assert.rejects( diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts index 60613c6f28d..99e66c5f741 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts @@ -29,6 +29,7 @@ export class ConversationTurnExecutor { this.logger.log( `Handling conversation turn event, currentMessageId=${this.event.currentMessageId}, conversationId=${this.event.conversationId}` ); + this.logger.debug('Event received:', this.event); const assistantResponse = await this.bedrockConverseAdapter.askBedrock(); diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts index d3bb9accdce..4ec45030b03 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts @@ -23,18 +23,17 @@ export class ConversationTurnResponseSender { private readonly graphqlRequestExecutor = new GraphqlRequestExecutor( event.graphqlApiEndpoint, event.request.headers.authorization - ) + ), + private readonly logger = console ) {} sendResponse = async (message: ContentBlock[]) => { - const { query, variables } = this.createMutationRequest(message); + const responseMutationRequest = this.createMutationRequest(message); + this.logger.debug('Sending response mutation:', responseMutationRequest); await this.graphqlRequestExecutor.executeGraphql< MutationResponseInput, void - >({ - query, - variables, - }); + >(responseMutationRequest); }; private createMutationRequest = (content: ContentBlock[]) => { diff --git a/packages/ai-constructs/tsconfig.json b/packages/ai-constructs/tsconfig.json index c07fe67565c..fd5619c21a0 100644 --- a/packages/ai-constructs/tsconfig.json +++ b/packages/ai-constructs/tsconfig.json @@ -9,5 +9,10 @@ "outDir": "lib", "allowJs": true }, - "references": [{ "path": "../plugin-types" }] + "references": [ + { "path": "../backend-output-schemas" }, + { "path": "../platform-core" }, + { "path": "../plugin-types" }, + { "path": "../backend-output-storage" } + ] } diff --git a/packages/backend-ai/src/conversation/factory.test.ts b/packages/backend-ai/src/conversation/factory.test.ts index aa6f76fc7de..b3a239432a0 100644 --- a/packages/backend-ai/src/conversation/factory.test.ts +++ b/packages/backend-ai/src/conversation/factory.test.ts @@ -158,8 +158,8 @@ void describe('ConversationHandlerFactory', () => { }); factory.getInstance(getInstanceProps); const template = Template.fromStack(rootStack); - const outputValue = - template.findOutputs('definedFunctions').definedFunctions.Value; + const outputValue = template.findOutputs('definedConversationHandlers') + .definedConversationHandlers.Value; assert.deepStrictEqual(outputValue, { ['Fn::Join']: [ '', diff --git a/packages/backend-ai/src/conversation/factory.ts b/packages/backend-ai/src/conversation/factory.ts index 0a095204dec..ddbb925597a 100644 --- a/packages/backend-ai/src/conversation/factory.ts +++ b/packages/backend-ai/src/conversation/factory.ts @@ -1,7 +1,4 @@ -import { - FunctionOutput, - functionOutputKey, -} from '@aws-amplify/backend-output-schemas'; +import { AIConversationOutput } from '@aws-amplify/backend-output-schemas'; import { BackendOutputStorageStrategy, ConstructContainerEntryGenerator, @@ -25,7 +22,7 @@ class ConversationHandlerFunctionGenerator constructor( private readonly props: DefineConversationHandlerFunctionProps, - private readonly outputStorageStrategy: BackendOutputStorageStrategy + private readonly outputStorageStrategy: BackendOutputStorageStrategy ) {} generateContainerEntry = ({ scope }: GenerateContainerEntryProps) => { @@ -43,33 +40,15 @@ class ConversationHandlerFunctionGenerator region: model.region, }; }), + outputStorageStrategy: this.outputStorageStrategy, }; const conversationHandlerFunction = new ConversationHandlerFunction( scope, this.props.name, constructProps ); - this.storeOutput(this.outputStorageStrategy, conversationHandlerFunction); return conversationHandlerFunction; }; - - /** - * Append conversation handler to defined functions. - * Explicitly defined custom handler is customer's function and should be visible - * in the outputs. - */ - private storeOutput = ( - outputStorageStrategy: BackendOutputStorageStrategy, - conversationHandlerFunction: ConversationHandlerFunction - ): void => { - outputStorageStrategy.appendToBackendOutputList(functionOutputKey, { - version: '1', - payload: { - definedFunctions: - conversationHandlerFunction.resources.lambda.functionName, - }, - }); - }; } class ConversationHandlerFunctionFactory diff --git a/packages/backend-output-schemas/API.md b/packages/backend-output-schemas/API.md index 41fbd8abeda..6fd3f07d275 100644 --- a/packages/backend-output-schemas/API.md +++ b/packages/backend-output-schemas/API.md @@ -6,6 +6,12 @@ import { z } from 'zod'; +// @public (undocumented) +export type AIConversationOutput = z.infer; + +// @public +export const aiConversationOutputKey = "AWS::Amplify::AI::Conversation"; + // @public (undocumented) export type AuthOutput = z.infer; @@ -340,6 +346,26 @@ export const unifiedBackendOutputSchema: z.ZodObject<{ definedFunctions: string; }; }>]>>; + "AWS::Amplify::AI::Conversation": z.ZodOptional; + payload: z.ZodObject<{ + definedConversationHandlers: z.ZodString; + }, "strip", z.ZodTypeAny, { + definedConversationHandlers: string; + }, { + definedConversationHandlers: string; + }>; + }, "strip", z.ZodTypeAny, { + version: "1"; + payload: { + definedConversationHandlers: string; + }; + }, { + version: "1"; + payload: { + definedConversationHandlers: string; + }; + }>]>>; }, "strip", z.ZodTypeAny, { "AWS::Amplify::Platform"?: { version: "1"; @@ -405,6 +431,12 @@ export const unifiedBackendOutputSchema: z.ZodObject<{ definedFunctions: string; }; } | undefined; + "AWS::Amplify::AI::Conversation"?: { + version: "1"; + payload: { + definedConversationHandlers: string; + }; + } | undefined; }, { "AWS::Amplify::Platform"?: { version: "1"; @@ -470,8 +502,36 @@ export const unifiedBackendOutputSchema: z.ZodObject<{ definedFunctions: string; }; } | undefined; + "AWS::Amplify::AI::Conversation"?: { + version: "1"; + payload: { + definedConversationHandlers: string; + }; + } | undefined; }>; +// @public (undocumented) +export const versionedAIConversationOutputSchema: z.ZodDiscriminatedUnion<"version", [z.ZodObject<{ + version: z.ZodLiteral<"1">; + payload: z.ZodObject<{ + definedConversationHandlers: z.ZodString; + }, "strip", z.ZodTypeAny, { + definedConversationHandlers: string; + }, { + definedConversationHandlers: string; + }>; +}, "strip", z.ZodTypeAny, { + version: "1"; + payload: { + definedConversationHandlers: string; + }; +}, { + version: "1"; + payload: { + definedConversationHandlers: string; + }; +}>]>; + // @public (undocumented) export const versionedAuthOutputSchema: z.ZodDiscriminatedUnion<"version", [z.ZodObject<{ version: z.ZodLiteral<"1">; diff --git a/packages/backend-output-schemas/src/ai/conversation/index.ts b/packages/backend-output-schemas/src/ai/conversation/index.ts new file mode 100644 index 00000000000..29b0d413976 --- /dev/null +++ b/packages/backend-output-schemas/src/ai/conversation/index.ts @@ -0,0 +1,14 @@ +import { z } from 'zod'; +import { aiConversationOutputSchema as aiConversationOutputSchemaV1 } from './v1'; + +export const versionedAIConversationOutputSchema = z.discriminatedUnion( + 'version', + [ + aiConversationOutputSchemaV1, + // this is where additional function major version schemas would go + ] +); + +export type AIConversationOutput = z.infer< + typeof versionedAIConversationOutputSchema +>; diff --git a/packages/backend-output-schemas/src/ai/conversation/v1.ts b/packages/backend-output-schemas/src/ai/conversation/v1.ts new file mode 100644 index 00000000000..cb50b29cda1 --- /dev/null +++ b/packages/backend-output-schemas/src/ai/conversation/v1.ts @@ -0,0 +1,8 @@ +import { z } from 'zod'; + +export const aiConversationOutputSchema = z.object({ + version: z.literal('1'), + payload: z.object({ + definedConversationHandlers: z.string(), // JSON array as string + }), +}); diff --git a/packages/backend-output-schemas/src/index.ts b/packages/backend-output-schemas/src/index.ts index ec7cef02096..11bfb14cd2d 100644 --- a/packages/backend-output-schemas/src/index.ts +++ b/packages/backend-output-schemas/src/index.ts @@ -5,6 +5,7 @@ import { versionedStorageOutputSchema } from './storage/index.js'; import { versionedStackOutputSchema } from './stack/index.js'; import { versionedCustomOutputSchema } from './custom'; import { versionedFunctionOutputSchema } from './function/index.js'; +import { versionedAIConversationOutputSchema } from './ai/conversation/index.js'; /** * The auth, graphql and storage exports here are duplicated from the submodule exports in the package.json file @@ -84,6 +85,20 @@ export * from './function/index.js'; */ export const functionOutputKey = 'AWS::Amplify::Function'; +/** + * ---------- AI conversation exports ---------- + */ + +/** + * re-export the AI conversation output schema + */ +export * from './ai/conversation/index.js'; + +/** + * Expected key that AI conversation output is stored under + */ +export const aiConversationOutputKey = 'AWS::Amplify::AI::Conversation'; + /** * ---------- Unified exports ---------- */ @@ -99,6 +114,7 @@ export const unifiedBackendOutputSchema = z.object({ [storageOutputKey]: versionedStorageOutputSchema.optional(), [customOutputKey]: versionedCustomOutputSchema.optional(), [functionOutputKey]: versionedFunctionOutputSchema.optional(), + [aiConversationOutputKey]: versionedAIConversationOutputSchema.optional(), }); /** * This type is a subset of the BackendOutput type that is exposed by the platform. diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index 2ca57e592f3..5f90daa9f87 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -26,13 +26,11 @@ "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/platform-core": "^1.0.6", "@aws-amplify/plugin-types": "^1.2.2", - "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "@aws-sdk/credential-providers": "^3.624.0", "@aws-sdk/types": "^3.609.0", - "@aws-sdk/util-arn-parser": "^3.568.0", "@parcel/watcher": "^2.4.1", "debounce-promise": "^3.1.2", "glob": "^10.2.7", diff --git a/packages/sandbox/src/lambda_function_log_streamer.test.ts b/packages/sandbox/src/lambda_function_log_streamer.test.ts index 7ba897fea28..6072e57adc5 100644 --- a/packages/sandbox/src/lambda_function_log_streamer.test.ts +++ b/packages/sandbox/src/lambda_function_log_streamer.test.ts @@ -3,21 +3,16 @@ import { LambdaFunctionLogStreamer } from './lambda_function_log_streamer.js'; import assert from 'node:assert'; import { BackendOutputClient } from '@aws-amplify/deployed-backend-client'; -import { - CloudFormationClient, - DescribeStacksOutput, -} from '@aws-sdk/client-cloudformation'; import { CloudWatchLogsClient } from '@aws-sdk/client-cloudwatch-logs'; import { + GetFunctionCommand, + GetFunctionCommandOutput, LambdaClient, - ListTagsCommand, - ListTagsCommandOutput, } from '@aws-sdk/client-lambda'; import { CloudWatchLogEventMonitor } from './cloudwatch_logs_monitor.js'; import { Printer } from '@aws-amplify/cli-core'; import { BackendIdentifier, BackendOutput } from '@aws-amplify/plugin-types'; import { TagName } from '@aws-amplify/platform-core'; -import { parse as parseArn } from '@aws-sdk/util-arn-parser'; void describe('LambdaFunctionLogStreamer', () => { const region = 'test-region'; @@ -25,20 +20,10 @@ void describe('LambdaFunctionLogStreamer', () => { 'func1FullName', 'func2FullName', ]); - - // CFN default implementation - const cfnClientMock = new CloudFormationClient({ region }); - const cfnClientSendMock = mock.fn(() => { - return Promise.resolve({ - Stacks: [ - { - StackId: - 'arn:aws:cloudformation:us-west-2:123456789012:stack/stack-name/uuid', - }, - ], - } as DescribeStacksOutput); - }); - mock.method(cfnClientMock, 'send', cfnClientSendMock); + const definedConversationHandlers = JSON.stringify([ + 'conversationHandler1FullName', + 'conversationHandler2FullName', + ]); // CW default implementation const cloudWatchClientMock = new CloudWatchLogsClient({ region }); @@ -48,15 +33,26 @@ void describe('LambdaFunctionLogStreamer', () => { // Lambda default implementation. // Given a resource Arn with lambda function name with `FullName` suffix, this will return the function name with `friendlyName` as suffix const lambdaClientMock = new LambdaClient({ region }); - const lambdaClientSendMock = mock.fn((listTagsCommand: ListTagsCommand) => { - return Promise.resolve({ - Tags: { - [TagName.FRIENDLY_NAME]: parseArn(listTagsCommand.input.Resource ?? '') - .resource?.split(':')[1] - .replace('FullName', 'FriendlyName'), - } as unknown as ListTagsCommandOutput, - }); - }); + const lambdaClientSendMock = mock.fn( + (getFunctionCommand: GetFunctionCommand) => { + return Promise.resolve({ + Configuration: { + LoggingConfig: { + LogGroup: `/aws/lambda/${ + getFunctionCommand.input.FunctionName ?? '' + }`, + }, + }, + Tags: { + [TagName.FRIENDLY_NAME]: + getFunctionCommand.input.FunctionName?.replace( + 'FullName', + 'FriendlyName' + ), + } as unknown as GetFunctionCommandOutput, + }); + } + ); mock.method(lambdaClientMock, 'send', lambdaClientSendMock); // backendOutputClient default implementation @@ -74,6 +70,12 @@ void describe('LambdaFunctionLogStreamer', () => { }, version: '1', }, + ['AWS::Amplify::AI::Conversation']: { + payload: { + definedConversationHandlers: definedConversationHandlers, + }, + version: '1', + }, } as BackendOutput); }), }; @@ -91,14 +93,12 @@ void describe('LambdaFunctionLogStreamer', () => { const classUnderTest = new LambdaFunctionLogStreamer( lambdaClientMock, - cfnClientMock, cloudWatchLogMonitorMock as unknown as CloudWatchLogEventMonitor, backendOutputClientMock as unknown as BackendOutputClient, printer as unknown as Printer ); beforeEach(() => { - cfnClientSendMock.mock.resetCalls(); cloudWatchClientSendMock.mock.resetCalls(); lambdaClientSendMock.mock.resetCalls(); backendOutputClientMock.getOutput.mock.resetCalls(); @@ -147,26 +147,34 @@ void describe('LambdaFunctionLogStreamer', () => { assert.strictEqual(lambdaClientSendMock.mock.callCount(), 0); }); - void it('calls logs monitor with all the customer defined functions if no function name filter is provided', async () => { + void it('calls logs monitor with all the customer defined functions and conversation handlers if no function name filter is provided', async () => { await classUnderTest.startStreamingLogs(testSandboxBackendId, { enabled: true, }); // assert that lambda calls to retrieve tags were with the right function arn - assert.strictEqual(lambdaClientSendMock.mock.callCount(), 2); + assert.strictEqual(lambdaClientSendMock.mock.callCount(), 4); assert.strictEqual( - lambdaClientSendMock.mock.calls[0].arguments[0].input.Resource, - 'arn:aws:lambda:us-west-2:123456789012:function:func1FullName' + lambdaClientSendMock.mock.calls[0].arguments[0].input.FunctionName, + 'func1FullName' ); assert.strictEqual( - lambdaClientSendMock.mock.calls[1].arguments[0].input.Resource, - 'arn:aws:lambda:us-west-2:123456789012:function:func2FullName' + lambdaClientSendMock.mock.calls[1].arguments[0].input.FunctionName, + 'func2FullName' + ); + assert.strictEqual( + lambdaClientSendMock.mock.calls[2].arguments[0].input.FunctionName, + 'conversationHandler1FullName' + ); + assert.strictEqual( + lambdaClientSendMock.mock.calls[3].arguments[0].input.FunctionName, + 'conversationHandler2FullName' ); // assert that logs groups were added to the monitor and was then called activate assert.strictEqual( cloudWatchLogMonitorMock.addLogGroups.mock.callCount(), - 2 + 4 ); assert.strictEqual( cloudWatchLogMonitorMock.addLogGroups.mock.calls[0].arguments[0], @@ -184,6 +192,22 @@ void describe('LambdaFunctionLogStreamer', () => { cloudWatchLogMonitorMock.addLogGroups.mock.calls[1].arguments[1], '/aws/lambda/func2FullName' ); + assert.strictEqual( + cloudWatchLogMonitorMock.addLogGroups.mock.calls[2].arguments[0], + 'conversationHandler1FriendlyName' + ); + assert.strictEqual( + cloudWatchLogMonitorMock.addLogGroups.mock.calls[2].arguments[1], + '/aws/lambda/conversationHandler1FullName' + ); + assert.strictEqual( + cloudWatchLogMonitorMock.addLogGroups.mock.calls[3].arguments[0], + 'conversationHandler2FriendlyName' + ); + assert.strictEqual( + cloudWatchLogMonitorMock.addLogGroups.mock.calls[3].arguments[1], + '/aws/lambda/conversationHandler2FullName' + ); assert.strictEqual(cloudWatchLogMonitorMock.activate.mock.callCount(), 1); }); @@ -197,14 +221,22 @@ void describe('LambdaFunctionLogStreamer', () => { // assert that lambda calls to retrieve tags were with the right function arn // We do it for all customer defined functions, filtering happens after - assert.strictEqual(lambdaClientSendMock.mock.callCount(), 2); + assert.strictEqual(lambdaClientSendMock.mock.callCount(), 4); + assert.strictEqual( + lambdaClientSendMock.mock.calls[0].arguments[0].input.FunctionName, + 'func1FullName' + ); assert.strictEqual( - lambdaClientSendMock.mock.calls[0].arguments[0].input.Resource, - 'arn:aws:lambda:us-west-2:123456789012:function:func1FullName' + lambdaClientSendMock.mock.calls[1].arguments[0].input.FunctionName, + 'func2FullName' ); assert.strictEqual( - lambdaClientSendMock.mock.calls[1].arguments[0].input.Resource, - 'arn:aws:lambda:us-west-2:123456789012:function:func2FullName' + lambdaClientSendMock.mock.calls[2].arguments[0].input.FunctionName, + 'conversationHandler1FullName' + ); + assert.strictEqual( + lambdaClientSendMock.mock.calls[3].arguments[0].input.FunctionName, + 'conversationHandler2FullName' ); // assert that logs groups were added to the monitor for only filtered functions and was then called activate @@ -231,14 +263,22 @@ void describe('LambdaFunctionLogStreamer', () => { // assert that lambda calls to retrieve tags were with the right function arn // We do it for all customer defined functions, filtering happens after - assert.strictEqual(lambdaClientSendMock.mock.callCount(), 2); + assert.strictEqual(lambdaClientSendMock.mock.callCount(), 4); + assert.strictEqual( + lambdaClientSendMock.mock.calls[0].arguments[0].input.FunctionName, + 'func1FullName' + ); + assert.strictEqual( + lambdaClientSendMock.mock.calls[1].arguments[0].input.FunctionName, + 'func2FullName' + ); assert.strictEqual( - lambdaClientSendMock.mock.calls[0].arguments[0].input.Resource, - 'arn:aws:lambda:us-west-2:123456789012:function:func1FullName' + lambdaClientSendMock.mock.calls[2].arguments[0].input.FunctionName, + 'conversationHandler1FullName' ); assert.strictEqual( - lambdaClientSendMock.mock.calls[1].arguments[0].input.Resource, - 'arn:aws:lambda:us-west-2:123456789012:function:func2FullName' + lambdaClientSendMock.mock.calls[3].arguments[0].input.FunctionName, + 'conversationHandler2FullName' ); // assert that logs groups were added to the monitor for only filtered functions and was then called activate @@ -273,14 +313,22 @@ void describe('LambdaFunctionLogStreamer', () => { // assert that lambda calls to retrieve tags were with the right function arn // We do it for all customer defined functions, filtering happens after - assert.strictEqual(lambdaClientSendMock.mock.callCount(), 2); + assert.strictEqual(lambdaClientSendMock.mock.callCount(), 4); + assert.strictEqual( + lambdaClientSendMock.mock.calls[0].arguments[0].input.FunctionName, + 'func1FullName' + ); + assert.strictEqual( + lambdaClientSendMock.mock.calls[1].arguments[0].input.FunctionName, + 'func2FullName' + ); assert.strictEqual( - lambdaClientSendMock.mock.calls[0].arguments[0].input.Resource, - 'arn:aws:lambda:us-west-2:123456789012:function:func1FullName' + lambdaClientSendMock.mock.calls[2].arguments[0].input.FunctionName, + 'conversationHandler1FullName' ); assert.strictEqual( - lambdaClientSendMock.mock.calls[1].arguments[0].input.Resource, - 'arn:aws:lambda:us-west-2:123456789012:function:func2FullName' + lambdaClientSendMock.mock.calls[3].arguments[0].input.FunctionName, + 'conversationHandler2FullName' ); // assert that no logs groups were added to the monitor diff --git a/packages/sandbox/src/lambda_function_log_streamer.ts b/packages/sandbox/src/lambda_function_log_streamer.ts index 74688495963..ee3ac62019d 100644 --- a/packages/sandbox/src/lambda_function_log_streamer.ts +++ b/packages/sandbox/src/lambda_function_log_streamer.ts @@ -1,17 +1,10 @@ import { LogLevel, Printer } from '@aws-amplify/cli-core'; import { BackendOutputClient } from '@aws-amplify/deployed-backend-client'; -import { - BackendIdentifierConversions, - TagName, -} from '@aws-amplify/platform-core'; +import { TagName } from '@aws-amplify/platform-core'; import { BackendIdentifier, BackendOutput } from '@aws-amplify/plugin-types'; -import { - CloudFormationClient, - DescribeStacksCommand, -} from '@aws-sdk/client-cloudformation'; -import { LambdaClient, ListTagsCommand } from '@aws-sdk/client-lambda'; + +import { GetFunctionCommand, LambdaClient } from '@aws-sdk/client-lambda'; import { CloudWatchLogEventMonitor } from './cloudwatch_logs_monitor.js'; -import { build as buildArn, parse as parseArn } from '@aws-sdk/util-arn-parser'; import { SandboxFunctionStreamingOptions } from './sandbox.js'; /** @@ -24,7 +17,6 @@ export class LambdaFunctionLogStreamer { */ constructor( private readonly lambda: LambdaClient, - private readonly cfnClient: CloudFormationClient, private readonly logsMonitor: CloudWatchLogEventMonitor, private readonly backendOutputClient: BackendOutputClient, private readonly printer: Printer @@ -50,37 +42,38 @@ export class LambdaFunctionLogStreamer { const definedFunctionsPayload = backendOutput['AWS::Amplify::Function']?.payload.definedFunctions; + const definedConversationHandlersPayload = + backendOutput['AWS::Amplify::AI::Conversation']?.payload + .definedConversationHandlers; const deployedFunctionNames = definedFunctionsPayload ? (JSON.parse(definedFunctionsPayload) as string[]) : []; - - // To use list-tags API we need to convert function name to function Arn since it only accepts ARN as input - const deployedFunctionNameToArnMap = await this.getFunctionArnFromNames( - sandboxBackendId, - deployedFunctionNames + deployedFunctionNames.push( + ...(definedConversationHandlersPayload + ? (JSON.parse(definedConversationHandlersPayload) as string[]) + : []) ); - if (!deployedFunctionNameToArnMap) { - this.printer.log( - `[Sandbox] Could not find any function in stack ${BackendIdentifierConversions.toStackName( - sandboxBackendId - )}. Streaming function logs will be turned off.`, - LogLevel.DEBUG - ); - return; - } - - for (const entry of deployedFunctionNameToArnMap) { - const listTagsResponse = await this.lambda.send( - new ListTagsCommand({ - Resource: entry.arn, + for (const functionName of deployedFunctionNames) { + const getFunctionResponse = await this.lambda.send( + new GetFunctionCommand({ + FunctionName: functionName, }) ); + const logGroupName = + getFunctionResponse.Configuration?.LoggingConfig?.LogGroup; + if (!logGroupName) { + this.printer.log( + `[Sandbox] Could not find logGroup for lambda function ${functionName}. Logs will not be streamed for this function.`, + LogLevel.DEBUG + ); + continue; + } const friendlyFunctionName = - listTagsResponse.Tags?.[TagName.FRIENDLY_NAME]; + getFunctionResponse.Tags?.[TagName.FRIENDLY_NAME]; if (!friendlyFunctionName) { this.printer.log( - `[Sandbox] Could not find user defined name for lambda function ${entry.name}. Logs will not be streamed for this function.`, + `[Sandbox] Could not find user defined name for lambda function ${functionName}. Logs will not be streamed for this function.`, LogLevel.DEBUG ); continue; @@ -109,11 +102,7 @@ export class LambdaFunctionLogStreamer { } if (shouldStreamLogs) { - this.logsMonitor?.addLogGroups( - friendlyFunctionName, - // a CW log group is implicitly created for each lambda function with the lambda function's name - `/aws/lambda/${entry.name}` - ); + this.logsMonitor?.addLogGroups(friendlyFunctionName, logGroupName); } else { this.printer.log( `[Sandbox] Skipping logs streaming for function ${friendlyFunctionName} since it did not match any filters. To stream logs for this function, ensure at least one of your logs-filters match this function name.`, @@ -136,50 +125,4 @@ export class LambdaFunctionLogStreamer { ); this.logsMonitor?.pause(); }; - - /** - * Adds functionArn for each function name provided. All the ARN components are taken from the root stack Arn - * @param sandboxBackendId backendId for retrieving the root stack - * @param functionNames Name of the functions for which ARN needs to be generated - * @returns An object containing function name and ARN for each function name provided - */ - private getFunctionArnFromNames = async ( - sandboxBackendId: BackendIdentifier, - functionNames?: string[] - ) => { - if (!functionNames || functionNames.length === 0) { - return; - } - - const rootStackResources = await this.cfnClient.send( - new DescribeStacksCommand({ - StackName: BackendIdentifierConversions.toStackName(sandboxBackendId), - }) - ); - - if (!rootStackResources?.Stacks?.[0]?.StackId) { - this.printer.log( - `[Sandbox] Cannot load root stack for Id ${BackendIdentifierConversions.toStackName( - sandboxBackendId - )}. Streaming function logs will be turned off.`, - LogLevel.DEBUG - ); - return; - } - - const arnParts = parseArn(rootStackResources.Stacks[0].StackId); - - return functionNames.map((name) => { - return { - name, - arn: buildArn({ - resource: `function:${name}`, - service: 'lambda', - accountId: arnParts.accountId, - partition: arnParts.partition, - region: arnParts.region, - }), - }; - }); - }; } diff --git a/packages/sandbox/src/sandbox_singleton_factory.ts b/packages/sandbox/src/sandbox_singleton_factory.ts index b4d872ab272..f1f36344e87 100644 --- a/packages/sandbox/src/sandbox_singleton_factory.ts +++ b/packages/sandbox/src/sandbox_singleton_factory.ts @@ -14,7 +14,6 @@ import { LambdaClient } from '@aws-sdk/client-lambda'; import { BackendOutputClientFactory } from '@aws-amplify/deployed-backend-client'; import { LambdaFunctionLogStreamer } from './lambda_function_log_streamer.js'; import { CloudWatchLogEventMonitor } from './cloudwatch_logs_monitor.js'; -import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; /** * Factory to create a new sandbox @@ -41,7 +40,6 @@ export class SandboxSingletonFactory { packageManagerControllerFactory.getPackageManagerController(), this.format ); - const cfnClient = new CloudFormationClient(); this.instance = new FileWatchingSandbox( this.sandboxIdResolver, new AmplifySandboxExecutor( @@ -52,7 +50,6 @@ export class SandboxSingletonFactory { new SSMClient(), new LambdaFunctionLogStreamer( new LambdaClient(), - cfnClient, new CloudWatchLogEventMonitor(new CloudWatchLogsClient()), BackendOutputClientFactory.getInstance(), this.printer From c6ba1757607da657dc35a575465678c555e836b1 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Fri, 4 Oct 2024 10:19:25 -0700 Subject: [PATCH 029/199] Exclude FromJSONSchema from api checks due to its complexity (#2079) --- scripts/check_api_changes.ts | 15 ++++++++++++++- .../api_changes_validator.test.ts | 2 ++ .../api_changes_validator.ts | 4 +++- .../api_usage_generator.test.ts | 12 +++++++++++- .../api-changes-validator/api_usage_generator.ts | 6 ++++-- .../api_usage_statements_generators.ts | 6 +++++- .../without-breaks/project-without-breaks/API.md | 5 +++++ .../project-without-breaks/src/index.ts | 5 +++++ 8 files changed, 49 insertions(+), 6 deletions(-) diff --git a/scripts/check_api_changes.ts b/scripts/check_api_changes.ts index e80682f4fdb..b574e8d23ca 100644 --- a/scripts/check_api_changes.ts +++ b/scripts/check_api_changes.ts @@ -47,6 +47,18 @@ console.log( const packagePaths = await glob(`${latestRepositoryPath}/packages/*`); +const excludedTypesByPackageName: Record> = { + 'ai-constructs': [ + // FromJSONSchema is complex enough to trigger + // index.ts(113,9): error TS2589: Type instantiation is excessively deep and possibly infinite. + // index.ts(113,87): error TS2589: Type instantiation is excessively deep and possibly infinite. + // index.ts(113,87): error TS2590: Expression produces a union type that is too complex to represent. + // See https://github.com/ThomasAribart/json-schema-to-ts/blob/main/documentation/FAQs/i-get-a-type-instantiation-is-excessively-deep-and-potentially-infinite-error-what-should-i-do.md. + // Therefore, excluding this type from checks. + 'FromJSONSchema', + ], +}; + const validationResults = await Promise.allSettled( packagePaths.map(async (packagePath) => { const packageName = path.basename(packagePath); @@ -70,7 +82,8 @@ const validationResults = await Promise.allSettled( await new ApiChangesValidator( packagePath, baselinePackageApiReportPath, - workingDirectory + workingDirectory, + excludedTypesByPackageName[packageName] ).validate(); console.log(`Validation of ${packageName} completed successfully`); }) diff --git a/scripts/components/api-changes-validator/api_changes_validator.test.ts b/scripts/components/api-changes-validator/api_changes_validator.test.ts index 1e8e02f40a4..f6aa9e19c30 100644 --- a/scripts/components/api-changes-validator/api_changes_validator.test.ts +++ b/scripts/components/api-changes-validator/api_changes_validator.test.ts @@ -61,6 +61,7 @@ void describe('Api changes validator', { concurrency: true }, () => { latestPackagePath, baselinePackageApiReportPath, workingDirectory, + [], 'npmLocalLink' ); @@ -92,6 +93,7 @@ void describe('Api changes validator', { concurrency: true }, () => { latestPackagePath, baselinePackageApiReportPath, workingDirectory, + ['SampleIgnoredType'], 'npmLocalLink' ); diff --git a/scripts/components/api-changes-validator/api_changes_validator.ts b/scripts/components/api-changes-validator/api_changes_validator.ts index e024aeec5aa..9fc94d6031a 100644 --- a/scripts/components/api-changes-validator/api_changes_validator.ts +++ b/scripts/components/api-changes-validator/api_changes_validator.ts @@ -30,6 +30,7 @@ export class ApiChangesValidator { private readonly latestPackagePath: string, private readonly baselinePackageApiReportPath: string, private readonly workingDirectory: string, + private readonly excludedTypes: Array = [], private readonly latestPackageDependencyDeclarationStrategy: | 'npmRegistry' | 'npmLocalLink' = 'npmRegistry' @@ -103,7 +104,8 @@ export class ApiChangesValidator { const apiReportAST = ApiReportParser.parse(apiReportContent); const usage = new ApiUsageGenerator( latestPackageJson.name, - apiReportAST + apiReportAST, + this.excludedTypes ).generate(); await fsp.writeFile(path.join(this.testProjectPath, 'index.ts'), usage); await execa('npm', ['install'], { cwd: this.testProjectPath }); diff --git a/scripts/components/api-changes-validator/api_usage_generator.test.ts b/scripts/components/api-changes-validator/api_usage_generator.test.ts index b4e001010d2..de70ba1a81d 100644 --- a/scripts/components/api-changes-validator/api_usage_generator.test.ts +++ b/scripts/components/api-changes-validator/api_usage_generator.test.ts @@ -337,6 +337,15 @@ const someTypeUnderSubNamespaceUsageFunction = (someTypeUnderSubNamespaceFunctio } `, }, + { + description: 'Skips ignored type', + apiReportCode: ` +export type SampleIgnoredType = { + someProperty: string; +} + `, + expectedApiUsage: '', + }, ]; const nestInMarkdownCodeBlock = (apiReportCode: string) => { @@ -351,7 +360,8 @@ void describe('Api usage generator', () => { ); const apiUsage = new ApiUsageGenerator( 'samplePackageName', - apiReportAST + apiReportAST, + ['SampleIgnoredType'] ).generate(); assert.strictEqual( // .replace() removes EOL differences between Windows and other OS so output matches for all diff --git a/scripts/components/api-changes-validator/api_usage_generator.ts b/scripts/components/api-changes-validator/api_usage_generator.ts index 3e69d7c7bfa..ffb9b207831 100644 --- a/scripts/components/api-changes-validator/api_usage_generator.ts +++ b/scripts/components/api-changes-validator/api_usage_generator.ts @@ -25,7 +25,8 @@ export class ApiUsageGenerator { */ constructor( private readonly packageName: string, - private readonly apiReportAST: ts.SourceFile + private readonly apiReportAST: ts.SourceFile, + private readonly excludedTypes: Array ) { this.namespaceDefinitions = this.getNamespaceDefinitions(); } @@ -65,7 +66,8 @@ export class ApiUsageGenerator { case ts.SyntaxKind.TypeAliasDeclaration: return new TypeUsageStatementsGenerator( node as ts.TypeAliasDeclaration, - this.packageName + this.packageName, + this.excludedTypes ).generate(); case ts.SyntaxKind.EnumDeclaration: return new EnumUsageStatementsGenerator( diff --git a/scripts/components/api-changes-validator/api_usage_statements_generators.ts b/scripts/components/api-changes-validator/api_usage_statements_generators.ts index ad6088f0987..3249c91d826 100644 --- a/scripts/components/api-changes-validator/api_usage_statements_generators.ts +++ b/scripts/components/api-changes-validator/api_usage_statements_generators.ts @@ -122,10 +122,14 @@ export class TypeUsageStatementsGenerator implements UsageStatementsGenerator { */ constructor( private readonly typeAliasDeclaration: ts.TypeAliasDeclaration, - private readonly packageName: string + private readonly packageName: string, + private readonly excludedTypes: Array ) {} generate = (): UsageStatementsGeneratorOutput => { const typeName = this.typeAliasDeclaration.name.getText(); + if (this.excludedTypes.includes(typeName)) { + return {}; + } const constName = toLowerCamelCase(typeName); const genericTypeParametersDeclaration = new GenericTypeParameterDeclarationUsageStatementsGenerator( diff --git a/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/API.md b/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/API.md index b84fb63b5a0..410b6690e22 100644 --- a/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/API.md +++ b/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/API.md @@ -79,4 +79,9 @@ export type SampleTypeUsingClass = { export type SampleTypeThatReferencesFunction = { sampleProperty: T; }; + +// This type is intentionally different from what's in sources +export type SampleIgnoredType = { + someProperty: string; +}; ``` diff --git a/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/src/index.ts b/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/src/index.ts index 4de03c28f05..a6f2201d71e 100644 --- a/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/src/index.ts +++ b/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/src/index.ts @@ -110,3 +110,8 @@ export type SampleTypeUsingClass = { export type SampleTypeThatReferencesFunction = { sampleProperty: T; }; + +// This type is intentionally different from what's in api report +export type SampleIgnoredType = { + someProperty: number; +}; From 45fe8b764187f27fa403757cff79edea4910782a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 11:39:15 -0700 Subject: [PATCH 030/199] Version Packages (#2077) Co-authored-by: github-actions[bot] --- .changeset/eleven-mails-scream.md | 6 ------ .changeset/eleven-snails-move.md | 9 --------- packages/ai-constructs/CHANGELOG.md | 12 ++++++++++++ packages/ai-constructs/package.json | 4 ++-- packages/backend-ai/CHANGELOG.md | 14 ++++++++++++++ packages/backend-ai/package.json | 6 +++--- packages/backend-output-schemas/CHANGELOG.md | 6 ++++++ packages/backend-output-schemas/package.json | 2 +- packages/backend/CHANGELOG.md | 8 ++++++++ packages/backend/package.json | 4 ++-- packages/integration-tests/package.json | 6 +++--- packages/sandbox/CHANGELOG.md | 6 ++++++ packages/sandbox/package.json | 2 +- 13 files changed, 58 insertions(+), 27 deletions(-) delete mode 100644 .changeset/eleven-mails-scream.md delete mode 100644 .changeset/eleven-snails-move.md diff --git a/.changeset/eleven-mails-scream.md b/.changeset/eleven-mails-scream.md deleted file mode 100644 index 590877bfccb..00000000000 --- a/.changeset/eleven-mails-scream.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/ai-constructs': minor -'@aws-amplify/backend-ai': minor ---- - -Infer executable tool input type from input schema diff --git a/.changeset/eleven-snails-move.md b/.changeset/eleven-snails-move.md deleted file mode 100644 index 1ee858b3764..00000000000 --- a/.changeset/eleven-snails-move.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@aws-amplify/ai-constructs': minor -'@aws-amplify/backend-ai': minor -'@aws-amplify/backend-output-schemas': minor -'@aws-amplify/backend': patch -'@aws-amplify/sandbox': patch ---- - -Stream conversation logs in sandbox diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index afddd6721fc..5eac972378a 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/ai-constructs +## 0.3.0 + +### Minor Changes + +- 300a72d: Infer executable tool input type from input schema +- 0a5e51c: Stream conversation logs in sandbox + +### Patch Changes + +- Updated dependencies [0a5e51c] + - @aws-amplify/backend-output-schemas@1.3.0 + ## 0.2.0 ### Minor Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index e4d3fede0f3..783ec587e67 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "0.2.0", + "version": "0.3.0", "type": "commonjs", "publishConfig": { "access": "public" @@ -26,7 +26,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.2.1", + "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.0.1", "@aws-sdk/client-bedrock-runtime": "^3.622.0", diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index ab8d66df297..2e083216a47 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,19 @@ # @aws-amplify/backend-ai +## 0.2.0 + +### Minor Changes + +- 300a72d: Infer executable tool input type from input schema +- 0a5e51c: Stream conversation logs in sandbox + +### Patch Changes + +- Updated dependencies [300a72d] +- Updated dependencies [0a5e51c] + - @aws-amplify/ai-constructs@0.3.0 + - @aws-amplify/backend-output-schemas@1.3.0 + ## 0.1.2 ### Patch Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 20e47e789a8..e49d80c0b5c 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "0.1.2", + "version": "0.2.0", "type": "module", "publishConfig": { "access": "public" @@ -22,8 +22,8 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.2.0", - "@aws-amplify/backend-output-schemas": "^1.2.1", + "@aws-amplify/ai-constructs": "^0.3.0", + "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.0.2", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.0.1" diff --git a/packages/backend-output-schemas/CHANGELOG.md b/packages/backend-output-schemas/CHANGELOG.md index 1b23c4492bf..a50379c41e0 100644 --- a/packages/backend-output-schemas/CHANGELOG.md +++ b/packages/backend-output-schemas/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/backend-output-schemas +## 1.3.0 + +### Minor Changes + +- 0a5e51c: Stream conversation logs in sandbox + ## 1.2.1 ### Patch Changes diff --git a/packages/backend-output-schemas/package.json b/packages/backend-output-schemas/package.json index 935afd17758..1f1a70be4bc 100644 --- a/packages/backend-output-schemas/package.json +++ b/packages/backend-output-schemas/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-output-schemas", - "version": "1.2.1", + "version": "1.3.0", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index aedc9875027..04cf417613d 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend +## 1.3.2 + +### Patch Changes + +- 0a5e51c: Stream conversation logs in sandbox +- Updated dependencies [0a5e51c] + - @aws-amplify/backend-output-schemas@1.3.0 + ## 1.3.1 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index cc14951bbc0..04aec49b7ee 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.3.1", + "version": "1.3.2", "type": "module", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "@aws-amplify/backend-auth": "^1.2.0", "@aws-amplify/backend-function": "^1.5.0", "@aws-amplify/backend-data": "^1.1.4", - "@aws-amplify/backend-output-schemas": "^1.2.1", + "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/backend-storage": "^1.2.1", diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 3f3b56c6d27..089995d6961 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -5,10 +5,10 @@ "type": "module", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.2.0", + "@aws-amplify/ai-constructs": "^0.3.0", "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend": "^1.3.1", - "@aws-amplify/backend-ai": "^0.1.2", + "@aws-amplify/backend": "^1.3.2", + "@aws-amplify/backend-ai": "^0.2.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/data-schema": "^1.0.0", diff --git a/packages/sandbox/CHANGELOG.md b/packages/sandbox/CHANGELOG.md index 6a8608ba710..8ba88ce39b3 100644 --- a/packages/sandbox/CHANGELOG.md +++ b/packages/sandbox/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/sandbox +## 1.2.3 + +### Patch Changes + +- 0a5e51c: Stream conversation logs in sandbox + ## 1.2.2 ### Patch Changes diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index 5f90daa9f87..b5c04aa0c8c 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/sandbox", - "version": "1.2.2", + "version": "1.2.3", "type": "module", "publishConfig": { "access": "public" From 6e4a62f1d0fe98e4a96cdc44c6d6b40cd9269397 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 7 Oct 2024 11:10:59 -0700 Subject: [PATCH 031/199] Fix case when multiple tools are requested by Bedrock. (#2083) * Limit tool usage in conversation turn * Limit tool usage in conversation turn --- .changeset/tender-camels-agree.md | 5 ++ .../runtime/bedrock_converse_adapter.test.ts | 43 +++++++++++++- .../runtime/bedrock_converse_adapter.ts | 56 ++++++++----------- .../conversation_handler_project.ts | 20 ++++++- .../conversation-handler/amplify/constants.ts | 6 +- .../custom_handler.ts | 7 ++- 6 files changed, 95 insertions(+), 42 deletions(-) create mode 100644 .changeset/tender-camels-agree.md diff --git a/.changeset/tender-camels-agree.md b/.changeset/tender-camels-agree.md new file mode 100644 index 00000000000..1a85db90560 --- /dev/null +++ b/.changeset/tender-camels-agree.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': patch +--- + +Fix multi tool usage in single turn. diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts index 81330612f44..cb07d4fe33a 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts @@ -174,7 +174,14 @@ void describe('Bedrock converse adapter', () => { toolUse: { toolUseId: randomUUID().toString(), name: additionalTool.name, - input: 'additionalToolInput', + input: 'additionalToolInput1', + }, + }, + { + toolUse: { + toolUseId: randomUUID().toString(), + name: additionalTool.name, + input: 'additionalToolInput2', }, }, ], @@ -195,7 +202,14 @@ void describe('Bedrock converse adapter', () => { toolUse: { toolUseId: randomUUID().toString(), name: eventTool.name, - input: 'eventToolToolInput', + input: 'eventToolToolInput1', + }, + }, + { + toolUse: { + toolUseId: randomUUID().toString(), + name: eventTool.name, + input: 'eventToolToolInput2', }, }, ], @@ -289,6 +303,10 @@ void describe('Bedrock converse adapter', () => { additionalToolUseBedrockResponse.output?.message?.content[0].toolUse ?.toolUseId ); + assert.ok( + additionalToolUseBedrockResponse.output?.message?.content[1].toolUse + ?.toolUseId + ); const expectedBedrockInput2: ConverseCommandInput = { messages: [ ...(messages as Array), @@ -305,6 +323,15 @@ void describe('Bedrock converse adapter', () => { .toolUse.toolUseId, }, }, + { + toolResult: { + content: [additionalToolOutput], + status: 'success', + toolUseId: + additionalToolUseBedrockResponse.output?.message.content[1] + .toolUse.toolUseId, + }, + }, ], }, ], @@ -317,6 +344,9 @@ void describe('Bedrock converse adapter', () => { assert.ok( eventToolUseBedrockResponse.output?.message?.content[0].toolUse?.toolUseId ); + assert.ok( + eventToolUseBedrockResponse.output?.message?.content[1].toolUse?.toolUseId + ); assert.ok(expectedBedrockInput2.messages); const expectedBedrockInput3: ConverseCommandInput = { messages: [ @@ -334,6 +364,15 @@ void describe('Bedrock converse adapter', () => { .toolUseId, }, }, + { + toolResult: { + content: [eventToolOutput], + status: 'success', + toolUseId: + eventToolUseBedrockResponse.output?.message.content[1].toolUse + .toolUseId, + }, + }, ], }, ], diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts index 721c9e09d6a..8e4001327f6 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts @@ -121,12 +121,17 @@ export class BedrockConverseAdapter { // and propagate result back to client. return clientToolUseBlocks; } + const toolResponseContentBlocks: Array = []; for (const responseContentBlock of toolUseBlocks) { const toolUseBlock = responseContentBlock as ContentBlock.ToolUseMember; - const toolMessage = await this.executeTool(toolUseBlock); - messages.push(toolMessage); + const toolResultContentBlock = await this.executeTool(toolUseBlock); + toolResponseContentBlocks.push(toolResultContentBlock); } + messages.push({ + role: 'user', + content: toolResponseContentBlocks, + }); } } while (bedrockResponse.stopReason === 'tool_use'); @@ -191,7 +196,7 @@ export class BedrockConverseAdapter { private executeTool = async ( toolUseBlock: ContentBlock.ToolUseMember - ): Promise => { + ): Promise => { if (!toolUseBlock.toolUse.name) { throw Error('Bedrock tool use response is missing a tool name'); } @@ -208,43 +213,28 @@ export class BedrockConverseAdapter { this.logger.info(`Received response from ${tool.name} tool`); this.logger.debug(toolResponse); return { - role: 'user', - content: [ - { - toolResult: { - toolUseId: toolUseBlock.toolUse.toolUseId, - content: [toolResponse], - status: 'success', - }, - }, - ], + toolResult: { + toolUseId: toolUseBlock.toolUse.toolUseId, + content: [toolResponse], + status: 'success', + }, }; } catch (e) { if (e instanceof Error) { return { - role: 'user', - content: [ - { - toolResult: { - toolUseId: toolUseBlock.toolUse.toolUseId, - content: [{ text: e.toString() }], - status: 'error', - }, - }, - ], + toolResult: { + toolUseId: toolUseBlock.toolUse.toolUseId, + content: [{ text: e.toString() }], + status: 'error', + }, }; } return { - role: 'user', - content: [ - { - toolResult: { - toolUseId: toolUseBlock.toolUse.toolUseId, - content: [{ text: 'unknown error occurred' }], - status: 'error', - }, - }, - ], + toolResult: { + toolUseId: toolUseBlock.toolUse.toolUseId, + content: [{ text: 'unknown error occurred' }], + status: 'error', + }, }; } }; diff --git a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts index 1ab7ab21754..47b51c4cb31 100644 --- a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts +++ b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts @@ -30,7 +30,7 @@ import { NormalizedCacheObject } from '@apollo/client'; import { bedrockModelId, expectedTemperatureInDataToolScenario, - expectedTemperatureInProgrammaticToolScenario, + expectedTemperaturesInProgrammaticToolScenario, } from '../test-projects/conversation-handler/amplify/constants.js'; import { resolve } from 'path'; import { fileURLToPath } from 'url'; @@ -581,7 +581,7 @@ class ConversationHandlerTestProject extends TestProjectBase { role: 'user', content: [ { - text: 'What is the temperature in Seattle?', + text: 'What is the temperature in Seattle, Boston and Miami?', }, ], }; @@ -605,7 +605,21 @@ class ConversationHandlerTestProject extends TestProjectBase { // Assert that tool was used. I.e. LLM used value provided by the tool. assert.match( response.content, - new RegExp(expectedTemperatureInProgrammaticToolScenario.toString()) + new RegExp( + expectedTemperaturesInProgrammaticToolScenario.Seattle.toString() + ) + ); + assert.match( + response.content, + new RegExp( + expectedTemperaturesInProgrammaticToolScenario.Boston.toString() + ) + ); + assert.match( + response.content, + new RegExp( + expectedTemperaturesInProgrammaticToolScenario.Miami.toString() + ) ); }; diff --git a/packages/integration-tests/src/test-projects/conversation-handler/amplify/constants.ts b/packages/integration-tests/src/test-projects/conversation-handler/amplify/constants.ts index af652ce8901..32bd11a5e2d 100644 --- a/packages/integration-tests/src/test-projects/conversation-handler/amplify/constants.ts +++ b/packages/integration-tests/src/test-projects/conversation-handler/amplify/constants.ts @@ -8,6 +8,10 @@ */ export const bedrockModelId = 'anthropic.claude-3-haiku-20240307-v1:0'; -export const expectedTemperatureInProgrammaticToolScenario = 75; +export const expectedTemperaturesInProgrammaticToolScenario = { + Seattle: 75, + Boston: 58, + Miami: 97, +}; export const expectedTemperatureInDataToolScenario = 85; diff --git a/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts b/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts index 99f67fbf3c1..922bbf3fd73 100644 --- a/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts +++ b/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts @@ -3,7 +3,7 @@ import { createExecutableTool, handleConversationTurnEvent, } from '@aws-amplify/backend-ai/conversation/runtime'; -import { expectedTemperatureInProgrammaticToolScenario } from '../constants.js'; +import { expectedTemperaturesInProgrammaticToolScenario } from '../constants.js'; const thermometerInputSchema = { type: 'object', @@ -20,12 +20,13 @@ const thermometer = createExecutableTool( json: thermometerInputSchema, }, (input) => { - if (input.city === 'Seattle') { + const city = input.city; + if (city === 'Seattle' || city === 'Boston' || city === 'Miami') { return Promise.resolve({ // We use this value in test assertion. // LLM uses tool to get temperature and serves this value in final response. // We're matching number only as LLM may translate unit to something more descriptive. - text: `${expectedTemperatureInProgrammaticToolScenario}F`, + text: `${expectedTemperaturesInProgrammaticToolScenario[city]}F`, }); } throw new Error(`Unknown city ${input.city}`); From 3a29d43fee6782406bcc58481818fea4a3c4e285 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 7 Oct 2024 15:49:29 -0700 Subject: [PATCH 032/199] Pass user agent in conversation handler lambda (#2086) * Pass user agent in conversation handler lambda * Pass user agent in conversation handler lambda --- .changeset/soft-gifts-exist.md | 5 +++ packages/ai-constructs/API.md | 4 +- .../runtime/bedrock_converse_adapter.test.ts | 39 +++++++++++++++++++ .../runtime/bedrock_converse_adapter.ts | 15 +++++++ ...ersation_message_history_retriever.test.ts | 2 +- .../conversation_message_history_retriever.ts | 3 +- .../conversation_turn_response_sender.test.ts | 4 +- .../conversation_turn_response_sender.ts | 3 +- .../event_tools_provider.ts | 3 +- .../event-tools-provider/graphql_tool.test.ts | 3 +- .../event-tools-provider/graphql_tool.ts | 4 +- .../runtime/graphql_request_executor.test.ts | 5 +++ .../runtime/graphql_request_executor.ts | 2 + .../src/conversation/runtime/types.ts | 4 +- 14 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 .changeset/soft-gifts-exist.md diff --git a/.changeset/soft-gifts-exist.md b/.changeset/soft-gifts-exist.md new file mode 100644 index 00000000000..d57eb32a9a1 --- /dev/null +++ b/.changeset/soft-gifts-exist.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': minor +--- + +Pass user agent in conversation handler lambda diff --git a/packages/ai-constructs/API.md b/packages/ai-constructs/API.md index 04b61c0baf3..774f8a44122 100644 --- a/packages/ai-constructs/API.md +++ b/packages/ai-constructs/API.md @@ -92,9 +92,7 @@ type ConversationTurnEvent = { }; }; request: { - headers: { - authorization: string; - }; + headers: Record; }; messages?: Array; messageHistoryQuery: { diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts index cb07d4fe33a..d1c2afedbea 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts @@ -840,4 +840,43 @@ void describe('Bedrock converse adapter', () => { }, ]); }); + + void it('adds user agent middleware', async () => { + const event: ConversationTurnEvent = { + ...commonEvent, + }; + + event.request.headers['x-amz-user-agent'] = 'testUserAgent'; + + const bedrockClient = new BedrockRuntimeClient(); + const addMiddlewareMock = mock.method(bedrockClient.middlewareStack, 'add'); + + new BedrockConverseAdapter( + event, + [], + bedrockClient, + undefined, + messageHistoryRetriever + ); + + assert.strictEqual(addMiddlewareMock.mock.calls.length, 1); + const middlewareHandler = addMiddlewareMock.mock.calls[0].arguments[0]; + const options = addMiddlewareMock.mock.calls[0].arguments[1]; + assert.strictEqual(options.name, 'amplify-user-agent-injector'); + const args: { + request: { + headers: Record; + }; + } = { + request: { + headers: {}, + }, + }; + // @ts-expect-error We mock subset of middleware inputs here. + await middlewareHandler(mock.fn(), {})(args); + assert.strictEqual( + args.request.headers['x-amz-user-agent'], + 'testUserAgent' + ); + }); }); diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts index 8e4001327f6..2a8bc77bfc9 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts @@ -44,6 +44,21 @@ export class BedrockConverseAdapter { ), private readonly logger = console ) { + if (event.request.headers['x-amz-user-agent']) { + this.bedrockClient.middlewareStack.add( + (next) => (args) => { + // @ts-expect-error Request is typed as unknown. + // But this is recommended way to alter headers per https://github.com/aws/aws-sdk-js-v3/blob/main/README.md. + args.request.headers['x-amz-user-agent'] = + event.request.headers['x-amz-user-agent']; + return next(args); + }, + { + step: 'build', + name: 'amplify-user-agent-injector', + } + ); + } this.executableTools = [ ...eventToolsProvider.getEventTools(), ...additionalTools, diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts index c53963f3f96..b816e0bf525 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts @@ -346,7 +346,7 @@ void describe('Conversation message history retriever', () => { for (const testCase of testCases) { void it(testCase.name, async () => { - const graphqlRequestExecutor = new GraphqlRequestExecutor('', ''); + const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); const executeGraphqlMock = mock.method( graphqlRequestExecutor, 'executeGraphql', diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts index 223f372cf02..7582df8a669 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts @@ -102,7 +102,8 @@ export class ConversationMessageHistoryRetriever { private readonly event: ConversationTurnEvent, private readonly graphqlRequestExecutor = new GraphqlRequestExecutor( event.graphqlApiEndpoint, - event.request.headers.authorization + event.request.headers.authorization, + event.request.headers['x-amz-user-agent'] ) ) {} diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts index 86805927607..da49e7eb899 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts @@ -33,7 +33,7 @@ void describe('Conversation turn response sender', () => { }; void it('sends response back to appsync', async () => { - const graphqlRequestExecutor = new GraphqlRequestExecutor('', ''); + const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); const executeGraphqlMock = mock.method( graphqlRequestExecutor, 'executeGraphql', @@ -81,7 +81,7 @@ void describe('Conversation turn response sender', () => { }); void it('serializes tool use input to JSON', async () => { - const graphqlRequestExecutor = new GraphqlRequestExecutor('', ''); + const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); const executeGraphqlMock = mock.method( graphqlRequestExecutor, 'executeGraphql', diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts index 4ec45030b03..9ca441fd6bc 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts @@ -22,7 +22,8 @@ export class ConversationTurnResponseSender { private readonly event: ConversationTurnEvent, private readonly graphqlRequestExecutor = new GraphqlRequestExecutor( event.graphqlApiEndpoint, - event.request.headers.authorization + event.request.headers.authorization, + event.request.headers['x-amz-user-agent'] ), private readonly logger = console ) {} diff --git a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.ts b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.ts index c008e7905d3..b895c757be9 100644 --- a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.ts +++ b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.ts @@ -28,7 +28,8 @@ export class ConversationTurnEventToolsProvider { inputSchema, graphqlApiEndpoint, query, - this.event.request.headers.authorization + this.event.request.headers.authorization, + this.event.request.headers['x-amz-user-agent'] ); }); return tools ?? []; diff --git a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.test.ts b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.test.ts index 6d556e4a2e7..68308ebb7b2 100644 --- a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.test.ts @@ -22,6 +22,7 @@ void describe('GraphQl tool', () => { graphQlEndpoint, query, accessToken, + '', graphqlRequestExecutor ); }; @@ -30,7 +31,7 @@ void describe('GraphQl tool', () => { const testResponse = { test: 'response', }; - const graphqlRequestExecutor = new GraphqlRequestExecutor('', ''); + const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); const executeGraphqlMock = mock.method( graphqlRequestExecutor, 'executeGraphql', diff --git a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts index 14212ccd240..8890b5f78b0 100644 --- a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts +++ b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts @@ -17,9 +17,11 @@ export class GraphQlTool implements ExecutableTool { readonly graphQlEndpoint: string, private readonly query: string, readonly accessToken: string, + readonly userAgent: string, private readonly graphqlRequestExecutor = new GraphqlRequestExecutor( graphQlEndpoint, - accessToken + accessToken, + userAgent ) ) {} diff --git a/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.test.ts b/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.test.ts index 17820e1afff..674bad3ea32 100644 --- a/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.test.ts @@ -6,6 +6,7 @@ import { GraphqlRequestExecutor } from './graphql_request_executor'; void describe('Graphql executor test', () => { const graphqlEndpoint = 'http://fake.endpoint/'; const accessToken = 'testToken'; + const userAgent = 'testUserAgent'; void it('sends request to appsync', async () => { const fetchMock = mock.fn( @@ -17,6 +18,7 @@ void describe('Graphql executor test', () => { const executor = new GraphqlRequestExecutor( graphqlEndpoint, accessToken, + userAgent, fetchMock ); const query = 'testQuery'; @@ -37,6 +39,7 @@ void describe('Graphql executor test', () => { 'application/graphql' ); assert.strictEqual(request.headers.get('Authorization'), accessToken); + assert.strictEqual(request.headers.get('x-amz-user-agent'), userAgent); assert.ok(request.body); assert.deepStrictEqual(JSON.parse(await text(request.body)), { query: 'testQuery', @@ -59,6 +62,7 @@ void describe('Graphql executor test', () => { const executor = new GraphqlRequestExecutor( graphqlEndpoint, accessToken, + userAgent, fetchMock ); const query = 'testQuery'; @@ -98,6 +102,7 @@ void describe('Graphql executor test', () => { const executor = new GraphqlRequestExecutor( graphqlEndpoint, accessToken, + userAgent, fetchMock ); const query = 'testQuery'; diff --git a/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.ts b/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.ts index 17d759deaee..a025b75cff6 100644 --- a/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.ts +++ b/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.ts @@ -15,6 +15,7 @@ export class GraphqlRequestExecutor { constructor( private readonly graphQlEndpoint: string, private readonly accessToken: string, + private readonly userAgent: string, private readonly _fetch = fetch ) {} @@ -26,6 +27,7 @@ export class GraphqlRequestExecutor { headers: { 'Content-Type': 'application/graphql', Authorization: this.accessToken, + 'x-amz-user-agent': this.userAgent, }, body: JSON.stringify({ query: request.query, diff --git a/packages/ai-constructs/src/conversation/runtime/types.ts b/packages/ai-constructs/src/conversation/runtime/types.ts index 0e68a60237b..64f5807dfc7 100644 --- a/packages/ai-constructs/src/conversation/runtime/types.ts +++ b/packages/ai-constructs/src/conversation/runtime/types.ts @@ -56,9 +56,7 @@ export type ConversationTurnEvent = { }; }; request: { - headers: { - authorization: string; - }; + headers: Record; }; /** * @deprecated This field is going to be removed in upcoming releases. From f5d0ab451fc38fe2830bdda1752f0c7ec8cb3e07 Mon Sep 17 00:00:00 2001 From: Kethan sai Date: Wed, 9 Oct 2024 13:02:58 -0400 Subject: [PATCH 033/199] adds layers to function (#1835) * adds referenceLayer to function * api.md and lint * api.md * remove referenceLayer * change set * update error to Amplify error type * update jsdocs * update jsdoc * change jsdoc * update layers * update layers * update to check for unqiue arns and adds a test * update to use set * fix api.md error * rm unrelated api.md changes * fix api.md --- .changeset/serious-dragons-attack.md | 5 + .changeset/strong-flowers-warn.md | 5 + packages/backend-function/API.md | 1 + packages/backend-function/src/factory.ts | 75 +++++--- .../backend-function/src/layer_parser.test.ts | 177 ++++++++++++++++++ packages/backend-function/src/layer_parser.ts | 66 +++++++ 6 files changed, 308 insertions(+), 21 deletions(-) create mode 100644 .changeset/serious-dragons-attack.md create mode 100644 .changeset/strong-flowers-warn.md create mode 100644 packages/backend-function/src/layer_parser.test.ts create mode 100644 packages/backend-function/src/layer_parser.ts diff --git a/.changeset/serious-dragons-attack.md b/.changeset/serious-dragons-attack.md new file mode 100644 index 00000000000..b38b05d24a8 --- /dev/null +++ b/.changeset/serious-dragons-attack.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-function': minor +--- + +adds support to reference existing layers in defineFunction diff --git a/.changeset/strong-flowers-warn.md b/.changeset/strong-flowers-warn.md new file mode 100644 index 00000000000..58648b8d7c2 --- /dev/null +++ b/.changeset/strong-flowers-warn.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend': minor +--- + +adds support to reference existing layers in defineFunction diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index bf82df50b04..c036670638c 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -31,6 +31,7 @@ export type FunctionProps = { environment?: Record; runtime?: NodeVersion; schedule?: FunctionSchedule | FunctionSchedule[]; + layers?: Record; }; // @public (undocumented) diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 46e66c15c47..a0f6feb06b3 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -1,3 +1,13 @@ +import { + FunctionOutput, + functionOutputKey, +} from '@aws-amplify/backend-output-schemas'; +import { AttributionMetadataStorage } from '@aws-amplify/backend-output-storage'; +import { + AmplifyUserError, + CallerDirectoryExtractor, + TagName, +} from '@aws-amplify/platform-core'; import { BackendOutputStorageStrategy, BackendSecret, @@ -13,31 +23,27 @@ import { SsmEnvironmentEntry, StackProvider, } from '@aws-amplify/plugin-types'; -import { Construct } from 'constructs'; -import { NodejsFunction, OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs'; -import * as path from 'path'; import { Duration, Stack, Tags } from 'aws-cdk-lib'; -import { CfnFunction, Runtime } from 'aws-cdk-lib/aws-lambda'; -import { createRequire } from 'module'; -import { FunctionEnvironmentTranslator } from './function_env_translator.js'; +import { Rule } from 'aws-cdk-lib/aws-events'; +import * as targets from 'aws-cdk-lib/aws-events-targets'; import { Policy } from 'aws-cdk-lib/aws-iam'; +import { + CfnFunction, + ILayerVersion, + LayerVersion, + Runtime, +} from 'aws-cdk-lib/aws-lambda'; +import { NodejsFunction, OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { Construct } from 'constructs'; import { readFileSync } from 'fs'; +import { createRequire } from 'module'; +import { fileURLToPath } from 'node:url'; import { EOL } from 'os'; -import { - FunctionOutput, - functionOutputKey, -} from '@aws-amplify/backend-output-schemas'; +import * as path from 'path'; +import { FunctionEnvironmentTranslator } from './function_env_translator.js'; import { FunctionEnvironmentTypeGenerator } from './function_env_type_generator.js'; -import { AttributionMetadataStorage } from '@aws-amplify/backend-output-storage'; -import { fileURLToPath } from 'node:url'; -import { - AmplifyUserError, - CallerDirectoryExtractor, - TagName, -} from '@aws-amplify/platform-core'; +import { FunctionLayerArnParser } from './layer_parser.js'; import { convertFunctionSchedulesToRuleSchedules } from './schedule_parser.js'; -import * as targets from 'aws-cdk-lib/aws-events-targets'; -import { Rule } from 'aws-cdk-lib/aws-events'; const functionStackType = 'function-Lambda'; @@ -126,6 +132,19 @@ export type FunctionProps = { * schedule: "0 9 ? * 2 *" // every Monday at 9am */ schedule?: FunctionSchedule | FunctionSchedule[]; + + /** + * Attach Lambda layers to a function + * - A Lambda layer is represented by an object of key/value pair where the key is the module name that is exported from your layer and the value is the ARN of the layer. The key (module name) is used to externalize the module dependency so it doesn't get bundled with your lambda function + * - Maximum of 5 layers can be attached to a function and must be in the same region as the function. + * @example + * layers: { + * "@aws-lambda-powertools/logger": "arn:aws:lambda::094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:11" + * }, + * @see [Amplify documentation for Lambda layers](https://docs.amplify.aws/react/build-a-backend/functions/add-lambda-layers) + * @see [AWS documentation for Lambda layers](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html) + */ + layers?: Record; }; /** @@ -163,6 +182,8 @@ class FunctionFactory implements ConstructFactory { ): HydratedFunctionProps => { const name = this.resolveName(); resourceNameValidator?.validate(name); + const parser = new FunctionLayerArnParser(); + const layers = parser.parseLayers(this.props.layers ?? {}, name); return { name, entry: this.resolveEntry(), @@ -171,6 +192,7 @@ class FunctionFactory implements ConstructFactory { environment: this.props.environment ?? {}, runtime: this.resolveRuntime(), schedule: this.resolveSchedule(), + layers, }; }; @@ -292,10 +314,19 @@ class FunctionGenerator implements ConstructContainerEntryGenerator { scope, backendSecretResolver, }: GenerateContainerEntryProps) => { + // resolve layers to LayerVersion objects for the NodejsFunction constructor using the scope. + const resolvedLayers = Object.entries(this.props.layers).map(([key, arn]) => + LayerVersion.fromLayerVersionArn( + scope, + `${this.props.name}-${key}-layer`, + arn + ) + ); + return new AmplifyFunction( scope, this.props.name, - this.props, + { ...this.props, resolvedLayers }, backendSecretResolver, this.outputStorageStrategy ); @@ -315,7 +346,7 @@ class AmplifyFunction constructor( scope: Construct, id: string, - props: HydratedFunctionProps, + props: HydratedFunctionProps & { resolvedLayers: ILayerVersion[] }, backendSecretResolver: BackendSecretResolver, outputStorageStrategy: BackendOutputStorageStrategy ) { @@ -365,6 +396,7 @@ class AmplifyFunction timeout: Duration.seconds(props.timeoutSeconds), memorySize: props.memoryMB, runtime: nodeVersionMap[props.runtime], + layers: props.resolvedLayers, bundling: { format: OutputFormat.ESM, banner: bannerCode, @@ -375,6 +407,7 @@ class AmplifyFunction }, minify: true, sourceMap: true, + externalModules: Object.keys(props.layers), }, }); } catch (error) { diff --git a/packages/backend-function/src/layer_parser.test.ts b/packages/backend-function/src/layer_parser.test.ts new file mode 100644 index 00000000000..4d1e8ea5cc9 --- /dev/null +++ b/packages/backend-function/src/layer_parser.test.ts @@ -0,0 +1,177 @@ +import { StackMetadataBackendOutputStorageStrategy } from '@aws-amplify/backend-output-storage'; +import { + ConstructContainerStub, + ResourceNameValidatorStub, + StackResolverStub, +} from '@aws-amplify/backend-platform-test-stubs'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; +import { + ConstructFactoryGetInstanceProps, + ResourceNameValidator, +} from '@aws-amplify/plugin-types'; +import { App, Stack } from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import assert from 'node:assert'; +import { beforeEach, describe, it } from 'node:test'; +import { defineFunction } from './factory.js'; + +const createStackAndSetContext = (): Stack => { + const app = new App(); + app.node.setContext('amplify-backend-name', 'testEnvName'); + app.node.setContext('amplify-backend-namespace', 'testBackendId'); + app.node.setContext('amplify-backend-type', 'branch'); + const stack = new Stack(app); + return stack; +}; + +void describe('AmplifyFunctionFactory - Layers', () => { + let rootStack: Stack; + let getInstanceProps: ConstructFactoryGetInstanceProps; + let resourceNameValidator: ResourceNameValidator; + + beforeEach(() => { + rootStack = createStackAndSetContext(); + + const constructContainer = new ConstructContainerStub( + new StackResolverStub(rootStack) + ); + + const outputStorageStrategy = new StackMetadataBackendOutputStorageStrategy( + rootStack + ); + + resourceNameValidator = new ResourceNameValidatorStub(); + + getInstanceProps = { + constructContainer, + outputStorageStrategy, + resourceNameValidator, + }; + }); + + void it('sets a valid layer', () => { + const layerArn = 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer:1'; + const functionFactory = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'lambdaWithLayer', + layers: { + myLayer: layerArn, + }, + }); + const lambda = functionFactory.getInstance(getInstanceProps); + const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'index.handler', + Layers: [layerArn], + }); + }); + + void it('sets multiple valid layers', () => { + const layerArns = [ + 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer-1:1', + 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer-2:1', + ]; + const functionFactory = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'lambdaWithMultipleLayers', + layers: { + myLayer1: layerArns[0], + myLayer2: layerArns[1], + }, + }); + const lambda = functionFactory.getInstance(getInstanceProps); + const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'index.handler', + Layers: layerArns, + }); + }); + + void it('throws an error for an invalid layer ARN', () => { + const invalidLayerArn = 'invalid:arn'; + const functionFactory = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'lambdaWithInvalidLayer', + layers: { + invalidLayer: invalidLayerArn, + }, + }); + assert.throws( + () => functionFactory.getInstance(getInstanceProps), + (error: AmplifyUserError) => { + assert.strictEqual( + error.message, + `Invalid ARN format for layer: ${invalidLayerArn}` + ); + assert.ok(error.resolution); + return true; + } + ); + }); + + void it('throws an error for exceeding the maximum number of layers', () => { + const layerArns = [ + 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer-1:1', + 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer-2:1', + 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer-3:1', + 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer-4:1', + 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer-5:1', + 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer-6:1', + ]; + const layers: Record = layerArns.reduce( + (acc, arn, index) => { + acc[`layer${index + 1}`] = arn; + return acc; + }, + {} as Record + ); + + const functionFactory = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'lambdaWithTooManyLayers', + layers, + }); + + assert.throws( + () => functionFactory.getInstance(getInstanceProps), + (error: AmplifyUserError) => { + assert.strictEqual( + error.message, + `A maximum of 5 unique layers can be attached to a function.` + ); + assert.ok(error.resolution); + return true; + } + ); + }); + + void it('checks if only unique Arns are being used', () => { + const duplicateArn = + 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer:1'; + const functionFactory = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'lambdaWithDuplicateLayers', + layers: { + layer1: duplicateArn, + layer2: duplicateArn, + layer3: duplicateArn, + layer4: duplicateArn, + layer5: duplicateArn, + layer6: duplicateArn, + }, + }); + + const lambda = functionFactory.getInstance(getInstanceProps); + const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'index.handler', + Layers: [duplicateArn], + }); + }); +}); diff --git a/packages/backend-function/src/layer_parser.ts b/packages/backend-function/src/layer_parser.ts new file mode 100644 index 00000000000..f90ba68e21b --- /dev/null +++ b/packages/backend-function/src/layer_parser.ts @@ -0,0 +1,66 @@ +import { AmplifyUserError } from '@aws-amplify/platform-core'; + +/** + * Parses Lambda Layer ARNs for a function + */ +export class FunctionLayerArnParser { + private arnPattern = new RegExp( + 'arn:[a-zA-Z0-9-]+:lambda:[a-zA-Z0-9-]+:\\d{12}:layer:[a-zA-Z0-9-_]+:[0-9]+' + ); + + /** + * Parse the layers for a function + * @param layers - Layers to be attached to the function + * @param functionName - Name of the function + * @returns Valid layers for the function + * @throws AmplifyUserError if the layer ARN is invalid + * @throws AmplifyUserError if the number of layers exceeds the limit + */ + parseLayers( + layers: Record, + functionName: string + ): Record { + const validLayers: Record = {}; + const uniqueArns = new Set(); + + for (const [key, arn] of Object.entries(layers)) { + if (!this.isValidLayerArn(arn)) { + throw new AmplifyUserError('InvalidLayerArnFormatError', { + message: `Invalid ARN format for layer: ${arn}`, + resolution: `Update the layer ARN with the expected format: arn:aws:lambda:::layer:: for function: ${functionName}`, + }); + } + + // Add to validLayers and uniqueArns only if the ARN hasn't been added already + if (!uniqueArns.has(arn)) { + uniqueArns.add(arn); + validLayers[key] = arn; + } + } + + // Validate the number of unique layers + this.validateLayerCount(uniqueArns); + + return validLayers; + } + + /** + * Validate the ARN format for a Lambda Layer + */ + private isValidLayerArn(arn: string): boolean { + return this.arnPattern.test(arn); + } + + /** + * Validate the number of layers attached to a function + * @see https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html#function-configuration-deployment-and-execution + */ + private validateLayerCount(uniqueArns: Set): void { + if (uniqueArns.size > 5) { + throw new AmplifyUserError('MaximumLayersReachedError', { + message: 'A maximum of 5 unique layers can be attached to a function.', + resolution: 'Remove unused layers in your function', + }); + } + } +} From 478170459ffda61bb8083e69f7c14bd6b4ffc44b Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 9 Oct 2024 10:07:27 -0700 Subject: [PATCH 034/199] Add information about event version to conversation components (#2089) --- .changeset/sixty-cycles-happen.md | 6 ++++++ packages/ai-constructs/API.md | 8 +++++++- .../conversation_handler_construct.ts | 6 ++++++ .../ai-constructs/src/conversation/index.ts | 7 ++++++- packages/backend-ai/API.md | 9 ++++++++- .../backend-ai/src/conversation/factory.test.ts | 9 +++++++++ packages/backend-ai/src/conversation/factory.ts | 17 +++++++++++++---- packages/backend-ai/src/conversation/index.ts | 2 ++ 8 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 .changeset/sixty-cycles-happen.md diff --git a/.changeset/sixty-cycles-happen.md b/.changeset/sixty-cycles-happen.md new file mode 100644 index 00000000000..0d5f2689d61 --- /dev/null +++ b/.changeset/sixty-cycles-happen.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/ai-constructs': minor +'@aws-amplify/backend-ai': minor +--- + +Add information about event version to conversation components diff --git a/packages/ai-constructs/API.md b/packages/ai-constructs/API.md index 774f8a44122..d2b811fc61a 100644 --- a/packages/ai-constructs/API.md +++ b/packages/ai-constructs/API.md @@ -17,7 +17,8 @@ import { ResourceProvider } from '@aws-amplify/plugin-types'; declare namespace __export__conversation { export { ConversationHandlerFunction, - ConversationHandlerFunctionProps + ConversationHandlerFunctionProps, + ConversationTurnEventVersion } } export { __export__conversation } @@ -43,6 +44,8 @@ export { __export__conversation__runtime } class ConversationHandlerFunction extends Construct implements ResourceProvider { constructor(scope: Construct, id: string, props: ConversationHandlerFunctionProps); // (undocumented) + static readonly eventVersion: ConversationTurnEventVersion; + // (undocumented) resources: FunctionResources; } @@ -114,6 +117,9 @@ type ConversationTurnEvent = { }; }; +// @public (undocumented) +type ConversationTurnEventVersion = `1.${number}`; + // @public const createExecutableTool: >(name: string, description: string, inputSchema: ToolInputSchema, handler: (input: TToolInput) => Promise) => ExecutableTool; diff --git a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts index b3e32d894d1..8f3c90efbd7 100644 --- a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts +++ b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts @@ -40,6 +40,11 @@ export type ConversationHandlerFunctionProps = { outputStorageStrategy?: BackendOutputStorageStrategy; }; +// Event is a protocol between AppSync and Lambda handler. Therefore, X.Y subset of semver is enough. +// Typing this as 1.X so that major version changes are caught by compiler if consumer of this construct inspects +// event version. +export type ConversationTurnEventVersion = `1.${number}`; + /** * Conversation Handler Function CDK construct. * This construct deploys resources that integrate conversation routes @@ -54,6 +59,7 @@ export class ConversationHandlerFunction extends Construct implements ResourceProvider { + static readonly eventVersion: ConversationTurnEventVersion = '1.0'; resources: FunctionResources; /** diff --git a/packages/ai-constructs/src/conversation/index.ts b/packages/ai-constructs/src/conversation/index.ts index 439dd4067cc..fa1330a494e 100644 --- a/packages/ai-constructs/src/conversation/index.ts +++ b/packages/ai-constructs/src/conversation/index.ts @@ -1,6 +1,11 @@ import { ConversationHandlerFunction, ConversationHandlerFunctionProps, + ConversationTurnEventVersion, } from './conversation_handler_construct.js'; -export { ConversationHandlerFunction, ConversationHandlerFunctionProps }; +export { + ConversationHandlerFunction, + ConversationHandlerFunctionProps, + ConversationTurnEventVersion, +}; diff --git a/packages/backend-ai/API.md b/packages/backend-ai/API.md index 16afe92398c..b9e044d8206 100644 --- a/packages/backend-ai/API.md +++ b/packages/backend-ai/API.md @@ -5,12 +5,14 @@ ```ts import { ConstructFactory } from '@aws-amplify/plugin-types'; +import { ConversationTurnEventVersion } from '@aws-amplify/ai-constructs/conversation'; import { FunctionResources } from '@aws-amplify/plugin-types'; import { ResourceProvider } from '@aws-amplify/plugin-types'; import * as runtime from '@aws-amplify/ai-constructs/conversation/runtime'; declare namespace __export__conversation { export { + ConversationHandlerFunctionFactory, DefineConversationHandlerFunctionProps, defineConversationHandlerFunction } @@ -28,6 +30,11 @@ declare namespace __export__conversation__runtime { } export { __export__conversation__runtime } +// @public (undocumented) +type ConversationHandlerFunctionFactory = ConstructFactory> & { + readonly eventVersion: ConversationTurnEventVersion; +}; + // @public (undocumented) type ConversationTurnEvent = runtime.ConversationTurnEvent; @@ -35,7 +42,7 @@ type ConversationTurnEvent = runtime.ConversationTurnEvent; const createExecutableTool: >(name: string, description: string, inputSchema: runtime.ToolInputSchema, handler: (input: TToolInput) => Promise) => ExecutableTool; // @public -const defineConversationHandlerFunction: (props: DefineConversationHandlerFunctionProps) => ConstructFactory>; +const defineConversationHandlerFunction: (props: DefineConversationHandlerFunctionProps) => ConversationHandlerFunctionFactory; // @public (undocumented) type DefineConversationHandlerFunctionProps = { diff --git a/packages/backend-ai/src/conversation/factory.test.ts b/packages/backend-ai/src/conversation/factory.test.ts index b3a239432a0..26ba1759be9 100644 --- a/packages/backend-ai/src/conversation/factory.test.ts +++ b/packages/backend-ai/src/conversation/factory.test.ts @@ -15,6 +15,7 @@ import { defaultEntryHandler } from './test-assets/with-default-entry/resource.j import { customEntryHandler } from './test-assets/with-custom-entry/resource.js'; import { Template } from 'aws-cdk-lib/assertions'; import { defineConversationHandlerFunction } from './factory.js'; +import { ConversationHandlerFunction } from '@aws-amplify/ai-constructs/conversation'; const createStackAndSetContext = (): Stack => { const app = new App(); @@ -57,6 +58,14 @@ void describe('ConversationHandlerFactory', () => { assert.strictEqual(instance1, instance2); }); + void it('has event version corresponding to construct', () => { + const factory = defaultEntryHandler; + assert.strictEqual( + factory.eventVersion, + ConversationHandlerFunction.eventVersion + ); + }); + void it('resolves default entry when not specified', () => { const factory = defaultEntryHandler; const lambda = factory.getInstance(getInstanceProps); diff --git a/packages/backend-ai/src/conversation/factory.ts b/packages/backend-ai/src/conversation/factory.ts index ddbb925597a..b75b6d49c4f 100644 --- a/packages/backend-ai/src/conversation/factory.ts +++ b/packages/backend-ai/src/conversation/factory.ts @@ -11,6 +11,7 @@ import { import { ConversationHandlerFunction, ConversationHandlerFunctionProps, + ConversationTurnEventVersion, } from '@aws-amplify/ai-constructs/conversation'; import path from 'path'; import { CallerDirectoryExtractor } from '@aws-amplify/platform-core'; @@ -51,9 +52,17 @@ class ConversationHandlerFunctionGenerator }; } -class ConversationHandlerFunctionFactory - implements ConstructFactory +export type ConversationHandlerFunctionFactory = ConstructFactory< + ResourceProvider +> & { + readonly eventVersion: ConversationTurnEventVersion; +}; + +class DefaultConversationHandlerFunctionFactory + implements ConversationHandlerFunctionFactory { + readonly eventVersion: ConversationTurnEventVersion = + ConversationHandlerFunction.eventVersion; private generator: ConstructContainerEntryGenerator; constructor( @@ -121,6 +130,6 @@ export type DefineConversationHandlerFunctionProps = { */ export const defineConversationHandlerFunction = ( props: DefineConversationHandlerFunctionProps -): ConstructFactory> => +): ConversationHandlerFunctionFactory => // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors - new ConversationHandlerFunctionFactory(props, new Error().stack); + new DefaultConversationHandlerFunctionFactory(props, new Error().stack); diff --git a/packages/backend-ai/src/conversation/index.ts b/packages/backend-ai/src/conversation/index.ts index dcf2f7c4419..489209c219d 100644 --- a/packages/backend-ai/src/conversation/index.ts +++ b/packages/backend-ai/src/conversation/index.ts @@ -1,9 +1,11 @@ import { + ConversationHandlerFunctionFactory, DefineConversationHandlerFunctionProps, defineConversationHandlerFunction, } from './factory.js'; export { + ConversationHandlerFunctionFactory, DefineConversationHandlerFunctionProps, defineConversationHandlerFunction, }; From d88363ad64b447addf61b10f04ab2cc06328510e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 18:25:49 +0000 Subject: [PATCH 035/199] Version Packages (#2085) Co-authored-by: github-actions[bot] --- .changeset/serious-dragons-attack.md | 5 ----- .changeset/sixty-cycles-happen.md | 6 ------ .changeset/soft-gifts-exist.md | 5 ----- .changeset/strong-flowers-warn.md | 5 ----- .changeset/tender-camels-agree.md | 5 ----- packages/ai-constructs/CHANGELOG.md | 11 +++++++++++ packages/ai-constructs/package.json | 2 +- packages/backend-ai/CHANGELOG.md | 13 +++++++++++++ packages/backend-ai/package.json | 4 ++-- packages/backend-function/CHANGELOG.md | 6 ++++++ packages/backend-function/package.json | 2 +- packages/backend/CHANGELOG.md | 11 +++++++++++ packages/backend/package.json | 4 ++-- packages/integration-tests/package.json | 6 +++--- 14 files changed, 50 insertions(+), 35 deletions(-) delete mode 100644 .changeset/serious-dragons-attack.md delete mode 100644 .changeset/sixty-cycles-happen.md delete mode 100644 .changeset/soft-gifts-exist.md delete mode 100644 .changeset/strong-flowers-warn.md delete mode 100644 .changeset/tender-camels-agree.md diff --git a/.changeset/serious-dragons-attack.md b/.changeset/serious-dragons-attack.md deleted file mode 100644 index b38b05d24a8..00000000000 --- a/.changeset/serious-dragons-attack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-function': minor ---- - -adds support to reference existing layers in defineFunction diff --git a/.changeset/sixty-cycles-happen.md b/.changeset/sixty-cycles-happen.md deleted file mode 100644 index 0d5f2689d61..00000000000 --- a/.changeset/sixty-cycles-happen.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/ai-constructs': minor -'@aws-amplify/backend-ai': minor ---- - -Add information about event version to conversation components diff --git a/.changeset/soft-gifts-exist.md b/.changeset/soft-gifts-exist.md deleted file mode 100644 index d57eb32a9a1..00000000000 --- a/.changeset/soft-gifts-exist.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': minor ---- - -Pass user agent in conversation handler lambda diff --git a/.changeset/strong-flowers-warn.md b/.changeset/strong-flowers-warn.md deleted file mode 100644 index 58648b8d7c2..00000000000 --- a/.changeset/strong-flowers-warn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend': minor ---- - -adds support to reference existing layers in defineFunction diff --git a/.changeset/tender-camels-agree.md b/.changeset/tender-camels-agree.md deleted file mode 100644 index 1a85db90560..00000000000 --- a/.changeset/tender-camels-agree.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': patch ---- - -Fix multi tool usage in single turn. diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index 5eac972378a..174ce5d2522 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/ai-constructs +## 0.4.0 + +### Minor Changes + +- 4781704: Add information about event version to conversation components +- 3a29d43: Pass user agent in conversation handler lambda + +### Patch Changes + +- 6e4a62f: Fix multi tool usage in single turn. + ## 0.3.0 ### Minor Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index 783ec587e67..21fbfa18c42 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "0.3.0", + "version": "0.4.0", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index 2e083216a47..7b0d715433e 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/backend-ai +## 0.3.0 + +### Minor Changes + +- 4781704: Add information about event version to conversation components + +### Patch Changes + +- Updated dependencies [4781704] +- Updated dependencies [3a29d43] +- Updated dependencies [6e4a62f] + - @aws-amplify/ai-constructs@0.4.0 + ## 0.2.0 ### Minor Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index e49d80c0b5c..7711ba387f0 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "0.2.0", + "version": "0.3.0", "type": "module", "publishConfig": { "access": "public" @@ -22,7 +22,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.3.0", + "@aws-amplify/ai-constructs": "^0.4.0", "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.0.2", "@aws-amplify/platform-core": "^1.1.0", diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index 6ef7160de58..4cb76fefa59 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/backend-function +## 1.6.0 + +### Minor Changes + +- f5d0ab4: adds support to reference existing layers in defineFunction + ## 1.5.0 ### Minor Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index 097ad1f416f..d4a4c3b60f6 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.5.0", + "version": "1.6.0", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 04cf417613d..1a319a379fd 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend +## 1.4.0 + +### Minor Changes + +- f5d0ab4: adds support to reference existing layers in defineFunction + +### Patch Changes + +- Updated dependencies [f5d0ab4] + - @aws-amplify/backend-function@1.6.0 + ## 1.3.2 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 04aec49b7ee..2d477fe3ee7 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.3.2", + "version": "1.4.0", "type": "module", "publishConfig": { "access": "public" @@ -27,7 +27,7 @@ "dependencies": { "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/backend-auth": "^1.2.0", - "@aws-amplify/backend-function": "^1.5.0", + "@aws-amplify/backend-function": "^1.6.0", "@aws-amplify/backend-data": "^1.1.4", "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.1.2", diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 089995d6961..a8199c7a1a1 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -5,10 +5,10 @@ "type": "module", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.3.0", + "@aws-amplify/ai-constructs": "^0.4.0", "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend": "^1.3.2", - "@aws-amplify/backend-ai": "^0.2.0", + "@aws-amplify/backend": "^1.4.0", + "@aws-amplify/backend-ai": "^0.3.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/data-schema": "^1.0.0", From 93d419f554320670e2a27c6a62f1d347434b2905 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Thu, 10 Oct 2024 08:32:25 -0700 Subject: [PATCH 036/199] detect more generic CFN deployment failure errors (#2097) --- .changeset/modern-toys-jump.md | 5 +++++ packages/backend-deployer/src/cdk_error_mapper.test.ts | 6 ++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .changeset/modern-toys-jump.md diff --git a/.changeset/modern-toys-jump.md b/.changeset/modern-toys-jump.md new file mode 100644 index 00000000000..6f13875f253 --- /dev/null +++ b/.changeset/modern-toys-jump.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +detect more generic CFN deployment failure errors diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 5e22f875d70..45f36f61e2d 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -133,6 +133,12 @@ const testErrorMappings = [ errorName: 'SecretNotSetError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: `[31m some-stack failed: The stack named some-stack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: The code contains one or more errors. (Service: AppSync, Status Code: 400, Request ID: 12345) (RequestToken: 123, HandlerErrorCode: GeneralServiceException), Embedded stack was not successfully updated. Currently in UPDATE_ROLLBACK_IN_PROGRESS with reason: The following resource(s) failed to create: [resource1, resource2]. [39m`, + expectedTopLevelErrorMessage: 'The CloudFormation deployment has failed.', + errorName: 'CloudFormationDeploymentError', + expectedDownstreamErrorMessage: `The stack named some-stack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: The code contains one or more errors. (Service: AppSync, Status Code: 400, Request ID: 12345) (RequestToken: 123, HandlerErrorCode: GeneralServiceException), Embedded stack was not successfully updated. Currently in UPDATE_ROLLBACK_IN_PROGRESS with reason: The following resource(s) failed to create: [resource1, resource2]. [39m`, + }, { errorMessage: 'CFN error happened: Updates are not allowed for property: some property', diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index f194d0d4aaa..fe5246ff3a7 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -277,7 +277,7 @@ export class CdkErrorMapper { { // Note that the order matters, this should be the last as it captures generic CFN error errorRegex: new RegExp( - `Deployment failed: (.*)${this.multiLineEolRegex}` + `Deployment failed: (.*)${this.multiLineEolRegex}|The stack named (.*) failed to deploy: (.*)` ), humanReadableErrorMessage: 'The CloudFormation deployment has failed.', resolutionMessage: From b17a6a64e746d53138f45a5056edc1bc7d84ff23 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 10 Oct 2024 10:14:19 -0700 Subject: [PATCH 037/199] allow trailing dot when applying namespace. (#2100) * allow trailing dot when applying namespace. * add comment * fix that --- .../without-breaks/project-with-namespace/API.md | 10 ++++++++++ .../project-with-namespace/src/some_namespace.ts | 8 ++++++++ .../api-changes-validator/usage_statemets_renderer.ts | 5 +++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-with-namespace/API.md b/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-with-namespace/API.md index 6d8f581f00f..f01fbae9acb 100644 --- a/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-with-namespace/API.md +++ b/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-with-namespace/API.md @@ -16,6 +16,14 @@ type SomeOtherTypeUnderSubNamespace = { someProperty: SomeTypeUnderSubNamespace; }; +class SomeClassUnderNamespace { + static readonly someStaticProperty: string; +} + +type SomeTypeUnderNamespaceWithGenerics = { + someGenericProperty: TPropertyType; +}; + declare namespace someSubNamespace { export { SomeTypeUnderSubNamespace, @@ -28,7 +36,9 @@ export const functionUsingTypes2: (props: SomeTypeUnderNamespace, extraArg: stri declare namespace someNamespace { export { + SomeClassUnderNamespace, SomeTypeUnderNamespace, + SomeTypeUnderNamespaceWithGenerics, someSubNamespace, functionUsingTypes1, functionUsingTypes2 diff --git a/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-with-namespace/src/some_namespace.ts b/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-with-namespace/src/some_namespace.ts index 24be0c1e89c..0b65dbb60d6 100644 --- a/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-with-namespace/src/some_namespace.ts +++ b/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-with-namespace/src/some_namespace.ts @@ -14,3 +14,11 @@ export const functionUsingTypes2 = ( ): Array => { throw new Error(); }; + +export class SomeClassUnderNamespace { + static readonly someStaticProperty: string; +} + +export type SomeTypeUnderNamespaceWithGenerics = { + someGenericProperty: TPropertyType; +}; diff --git a/scripts/components/api-changes-validator/usage_statemets_renderer.ts b/scripts/components/api-changes-validator/usage_statemets_renderer.ts index a232c4ba6f6..ba36d058be5 100644 --- a/scripts/components/api-changes-validator/usage_statemets_renderer.ts +++ b/scripts/components/api-changes-validator/usage_statemets_renderer.ts @@ -69,9 +69,10 @@ export class UsageStatementsRenderer { // characters that can be found before or after symbol // this is to prevent partial matches in case one symbol's characters are subset of longer one - const symbolTerminators = '[\\s\\,\\(\\)<>;]'; + const possibleSymbolPrefix = '[\\s\\,\\(<;]'; + const possibleSymbolSuffix = '[\\s\\,\\(\\)<>;\\.]'; const regex = new RegExp( - `(${symbolTerminators})(${symbolName})(${symbolTerminators})`, + `(${possibleSymbolPrefix})(${symbolName})(${possibleSymbolSuffix})`, 'g' ); usageStatements = usageStatements.replaceAll( From faacd1b2343cf3a6ce7a57716a0cd107cc2b5d0a Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 10 Oct 2024 15:29:39 -0700 Subject: [PATCH 038/199] Fix case where bedrock content blocks would be populated with 'null' instead of 'undefined. (#2101) --- .changeset/early-starfishes-impress.md | 5 ++ packages/ai-constructs/API.md | 6 +++ ...ersation_message_history_retriever.test.ts | 52 +++++++++++++++++++ .../conversation_message_history_retriever.ts | 20 ++++++- .../src/conversation/runtime/types.ts | 8 +++ 5 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 .changeset/early-starfishes-impress.md diff --git a/.changeset/early-starfishes-impress.md b/.changeset/early-starfishes-impress.md new file mode 100644 index 00000000000..0995ac35938 --- /dev/null +++ b/.changeset/early-starfishes-impress.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': patch +--- + +Fix case where bedrock content blocks would be populated with 'null' instead of 'undefined. diff --git a/packages/ai-constructs/API.md b/packages/ai-constructs/API.md index d2b811fc61a..6719fa2b59f 100644 --- a/packages/ai-constructs/API.md +++ b/packages/ai-constructs/API.md @@ -72,6 +72,12 @@ type ConversationMessageContentBlock = bedrock.ContentBlock | { bytes: string; }; }; + text?: never; + document?: never; + toolUse?: never; + toolResult?: never; + guardContent?: never; + $unknown?: never; }; // @public (undocumented) diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts index b816e0bf525..0c349a34e24 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts @@ -342,6 +342,58 @@ void describe('Conversation message history retriever', () => { }, ], }, + { + name: 'Replaces null values with undefined', + mockListResponseMessages: [ + { + id: event.currentMessageId, + conversationId: event.conversationId, + role: 'user', + content: [ + { + text: 'some_text', + // @ts-expect-error Intentionally providing null outside of typing + image: null, + // @ts-expect-error Intentionally providing null outside of typing + document: null, + // @ts-expect-error Intentionally providing null outside of typing + toolUse: null, + // @ts-expect-error Intentionally providing null outside of typing + toolResult: null, + // @ts-expect-error Intentionally providing null outside of typing + guardContent: null, + // @ts-expect-error Intentionally providing null outside of typing + $unknown: null, + }, + { + // @ts-expect-error Intentionally providing null outside of typing + text: null, + document: { format: 'csv', name: 'test_name', source: undefined }, + }, + ], + }, + ], + expectedMessages: [ + { + role: 'user', + content: [ + { + text: 'some_text', + image: undefined, + document: undefined, + toolUse: undefined, + toolResult: undefined, + guardContent: undefined, + $unknown: undefined, + }, + { + text: undefined, + document: { format: 'csv', name: 'test_name', source: undefined }, + }, + ], + }, + ], + }, ]; for (const testCase of testCases) { diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts index 7582df8a669..54a11f1eeef 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts @@ -235,6 +235,24 @@ export class ConversationMessageHistoryRetriever { variables, }); - return response.data[this.event.messageHistoryQuery.listQueryName].items; + const items = + response.data[this.event.messageHistoryQuery.listQueryName].items; + + items.forEach((item) => { + item.content?.forEach((contentBlock) => { + let property: keyof typeof contentBlock; + for (property in contentBlock) { + // Deserialization of GraphQl query result sets these properties to 'null' + // This can trigger Bedrock SDK validation as it expects 'undefined' if properties are not set. + // We can't fix how GraphQl response is deserialized. + // Therefore, we apply this transformation to fix the data. + if (contentBlock[property] === null) { + contentBlock[property] = undefined; + } + } + }); + }); + + return items; }; } diff --git a/packages/ai-constructs/src/conversation/runtime/types.ts b/packages/ai-constructs/src/conversation/runtime/types.ts index 64f5807dfc7..23a54bf146c 100644 --- a/packages/ai-constructs/src/conversation/runtime/types.ts +++ b/packages/ai-constructs/src/conversation/runtime/types.ts @@ -26,6 +26,14 @@ export type ConversationMessageContentBlock = // Upstream (Appsync) may send images in a form of Base64 encoded strings source: { bytes: string }; }; + // These are needed so that union with other content block types works. + // See https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-bedrock-runtime/TypeAlias/ContentBlock/. + text?: never; + document?: never; + toolUse?: never; + toolResult?: never; + guardContent?: never; + $unknown?: never; }; export type ToolDefinition = { From 777c80d6f8224aff1aabe543b611e077c89bdeca Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Thu, 10 Oct 2024 18:14:09 -0700 Subject: [PATCH 039/199] detect transform errors with multiple errors (#2102) * detect transform errors with multiple errors * new method of getting multiple transform errors --- .changeset/old-suns-float.md | 5 +++++ .../src/cdk_error_mapper.test.ts | 16 ++++++++++++++++ .../backend-deployer/src/cdk_error_mapper.ts | 9 ++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .changeset/old-suns-float.md diff --git a/.changeset/old-suns-float.md b/.changeset/old-suns-float.md new file mode 100644 index 00000000000..f0cdb5260bc --- /dev/null +++ b/.changeset/old-suns-float.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +detect transform errors with multiple errors diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 45f36f61e2d..99062785959 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -221,6 +221,22 @@ const testErrorMappings = [ errorName: 'ESBuildError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: + `Error [TransformError]: Transform failed with 2 errors:` + + EOL + + `/Users/user/work-space/amplify-app/amplify/auth/resource.ts:48:4: ERROR: Multiple exports with the same name auth` + + EOL + + `/Users/user/work-space/amplify-app/amplify/auth/resource.ts:48:4: ERROR: The symbol auth has already been declared` + + EOL + + ` at failureErrorWithLog (/Users/user/work-space/amplify-app/node_modules/tsx/node_modules/esbuild/lib/main.js:1472:15)`, + expectedTopLevelErrorMessage: + `/Users/user/work-space/amplify-app/amplify/auth/resource.ts:48:4: ERROR: Multiple exports with the same name auth` + + EOL + + `/Users/user/work-space/amplify-app/amplify/auth/resource.ts:48:4: ERROR: The symbol auth has already been declared`, + errorName: 'ESBuildError', + expectedDownstreamErrorMessage: undefined, + }, { errorMessage: `some rubbish before` + diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index fe5246ff3a7..a063f1694b4 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -50,6 +50,12 @@ export class CdkErrorMapper { underlyingError = undefined; } } + // remove any trailing EOL + matchingError.humanReadableErrorMessage = + matchingError.humanReadableErrorMessage.replace( + new RegExp(`${this.multiLineEolRegex}$`), + '' + ); } else { underlyingError.message = matchGroups[0]; } @@ -193,8 +199,9 @@ export class CdkErrorMapper { classification: 'ERROR', }, { + // If there are multiple errors, capture all lines containing the errors errorRegex: new RegExp( - `\\[TransformError\\]: Transform failed with .* error:${this.multiLineEolRegex}(?.*)` + `\\[TransformError\\]: Transform failed with .* error(s?):${this.multiLineEolRegex}(?(.*ERROR:.*${this.multiLineEolRegex})+)` ), humanReadableErrorMessage: '{esBuildErrorMessage}', resolutionMessage: From 4720412dab80b69ca2750a94fa954b21ccbc9c20 Mon Sep 17 00:00:00 2001 From: MURAKAMI Masahiko Date: Fri, 11 Oct 2024 23:52:45 +0900 Subject: [PATCH 040/199] Add minify option to defineFunction (#2093) * Add minify option to defineFunction * Add unit tests and e2e tests when set minify option to false * Add changeset * Update API.md * add bundling options * Update .changeset/pink-rockets-dance.md * use optional chaining * include funcNoMinify into function.ts --------- Co-authored-by: Kamil Sobol --- .changeset/pink-rockets-dance.md | 6 +++ package-lock.json | 36 ++++++++++----- packages/backend-function/API.md | 6 +++ packages/backend-function/src/factory.test.ts | 19 ++++++++ packages/backend-function/src/factory.ts | 44 ++++++++++++++++--- packages/integration-tests/package.json | 1 + .../data_storage_auth_with_triggers.test.ts | 1 + .../data_storage_auth_with_triggers.ts | 39 +++++++++++++++- .../amplify/func-src/handler_no_minify.ts | 6 +++ .../amplify/function.ts | 8 ++++ .../amplify/test_factories.ts | 2 + .../hotswap-update-files/function.ts | 8 ++++ 12 files changed, 156 insertions(+), 20 deletions(-) create mode 100644 .changeset/pink-rockets-dance.md create mode 100644 packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_no_minify.ts diff --git a/.changeset/pink-rockets-dance.md b/.changeset/pink-rockets-dance.md new file mode 100644 index 00000000000..e0d4c607241 --- /dev/null +++ b/.changeset/pink-rockets-dance.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/backend-function': minor +'@aws-amplify/backend': minor +--- + +Add minify option to defineFunction diff --git a/package-lock.json b/package-lock.json index 4c4620c988c..2cefb0ec584 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18876,6 +18876,17 @@ "node": ">=8" } }, + "node_modules/@zip.js/zip.js": { + "version": "2.7.52", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.52.tgz", + "integrity": "sha512-+5g7FQswvrCHwYKNMd/KFxZSObctLSsQOgqBSi0LzwHo3li9Eh1w5cF5ndjQw9Zbr3ajVnd2+XyiX85gAetx1Q==", + "dev": true, + "engines": { + "bun": ">=0.7.0", + "deno": ">=1.0.0", + "node": ">=16.5.0" + } + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -31445,10 +31456,10 @@ }, "packages/ai-constructs": { "name": "@aws-amplify/ai-constructs", - "version": "0.2.0", + "version": "0.3.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.2.1", + "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.0.1", "@aws-sdk/client-bedrock-runtime": "^3.622.0", @@ -31485,13 +31496,13 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.3.1", + "version": "1.3.2", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-auth": "^1.2.0", "@aws-amplify/backend-data": "^1.1.4", "@aws-amplify/backend-function": "^1.5.0", - "@aws-amplify/backend-output-schemas": "^1.2.1", + "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/backend-storage": "^1.2.1", @@ -31514,11 +31525,11 @@ }, "packages/backend-ai": { "name": "@aws-amplify/backend-ai", - "version": "0.1.2", + "version": "0.2.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.2.0", - "@aws-amplify/backend-output-schemas": "^1.2.1", + "@aws-amplify/ai-constructs": "^0.3.0", + "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.0.2", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.0.1" @@ -31620,7 +31631,7 @@ }, "packages/backend-output-schemas": { "name": "@aws-amplify/backend-output-schemas", - "version": "1.2.1", + "version": "1.3.0", "license": "Apache-2.0", "devDependencies": { "@aws-amplify/plugin-types": "^1.2.0" @@ -32028,10 +32039,10 @@ "license": "Apache-2.0", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.2.0", + "@aws-amplify/ai-constructs": "^0.3.0", "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend": "^1.3.1", - "@aws-amplify/backend-ai": "^0.1.2", + "@aws-amplify/backend": "^1.3.2", + "@aws-amplify/backend-ai": "^0.2.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/data-schema": "^1.0.0", @@ -32053,6 +32064,7 @@ "@aws-sdk/credential-providers": "^3.624.0", "@smithy/shared-ini-file-loader": "^2.2.5", "@types/lodash.ismatch": "^4.4.9", + "@zip.js/zip.js": "^2.7.52", "aws-amplify": "^6.0.16", "aws-appsync-auth-link": "^3.0.7", "aws-cdk-lib": "^2.152.0", @@ -32267,7 +32279,7 @@ }, "packages/sandbox": { "name": "@aws-amplify/sandbox", - "version": "1.2.2", + "version": "1.2.3", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-deployer": "^1.1.3", diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index c036670638c..7348dba85ec 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -22,6 +22,11 @@ export type CronSchedule = `${string} ${string} ${string} ${string} ${string}` | // @public export const defineFunction: (props?: FunctionProps) => ConstructFactory & ResourceAccessAcceptorFactory & AddEnvironmentFactory & StackProvider>; +// @public (undocumented) +export type FunctionBundlingOptions = { + minify?: boolean; +}; + // @public (undocumented) export type FunctionProps = { name?: string; @@ -32,6 +37,7 @@ export type FunctionProps = { runtime?: NodeVersion; schedule?: FunctionSchedule | FunctionSchedule[]; layers?: Record; + bundling?: FunctionBundlingOptions; }; // @public (undocumented) diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index 477ce69aec6..127c10c33ce 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -411,6 +411,25 @@ void describe('AmplifyFunctionFactory', () => { }); }); + void describe('minify property', () => { + void it('sets minify to false', () => { + const lambda = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + bundling: { + minify: false, + }, + }).getInstance(getInstanceProps); + const template = Template.fromStack(lambda.stack); + // There isn't a way to check the contents of the bundled lambda using the CDK Template utility + // So we just check that the lambda was created properly in the CFN template. + // There is an e2e test that validates proper lambda bundling + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'index.handler', + }); + }); + }); + void describe('resourceAccessAcceptor', () => { void it('attaches policy to execution role and configures ssm environment context', () => { const functionFactory = defineFunction({ diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index a0f6feb06b3..7c09ea97d9b 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -145,6 +145,20 @@ export type FunctionProps = { * @see [AWS documentation for Lambda layers](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html) */ layers?: Record; + + /* + * Options for bundling the function code. + */ + bundling?: FunctionBundlingOptions; +}; + +export type FunctionBundlingOptions = { + /** + * Whether to minify the function code. + * + * Defaults to true. + */ + minify?: boolean; }; /** @@ -192,6 +206,7 @@ class FunctionFactory implements ConstructFactory { environment: this.props.environment ?? {}, runtime: this.resolveRuntime(), schedule: this.resolveSchedule(), + bundling: this.resolveBundling(), layers, }; }; @@ -298,6 +313,27 @@ class FunctionFactory implements ConstructFactory { return this.props.schedule; }; + + private resolveBundling = () => { + const bundlingDefault = { + format: OutputFormat.ESM, + bundleAwsSDK: true, + loader: { + '.node': 'file', + }, + minify: true, + sourceMap: true, + }; + + return { + ...bundlingDefault, + minify: this.resolveMinify(this.props.bundling), + }; + }; + + private resolveMinify = (bundling?: FunctionBundlingOptions) => { + return bundling?.minify === undefined ? true : bundling.minify; + }; } type HydratedFunctionProps = Required; @@ -398,15 +434,9 @@ class AmplifyFunction runtime: nodeVersionMap[props.runtime], layers: props.resolvedLayers, bundling: { - format: OutputFormat.ESM, + ...props.bundling, banner: bannerCode, - bundleAwsSDK: true, inject: shims, - loader: { - '.node': 'file', - }, - minify: true, - sourceMap: true, externalModules: Object.keys(props.layers), }, }); diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index a8199c7a1a1..295227183c1 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -30,6 +30,7 @@ "@aws-sdk/credential-providers": "^3.624.0", "@smithy/shared-ini-file-loader": "^2.2.5", "@types/lodash.ismatch": "^4.4.9", + "@zip.js/zip.js": "^2.7.52", "aws-amplify": "^6.0.16", "aws-appsync-auth-link": "^3.0.7", "aws-cdk-lib": "^2.152.0", diff --git a/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts b/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts index 7294be74830..7570ece6fd7 100644 --- a/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts +++ b/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts @@ -52,6 +52,7 @@ void it('data storage auth with triggers', () => { assertExpectedLogicalIds(templates.defaultNodeFunc, 'AWS::Lambda::Function', [ 'defaultNodeFunctionlambda5C194062', 'echoFunclambdaE17DCA46', + 'funcNoMinifylambda91CDF3E0', 'funcWithAwsSdklambda5F770AD7', 'funcWithSchedulelambda0B6E4271', 'funcWithSsmlambda6A8824A1', diff --git a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts index 27077d8359a..f65f1f48c7f 100644 --- a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts +++ b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts @@ -9,7 +9,11 @@ import path from 'path'; import { TestProjectCreator } from './test_project_creator.js'; import { DeployedResourcesFinder } from '../find_deployed_resource.js'; import assert from 'node:assert'; -import { InvokeCommand, LambdaClient } from '@aws-sdk/client-lambda'; +import { + GetFunctionCommand, + InvokeCommand, + LambdaClient, +} from '@aws-sdk/client-lambda'; import { amplifySharedSecretNameKey, createAmplifySharedSecretName, @@ -28,6 +32,7 @@ import { } from '@aws-sdk/client-cloudtrail'; import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; import isMatch from 'lodash.ismatch'; +import { TextWriter, ZipReader } from '@zip.js/zip.js'; /** * Creates test projects with data, storage, and auth categories. @@ -238,6 +243,12 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { (name) => name.includes('funcWithSchedule') ); + const funcNoMinify = await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::Lambda::Function', + (name) => name.includes('funcNoMinify') + ); + assert.equal(defaultNodeLambda.length, 1); assert.equal(node16Lambda.length, 1); assert.equal(funcWithSsm.length, 1); @@ -258,6 +269,13 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { await this.assertScheduleInvokesFunction(backendId); + const expectedNoMinifyChunk = [ + 'var handler = async () => {', + ' return "No minify";', + '};', + ].join('\n'); + await this.checkLambdaCode(funcNoMinify[0], expectedNoMinifyChunk); + const bucketName = await this.resourceFinder.findByBackendIdentifier( backendId, 'AWS::S3::Bucket', @@ -390,6 +408,25 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { assert.deepStrictEqual(responsePayload, expectedResponse); }; + private checkLambdaCode = async ( + lambdaName: string, + expectedCode: string + ) => { + // get the lambda code + const response = await this.lambdaClient.send( + new GetFunctionCommand({ FunctionName: lambdaName }) + ); + const codeUrl = response.Code?.Location; + assert(codeUrl !== undefined); + const fetchResponse = await fetch(codeUrl); + const zipReader = new ZipReader(fetchResponse.body!); + const entries = await zipReader.getEntries(); + const entry = entries.find((entry) => entry.filename.endsWith('index.mjs')); + assert(entry !== undefined); + const sourceCode = await entry.getData!(new TextWriter()); + assert(sourceCode.includes(expectedCode)); + }; + private assertExpectedCleanup = async () => { await this.waitForBucketDeletion(this.testBucketName); await this.assertRolesDoNotExist(this.testRoleNames); diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_no_minify.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_no_minify.ts new file mode 100644 index 00000000000..f5a3fff4558 --- /dev/null +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_no_minify.ts @@ -0,0 +1,6 @@ +/** + * This function asserts that the code is not minified. + */ +export const handler = async () => { + return 'No minify'; +}; diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/function.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/function.ts index 2405878c75a..7e367aca48b 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/function.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/function.ts @@ -50,3 +50,11 @@ export const funcWithSchedule = defineFunction({ entry: './func-src/handler_with_aws_sqs.ts', schedule: '* * * * ?', }); + +export const funcNoMinify = defineFunction({ + name: 'funcNoMinify', + entry: './func-src/handler_no_minify.ts', + bundling: { + minify: false, + }, +}); diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/test_factories.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/test_factories.ts index 49227af6158..d782b45be15 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/test_factories.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/test_factories.ts @@ -5,6 +5,7 @@ import { funcWithAwsSdk, node16Func, funcWithSchedule, + funcNoMinify, } from './function.js'; import { storage } from './storage/resource.js'; import { auth } from './auth/resource.js'; @@ -18,4 +19,5 @@ export const dataStorageAuthWithTriggers = { funcWithSsm, funcWithAwsSdk, funcWithSchedule, + funcNoMinify, }; diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/hotswap-update-files/function.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/hotswap-update-files/function.ts index 61a9171a16d..542465f8595 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/hotswap-update-files/function.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/hotswap-update-files/function.ts @@ -52,3 +52,11 @@ export const funcWithSchedule = defineFunction({ entry: './func-src/handler_with_aws_sqs.ts', schedule: '* * * * ?', }); + +export const funcNoMinify = defineFunction({ + name: 'funcNoMinify', + entry: './func-src/handler_no_minify.ts', + bundling: { + minify: false, + }, +}); From 59050f5bcaaff8e5a8eb75fe392fef8f21b7d236 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Fri, 11 Oct 2024 09:03:27 -0700 Subject: [PATCH 041/199] upgrade constructs (#2103) --- package-lock.json | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2cefb0ec584..dbc1f5fbc8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21112,12 +21112,11 @@ } }, "node_modules/constructs": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.3.0.tgz", - "integrity": "sha512-vbK8i3rIb/xwZxSpTjz3SagHn1qq9BChLEfy5Hf6fB3/2eFbrwt2n9kHwQcS0CPTRBesreeAcsJfMq2229FnbQ==", - "license": "Apache-2.0", + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.3.2.tgz", + "integrity": "sha512-odjsmhoBKRWa2F/Z3edOSZCb7IgxAL5usXQMRKoINMJzcFfC1GvcbO6Dd/xMGLRv4J/tEsjSLwqLxRfJrjPsQw==", "engines": { - "node": ">= 16.14.0" + "node": ">= 18.12.0" } }, "node_modules/content-disposition": { @@ -31456,7 +31455,7 @@ }, "packages/ai-constructs": { "name": "@aws-amplify/ai-constructs", - "version": "0.3.0", + "version": "0.4.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.3.0", @@ -31496,12 +31495,12 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.3.2", + "version": "1.4.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-auth": "^1.2.0", "@aws-amplify/backend-data": "^1.1.4", - "@aws-amplify/backend-function": "^1.5.0", + "@aws-amplify/backend-function": "^1.6.0", "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/backend-secret": "^1.1.2", @@ -31525,10 +31524,10 @@ }, "packages/backend-ai": { "name": "@aws-amplify/backend-ai", - "version": "0.2.0", + "version": "0.3.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.3.0", + "@aws-amplify/ai-constructs": "^0.4.0", "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.0.2", "@aws-amplify/platform-core": "^1.1.0", @@ -31595,7 +31594,7 @@ }, "packages/backend-function": { "name": "@aws-amplify/backend-function", - "version": "1.5.0", + "version": "1.6.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", @@ -32039,10 +32038,10 @@ "license": "Apache-2.0", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.3.0", + "@aws-amplify/ai-constructs": "^0.4.0", "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend": "^1.3.2", - "@aws-amplify/backend-ai": "^0.2.0", + "@aws-amplify/backend": "^1.4.0", + "@aws-amplify/backend-ai": "^0.3.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/data-schema": "^1.0.0", From 46a0e8503f52755d40cd06a67dedefbec69dac35 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Fri, 11 Oct 2024 12:53:26 -0700 Subject: [PATCH 042/199] Remove deprecated messages field from event (#2106) --- .changeset/dull-mugs-agree.md | 5 + packages/ai-constructs/API.md | 24 ----- .../conversation_message_history_retriever.ts | 4 - .../conversation_turn_executor.test.ts | 1 - .../conversation_turn_response_sender.test.ts | 1 - .../event_tools_provider.test.ts | 2 - .../src/conversation/runtime/index.ts | 4 - .../src/conversation/runtime/types.ts | 4 - .../conversation_handler_project.ts | 102 ++++++------------ 9 files changed, 40 insertions(+), 107 deletions(-) create mode 100644 .changeset/dull-mugs-agree.md diff --git a/.changeset/dull-mugs-agree.md b/.changeset/dull-mugs-agree.md new file mode 100644 index 00000000000..e6b2be25bbe --- /dev/null +++ b/.changeset/dull-mugs-agree.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': minor +--- + +Remove deprecated messages field from event diff --git a/packages/ai-constructs/API.md b/packages/ai-constructs/API.md index 6719fa2b59f..0a9053d1592 100644 --- a/packages/ai-constructs/API.md +++ b/packages/ai-constructs/API.md @@ -25,8 +25,6 @@ export { __export__conversation } declare namespace __export__conversation__runtime { export { - ConversationMessage, - ConversationMessageContentBlock, ConversationTurnEvent, createExecutableTool, ExecutableTool, @@ -59,27 +57,6 @@ type ConversationHandlerFunctionProps = { outputStorageStrategy?: BackendOutputStorageStrategy; }; -// @public (undocumented) -type ConversationMessage = { - role: 'user' | 'assistant'; - content: Array; -}; - -// @public (undocumented) -type ConversationMessageContentBlock = bedrock.ContentBlock | { - image: Omit & { - source: { - bytes: string; - }; - }; - text?: never; - document?: never; - toolUse?: never; - toolResult?: never; - guardContent?: never; - $unknown?: never; -}; - // @public (undocumented) type ConversationTurnEvent = { conversationId: string; @@ -103,7 +80,6 @@ type ConversationTurnEvent = { request: { headers: Record; }; - messages?: Array; messageHistoryQuery: { getQueryName: string; getQueryInputTypeName: string; diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts index 54a11f1eeef..38ee05b07c2 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts @@ -108,10 +108,6 @@ export class ConversationMessageHistoryRetriever { ) {} getMessageHistory = async (): Promise> => { - if (this.event.messages?.length) { - // This is for backwards compatibility and should be removed with messages property. - return this.event.messages; - } const messages = await this.listMessages(); let currentMessage = messages.find( diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts index d30be4d7dfb..ad799fede9b 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts @@ -11,7 +11,6 @@ void describe('Conversation turn executor', () => { conversationId: 'testConversationId', currentMessageId: 'testCurrentMessageId', graphqlApiEndpoint: '', - messages: [], messageHistoryQuery: { getQueryName: '', getQueryInputTypeName: '', diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts index da49e7eb899..1cb79fe365f 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts @@ -16,7 +16,6 @@ void describe('Conversation turn response sender', () => { conversationId: 'testConversationId', currentMessageId: 'testCurrentMessageId', graphqlApiEndpoint: 'http://fake.endpoint/', - messages: [], messageHistoryQuery: { getQueryName: '', getQueryInputTypeName: '', diff --git a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.test.ts b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.test.ts index c35fa7adcc5..df63abd33d7 100644 --- a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.test.ts @@ -11,7 +11,6 @@ void describe('events tool provider', () => { conversationId: '', currentMessageId: '', graphqlApiEndpoint: '', - messages: [], messageHistoryQuery: { getQueryName: '', getQueryInputTypeName: '', @@ -73,7 +72,6 @@ void describe('events tool provider', () => { conversationId: '', currentMessageId: '', graphqlApiEndpoint: '', - messages: [], messageHistoryQuery: { getQueryName: '', getQueryInputTypeName: '', diff --git a/packages/ai-constructs/src/conversation/runtime/index.ts b/packages/ai-constructs/src/conversation/runtime/index.ts index 632d100cbfd..187d962b031 100644 --- a/packages/ai-constructs/src/conversation/runtime/index.ts +++ b/packages/ai-constructs/src/conversation/runtime/index.ts @@ -1,6 +1,4 @@ import { - ConversationMessage, - ConversationMessageContentBlock, ConversationTurnEvent, ExecutableTool, FromJSONSchema, @@ -14,8 +12,6 @@ import { handleConversationTurnEvent } from './conversation_turn_executor.js'; import { createExecutableTool } from './executable_tool_factory.js'; export { - ConversationMessage, - ConversationMessageContentBlock, ConversationTurnEvent, createExecutableTool, ExecutableTool, diff --git a/packages/ai-constructs/src/conversation/runtime/types.ts b/packages/ai-constructs/src/conversation/runtime/types.ts index 23a54bf146c..c05e8abc5c8 100644 --- a/packages/ai-constructs/src/conversation/runtime/types.ts +++ b/packages/ai-constructs/src/conversation/runtime/types.ts @@ -66,10 +66,6 @@ export type ConversationTurnEvent = { request: { headers: Record; }; - /** - * @deprecated This field is going to be removed in upcoming releases. - */ - messages?: Array; messageHistoryQuery: { getQueryName: string; getQueryInputTypeName: string; diff --git a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts index 47b51c4cb31..ea5bfb8619e 100644 --- a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts +++ b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts @@ -7,10 +7,7 @@ import { AmplifyClient } from '@aws-sdk/client-amplify'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; import { InvokeCommand, LambdaClient } from '@aws-sdk/client-lambda'; import { DeployedResourcesFinder } from '../find_deployed_resource.js'; -import { - ConversationMessage, - ConversationTurnEvent, -} from '@aws-amplify/ai-constructs/conversation/runtime'; +import { ConversationTurnEvent } from '@aws-amplify/ai-constructs/conversation/runtime'; import { randomUUID } from 'crypto'; import { generateClientConfig } from '@aws-amplify/client-config'; import { AmplifyAuthCredentialsFactory } from '../amplify_auth_credentials_factory.js'; @@ -34,6 +31,7 @@ import { } from '../test-projects/conversation-handler/amplify/constants.js'; import { resolve } from 'path'; import { fileURLToPath } from 'url'; +import * as bedrock from '@aws-sdk/client-bedrock-runtime'; // TODO: this is a work around // it seems like as of amplify v6 , some of the code only runs in the browser ... @@ -51,6 +49,28 @@ type ConversationTurnAppSyncResponse = { content: string; }; +type ConversationMessage = { + role: 'user' | 'assistant'; + content: Array; +}; + +type ConversationMessageContentBlock = + | bedrock.ContentBlock + | { + image: Omit & { + // Upstream (Appsync) may send images in a form of Base64 encoded strings + source: { bytes: string }; + }; + // These are needed so that union with other content block types works. + // See https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-bedrock-runtime/TypeAlias/ContentBlock/. + text?: never; + document?: never; + toolUse?: never; + toolResult?: never; + guardContent?: never; + $unknown?: never; + }; + type CreateConversationMessageChatInput = ConversationMessage & { conversationId: string; id: string; @@ -203,18 +223,7 @@ class ConversationHandlerTestProject extends TestProjectBase { backendId, authenticatedUserCredentials.accessToken, clientConfig.data.url, - apolloClient, - // Does not use message history lookup. - // This case should be removed when event.messages field is removed. - false - ); - - await this.assertDefaultConversationHandlerCanExecuteTurn( - backendId, - authenticatedUserCredentials.accessToken, - clientConfig.data.url, - apolloClient, - true + apolloClient ); await this.assertDefaultConversationHandlerCanExecuteTurn( @@ -222,7 +231,6 @@ class ConversationHandlerTestProject extends TestProjectBase { authenticatedUserCredentials.accessToken, clientConfig.data.url, apolloClient, - true, // Simulate eventual consistency true ); @@ -252,16 +260,7 @@ class ConversationHandlerTestProject extends TestProjectBase { backendId, authenticatedUserCredentials.accessToken, clientConfig.data.url, - apolloClient, - false - ); - - await this.assertDefaultConversationHandlerCanExecuteTurnWithImage( - backendId, - authenticatedUserCredentials.accessToken, - clientConfig.data.url, - apolloClient, - true + apolloClient ); } @@ -270,7 +269,6 @@ class ConversationHandlerTestProject extends TestProjectBase { accessToken: string, graphqlApiEndpoint: string, apolloClient: ApolloClient, - useMessageHistory: boolean, withoutMessageAvailableInTheMessageList = false ): Promise => { const defaultConversationHandlerFunction = ( @@ -303,28 +301,13 @@ class ConversationHandlerTestProject extends TestProjectBase { ...commonEventProperties, }; - if (useMessageHistory) { - if (withoutMessageAvailableInTheMessageList) { - // This tricks conversation handler to think that message is not available in the list. - // I.e. it simulates eventually consistency read at list operation where item is not yet visible. - // In this case handler should fall back to lookup by current message id. - message.conversationId = randomUUID().toString(); - } - await this.insertMessage(apolloClient, message); - } else { - event.messageHistoryQuery = { - getQueryName: '', - getQueryInputTypeName: '', - listQueryName: '', - listQueryInputTypeName: '', - }; - event.messages = [ - { - role: message.role, - content: message.content, - }, - ]; + if (withoutMessageAvailableInTheMessageList) { + // This tricks conversation handler to think that message is not available in the list. + // I.e. it simulates eventually consistency read at list operation where item is not yet visible. + // In this case handler should fall back to lookup by current message id. + message.conversationId = randomUUID().toString(); } + await this.insertMessage(apolloClient, message); const response = await this.executeConversationTurn( event, @@ -338,8 +321,7 @@ class ConversationHandlerTestProject extends TestProjectBase { backendId: BackendIdentifier, accessToken: string, graphqlApiEndpoint: string, - apolloClient: ApolloClient, - useMessageHistory: boolean + apolloClient: ApolloClient ): Promise => { const defaultConversationHandlerFunction = ( await this.resourceFinder.findByBackendIdentifier( @@ -390,22 +372,8 @@ class ConversationHandlerTestProject extends TestProjectBase { }, ...commonEventProperties, }; - if (useMessageHistory) { - await this.insertMessage(apolloClient, message); - } else { - event.messageHistoryQuery = { - getQueryName: '', - getQueryInputTypeName: '', - listQueryName: '', - listQueryInputTypeName: '', - }; - event.messages = [ - { - role: message.role, - content: message.content, - }, - ]; - } + + await this.insertMessage(apolloClient, message); const response = await this.executeConversationTurn( event, defaultConversationHandlerFunction, From b35f01d0bd7f43f89a006d8e6127cf44847d7e40 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 11 Oct 2024 14:52:01 -0700 Subject: [PATCH 043/199] detect generic CFN stack creation errors (#2108) --- .changeset/twenty-boxes-pretend.md | 5 +++++ packages/backend-deployer/src/cdk_error_mapper.test.ts | 6 ++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .changeset/twenty-boxes-pretend.md diff --git a/.changeset/twenty-boxes-pretend.md b/.changeset/twenty-boxes-pretend.md new file mode 100644 index 00000000000..86a01f84aca --- /dev/null +++ b/.changeset/twenty-boxes-pretend.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +detect generic CFN stack creation errors diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 99062785959..2976686ed18 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -139,6 +139,12 @@ const testErrorMappings = [ errorName: 'CloudFormationDeploymentError', expectedDownstreamErrorMessage: `The stack named some-stack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: The code contains one or more errors. (Service: AppSync, Status Code: 400, Request ID: 12345) (RequestToken: 123, HandlerErrorCode: GeneralServiceException), Embedded stack was not successfully updated. Currently in UPDATE_ROLLBACK_IN_PROGRESS with reason: The following resource(s) failed to create: [resource1, resource2]. [39m`, }, + { + errorMessage: `[31m some-stack failed: The stack named some-stack failed creation, it may need to be manually deleted from the AWS console: ROLLBACK_COMPLETE`, + expectedTopLevelErrorMessage: 'The CloudFormation deployment has failed.', + errorName: 'CloudFormationDeploymentError', + expectedDownstreamErrorMessage: `The stack named some-stack failed creation, it may need to be manually deleted from the AWS console: ROLLBACK_COMPLETE`, + }, { errorMessage: 'CFN error happened: Updates are not allowed for property: some property', diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index a063f1694b4..816cc4b6f18 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -284,7 +284,7 @@ export class CdkErrorMapper { { // Note that the order matters, this should be the last as it captures generic CFN error errorRegex: new RegExp( - `Deployment failed: (.*)${this.multiLineEolRegex}|The stack named (.*) failed to deploy: (.*)` + `Deployment failed: (.*)${this.multiLineEolRegex}|The stack named (.*) failed (to deploy:|creation,) (.*)` ), humanReadableErrorMessage: 'The CloudFormation deployment has failed.', resolutionMessage: From 39ea5a0ad6ac09671eecb527405bb1ee75b84a60 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 14 Oct 2024 09:46:24 -0700 Subject: [PATCH 044/199] Fix cdk tests when new dependencies are shipped to npm. (#2107) * Fix cdk tests when new dependencies are shipped to npm. * try this * try this * try this * try this --- .changeset/breezy-spiders-sip.md | 2 ++ .../cdk/create_empty_cdk_project.ts | 10 ++++++++++ .../setup_deployed_backend_client.ts | 16 ++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 .changeset/breezy-spiders-sip.md diff --git a/.changeset/breezy-spiders-sip.md b/.changeset/breezy-spiders-sip.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/breezy-spiders-sip.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/test-project-setup/cdk/create_empty_cdk_project.ts b/packages/integration-tests/src/test-project-setup/cdk/create_empty_cdk_project.ts index 67d3b677585..72db39e0feb 100644 --- a/packages/integration-tests/src/test-project-setup/cdk/create_empty_cdk_project.ts +++ b/packages/integration-tests/src/test-project-setup/cdk/create_empty_cdk_project.ts @@ -24,5 +24,15 @@ export const createEmptyCdkProject = async ( await cdkCli(['init', 'app', '--language', 'typescript'], projectRoot).run(); + // Remove local node_modules after CDK init. + // This is to make sure that test project is using same version of + // CDK and constructs as the rest of the codebase. + // Otherwise, we might get errors about incompatible classes if + // dependencies on npm are ahead of our package-lock. + await fsp.rm(path.join(projectRoot, 'node_modules'), { + recursive: true, + force: true, + }); + return { projectName, projectRoot }; }; diff --git a/packages/integration-tests/src/test-project-setup/setup_deployed_backend_client.ts b/packages/integration-tests/src/test-project-setup/setup_deployed_backend_client.ts index 618ec72b26a..66704f60cfc 100644 --- a/packages/integration-tests/src/test-project-setup/setup_deployed_backend_client.ts +++ b/packages/integration-tests/src/test-project-setup/setup_deployed_backend_client.ts @@ -1,4 +1,10 @@ import { execa } from 'execa'; +import fsp from 'fs/promises'; +import { fileURLToPath } from 'node:url'; + +const packageLockPath = fileURLToPath( + new URL('../../../../package-lock.json', import.meta.url) +); /** * Configures package.json for testing the specified project directory with the version of deployed-backend-client on npm @@ -9,4 +15,14 @@ export const setupDeployedBackendClient = async ( await execa('npm', ['install', '@aws-amplify/deployed-backend-client'], { cwd: projectRootDirPath, }); + + // Install constructs version that is matching our package lock. + // Otherwise, the test might fail due to incompatible properties + // when two definitions are present. + const packageLock = JSON.parse(await fsp.readFile(packageLockPath, 'utf-8')); + const constructsVersion = + packageLock.packages['node_modules/constructs'].version; + await execa('npm', ['install', `constructs@${constructsVersion}`], { + cwd: projectRootDirPath, + }); }; From f87cc87abebf5431ab097bbd7973c8d1a8f878ff Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Tue, 15 Oct 2024 18:24:47 +0200 Subject: [PATCH 045/199] fix: internally paginate list secret calls (#2113) --- .changeset/chilly-snakes-accept.md | 5 ++ .../backend-secret/src/ssm_secret.test.ts | 66 +++++++++++++++++++ packages/backend-secret/src/ssm_secret.ts | 39 ++++++----- 3 files changed, 93 insertions(+), 17 deletions(-) create mode 100644 .changeset/chilly-snakes-accept.md diff --git a/.changeset/chilly-snakes-accept.md b/.changeset/chilly-snakes-accept.md new file mode 100644 index 00000000000..f357b7045cb --- /dev/null +++ b/.changeset/chilly-snakes-accept.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-secret': patch +--- + +fix: internally paginate list secret calls diff --git a/packages/backend-secret/src/ssm_secret.test.ts b/packages/backend-secret/src/ssm_secret.test.ts index c33da91cc17..fbde647a4d6 100644 --- a/packages/backend-secret/src/ssm_secret.test.ts +++ b/packages/backend-secret/src/ssm_secret.test.ts @@ -1,6 +1,7 @@ import { beforeEach, describe, it, mock } from 'node:test'; import { GetParameterCommandOutput, + GetParametersByPathCommandInput, GetParametersByPathCommandOutput, InternalServerError, ParameterNotFound, @@ -306,6 +307,7 @@ void describe('SSMSecret', () => { assert.deepStrictEqual( mockGetParametersByPath.mock.calls[0].arguments[0], { + NextToken: undefined, Path: testBranchPath, WithDecryption: true, } @@ -337,6 +339,7 @@ void describe('SSMSecret', () => { assert.deepStrictEqual( mockGetParametersByPath.mock.calls[0].arguments[0], { + NextToken: undefined, Path: testSharedPath, WithDecryption: true, } @@ -344,6 +347,68 @@ void describe('SSMSecret', () => { assert.deepEqual(secrets, [testSecretListItem]); }); + void it('lists all secrets by internally paginating calls', async () => { + const mockGetParametersByPath = mock.method( + ssmClient, + 'getParametersByPath', + (input: GetParametersByPathCommandInput) => { + let nextToken: string | undefined = undefined; + if (!input.NextToken) { + nextToken = '1'; + } else if (input.NextToken === '1') { + nextToken = '2'; + } else if (input.NextToken === '2') { + nextToken = undefined; + } + return Promise.resolve({ + NextToken: nextToken, + Parameters: [ + { + Name: testSharedSecretFullNamePath.concat( + input.NextToken ?? '' + ), + Value: testSecretValue, + Version: testSecretVersion, + LastModifiedDate: testSecretLastUpdated, + }, + ], + } as GetParametersByPathCommandOutput); + } + ); + + const secrets = await ssmSecretClient.listSecrets(testBackendId); + assert.deepStrictEqual(mockGetParametersByPath.mock.calls.length, 3); + assert.deepStrictEqual( + mockGetParametersByPath.mock.calls[0].arguments[0], + { + NextToken: undefined, + Path: testSharedPath, + WithDecryption: true, + } + ); + assert.deepStrictEqual( + mockGetParametersByPath.mock.calls[1].arguments[0], + { + NextToken: '1', + Path: testSharedPath, + WithDecryption: true, + } + ); + assert.deepStrictEqual( + mockGetParametersByPath.mock.calls[2].arguments[0], + { + NextToken: '2', + Path: testSharedPath, + WithDecryption: true, + } + ); + assert.deepEqual(secrets, [ + { ...testSecretListItem, name: testSecretName }, + { ...testSecretListItem, name: testSecretName + '1' }, + { ...testSecretListItem, name: testSecretName + '2' }, + ]); + }); + void it('lists an empty list', async () => { const mockGetParametersByPath = mock.method( ssmClient, @@ -359,6 +424,7 @@ void describe('SSMSecret', () => { assert.deepStrictEqual( mockGetParametersByPath.mock.calls[0].arguments[0], { + NextToken: undefined, Path: testBranchPath, WithDecryption: true, } diff --git a/packages/backend-secret/src/ssm_secret.ts b/packages/backend-secret/src/ssm_secret.ts index c8c34219756..cd5b44563fb 100644 --- a/packages/backend-secret/src/ssm_secret.ts +++ b/packages/backend-secret/src/ssm_secret.ts @@ -68,24 +68,29 @@ export class SSMSecretClient implements SecretClient { const result: SecretListItem[] = []; try { - const resp = await this.ssmClient.getParametersByPath({ - Path: path, - WithDecryption: true, - }); + let nextToken: string | undefined; + do { + const resp = await this.ssmClient.getParametersByPath({ + Path: path, + WithDecryption: true, + NextToken: nextToken, + }); - resp.Parameters?.forEach((param) => { - if (!param.Name || !param.Value) { - return; - } - const secretName = param.Name.split('/').pop(); - if (secretName) { - result.push({ - name: secretName, - version: param.Version, - lastUpdated: param.LastModifiedDate, - }); - } - }); + resp.Parameters?.forEach((param) => { + if (!param.Name || !param.Value) { + return; + } + const secretName = param.Name.split('/').pop(); + if (secretName) { + result.push({ + name: secretName, + version: param.Version, + lastUpdated: param.LastModifiedDate, + }); + } + }); + nextToken = resp.NextToken; + } while (nextToken); return result; } catch (err) { throw SecretError.createInstance(err as Error); From fb3538c58cfb25b6ae8aa5d4036be05bf1ee2181 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 18:39:40 +0200 Subject: [PATCH 046/199] Version Packages (#2099) Co-authored-by: github-actions[bot] --- .changeset/breezy-spiders-sip.md | 2 -- .changeset/chilly-snakes-accept.md | 5 ----- .changeset/dull-mugs-agree.md | 5 ----- .changeset/early-starfishes-impress.md | 5 ----- .changeset/modern-toys-jump.md | 5 ----- .changeset/old-suns-float.md | 5 ----- .changeset/pink-rockets-dance.md | 6 ------ .changeset/twenty-boxes-pretend.md | 5 ----- packages/ai-constructs/CHANGELOG.md | 10 ++++++++++ packages/ai-constructs/package.json | 2 +- packages/backend-ai/CHANGELOG.md | 8 ++++++++ packages/backend-ai/package.json | 4 ++-- packages/backend-deployer/CHANGELOG.md | 8 ++++++++ packages/backend-deployer/package.json | 2 +- packages/backend-function/CHANGELOG.md | 6 ++++++ packages/backend-function/package.json | 2 +- packages/backend-secret/CHANGELOG.md | 6 ++++++ packages/backend-secret/package.json | 2 +- packages/backend/CHANGELOG.md | 13 +++++++++++++ packages/backend/package.json | 6 +++--- packages/integration-tests/package.json | 8 ++++---- 21 files changed, 64 insertions(+), 51 deletions(-) delete mode 100644 .changeset/breezy-spiders-sip.md delete mode 100644 .changeset/chilly-snakes-accept.md delete mode 100644 .changeset/dull-mugs-agree.md delete mode 100644 .changeset/early-starfishes-impress.md delete mode 100644 .changeset/modern-toys-jump.md delete mode 100644 .changeset/old-suns-float.md delete mode 100644 .changeset/pink-rockets-dance.md delete mode 100644 .changeset/twenty-boxes-pretend.md diff --git a/.changeset/breezy-spiders-sip.md b/.changeset/breezy-spiders-sip.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/breezy-spiders-sip.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/chilly-snakes-accept.md b/.changeset/chilly-snakes-accept.md deleted file mode 100644 index f357b7045cb..00000000000 --- a/.changeset/chilly-snakes-accept.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-secret': patch ---- - -fix: internally paginate list secret calls diff --git a/.changeset/dull-mugs-agree.md b/.changeset/dull-mugs-agree.md deleted file mode 100644 index e6b2be25bbe..00000000000 --- a/.changeset/dull-mugs-agree.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': minor ---- - -Remove deprecated messages field from event diff --git a/.changeset/early-starfishes-impress.md b/.changeset/early-starfishes-impress.md deleted file mode 100644 index 0995ac35938..00000000000 --- a/.changeset/early-starfishes-impress.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': patch ---- - -Fix case where bedrock content blocks would be populated with 'null' instead of 'undefined. diff --git a/.changeset/modern-toys-jump.md b/.changeset/modern-toys-jump.md deleted file mode 100644 index 6f13875f253..00000000000 --- a/.changeset/modern-toys-jump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -detect more generic CFN deployment failure errors diff --git a/.changeset/old-suns-float.md b/.changeset/old-suns-float.md deleted file mode 100644 index f0cdb5260bc..00000000000 --- a/.changeset/old-suns-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -detect transform errors with multiple errors diff --git a/.changeset/pink-rockets-dance.md b/.changeset/pink-rockets-dance.md deleted file mode 100644 index e0d4c607241..00000000000 --- a/.changeset/pink-rockets-dance.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/backend-function': minor -'@aws-amplify/backend': minor ---- - -Add minify option to defineFunction diff --git a/.changeset/twenty-boxes-pretend.md b/.changeset/twenty-boxes-pretend.md deleted file mode 100644 index 86a01f84aca..00000000000 --- a/.changeset/twenty-boxes-pretend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -detect generic CFN stack creation errors diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index 174ce5d2522..99b9d0dfc4d 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/ai-constructs +## 0.5.0 + +### Minor Changes + +- 46a0e85: Remove deprecated messages field from event + +### Patch Changes + +- faacd1b: Fix case where bedrock content blocks would be populated with 'null' instead of 'undefined. + ## 0.4.0 ### Minor Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index 21fbfa18c42..08a29e43f82 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "0.4.0", + "version": "0.5.0", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index 7b0d715433e..a1a93d41443 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-ai +## 0.3.1 + +### Patch Changes + +- Updated dependencies [46a0e85] +- Updated dependencies [faacd1b] + - @aws-amplify/ai-constructs@0.5.0 + ## 0.3.0 ### Minor Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 7711ba387f0..aea3b22f795 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "0.3.0", + "version": "0.3.1", "type": "module", "publishConfig": { "access": "public" @@ -22,7 +22,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.4.0", + "@aws-amplify/ai-constructs": "^0.5.0", "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.0.2", "@aws-amplify/platform-core": "^1.1.0", diff --git a/packages/backend-deployer/CHANGELOG.md b/packages/backend-deployer/CHANGELOG.md index 944794ffd45..6d5d1c843b4 100644 --- a/packages/backend-deployer/CHANGELOG.md +++ b/packages/backend-deployer/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-deployer +## 1.1.5 + +### Patch Changes + +- 93d419f: detect more generic CFN deployment failure errors +- 777c80d: detect transform errors with multiple errors +- b35f01d: detect generic CFN stack creation errors + ## 1.1.4 ### Patch Changes diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index 8e2be97e383..d78a6b7d7ae 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-deployer", - "version": "1.1.4", + "version": "1.1.5", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index 4cb76fefa59..56b83b92b67 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/backend-function +## 1.7.0 + +### Minor Changes + +- 4720412: Add minify option to defineFunction + ## 1.6.0 ### Minor Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index d4a4c3b60f6..c6266509831 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.6.0", + "version": "1.7.0", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/backend-secret/CHANGELOG.md b/packages/backend-secret/CHANGELOG.md index 495d2b9e2cf..14b518a08e7 100644 --- a/packages/backend-secret/CHANGELOG.md +++ b/packages/backend-secret/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/backend-secret +## 1.1.4 + +### Patch Changes + +- f87cc87: fix: internally paginate list secret calls + ## 1.1.3 ### Patch Changes diff --git a/packages/backend-secret/package.json b/packages/backend-secret/package.json index e5e67350907..e9a5d3da8ea 100644 --- a/packages/backend-secret/package.json +++ b/packages/backend-secret/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-secret", - "version": "1.1.3", + "version": "1.1.4", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 1a319a379fd..41bd9901fd5 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/backend +## 1.5.0 + +### Minor Changes + +- 4720412: Add minify option to defineFunction + +### Patch Changes + +- Updated dependencies [f87cc87] +- Updated dependencies [4720412] + - @aws-amplify/backend-secret@1.1.4 + - @aws-amplify/backend-function@1.7.0 + ## 1.4.0 ### Minor Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 2d477fe3ee7..979906fcce1 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.4.0", + "version": "1.5.0", "type": "module", "publishConfig": { "access": "public" @@ -27,11 +27,11 @@ "dependencies": { "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/backend-auth": "^1.2.0", - "@aws-amplify/backend-function": "^1.6.0", + "@aws-amplify/backend-function": "^1.7.0", "@aws-amplify/backend-data": "^1.1.4", "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/backend-secret": "^1.1.2", + "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.1", "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/platform-core": "^1.1.0", diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 295227183c1..f9f39d6988f 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -5,11 +5,11 @@ "type": "module", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.4.0", + "@aws-amplify/ai-constructs": "^0.5.0", "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend": "^1.4.0", - "@aws-amplify/backend-ai": "^0.3.0", - "@aws-amplify/backend-secret": "^1.1.2", + "@aws-amplify/backend": "^1.5.0", + "@aws-amplify/backend-ai": "^0.3.1", + "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/deployed-backend-client": "^1.4.1", From b6761b02b0640d2cce9c465b2d824c60f08ddebd Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 16 Oct 2024 10:24:35 -0700 Subject: [PATCH 047/199] Stream Bedrock responses (#2091) * Stream bedrock response * adjust this * this works! * api * add tests * tests * comment * comment * remove console logs --- .changeset/thirty-cheetahs-relate.md | 5 + .eslintrc.cjs | 1 + packages/ai-constructs/API.md | 1 + .../conversation_handler_construct.test.ts | 10 +- .../conversation_handler_construct.ts | 5 +- .../runtime/bedrock_converse_adapter.test.ts | 1450 +++++++++-------- .../runtime/bedrock_converse_adapter.ts | 167 ++ .../conversation_turn_executor.test.ts | 146 +- .../runtime/conversation_turn_executor.ts | 17 +- ...ion_turn_streaming_response_sender.test.ts | 72 + ...ersation_turn_streaming_response_sender.ts | 48 + .../src/conversation/runtime/types.ts | 41 + .../conversation_handler_project.ts | 368 +++-- .../conversation-handler/amplify/constants.ts | 1 - .../custom_handler.ts | 2 +- .../amplify/data/resource.ts | 24 + 16 files changed, 1600 insertions(+), 758 deletions(-) create mode 100644 .changeset/thirty-cheetahs-relate.md create mode 100644 packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.test.ts create mode 100644 packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.ts diff --git a/.changeset/thirty-cheetahs-relate.md b/.changeset/thirty-cheetahs-relate.md new file mode 100644 index 00000000000..97811042d4d --- /dev/null +++ b/.changeset/thirty-cheetahs-relate.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': minor +--- + +Stream Bedrock responses diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 05f9f0a155c..23e09de5175 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -160,6 +160,7 @@ module.exports = { }, ], 'jsdoc/require-param': 'off', + 'jsdoc/require-yields': 'off', 'jsdoc/require-returns': 'off', 'spellcheck/spell-checker': [ 'warn', diff --git a/packages/ai-constructs/API.md b/packages/ai-constructs/API.md index 0a9053d1592..26a63ca9def 100644 --- a/packages/ai-constructs/API.md +++ b/packages/ai-constructs/API.md @@ -61,6 +61,7 @@ type ConversationHandlerFunctionProps = { type ConversationTurnEvent = { conversationId: string; currentMessageId: string; + streamResponse?: boolean; responseMutation: { name: string; inputTypeName: string; diff --git a/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts b/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts index 93b87e8ea3d..bdc8f343500 100644 --- a/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts +++ b/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts @@ -82,7 +82,10 @@ void describe('Conversation Handler Function construct', () => { PolicyDocument: { Statement: [ { - Action: 'bedrock:InvokeModel', + Action: [ + 'bedrock:InvokeModel', + 'bedrock:InvokeModelWithResponseStream', + ], Effect: 'Allow', Resource: [ 'arn:aws:bedrock:region1::foundation-model/model1', @@ -115,7 +118,10 @@ void describe('Conversation Handler Function construct', () => { PolicyDocument: { Statement: [ { - Action: 'bedrock:InvokeModel', + Action: [ + 'bedrock:InvokeModel', + 'bedrock:InvokeModelWithResponseStream', + ], Effect: 'Allow', Resource: { 'Fn::Join': [ diff --git a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts index 8f3c90efbd7..f4972529a0d 100644 --- a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts +++ b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts @@ -117,7 +117,10 @@ export class ConversationHandlerFunction conversationHandler.addToRolePolicy( new PolicyStatement({ effect: Effect.ALLOW, - actions: ['bedrock:InvokeModel'], + actions: [ + 'bedrock:InvokeModel', + 'bedrock:InvokeModelWithResponseStream', + ], resources, }) ); diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts index d1c2afedbea..e75c672d5d8 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts @@ -4,15 +4,20 @@ import { ConversationMessage, ConversationTurnEvent, ExecutableTool, + StreamingResponseChunk, ToolDefinition, } from './types'; import { BedrockConverseAdapter } from './bedrock_converse_adapter'; import { BedrockRuntimeClient, + ContentBlock, ConverseCommand, ConverseCommandInput, ConverseCommandOutput, + ConverseStreamCommandOutput, + ConverseStreamOutput, Message, + StopReason, ToolConfiguration, ToolInputSchema, ToolResultContentBlock, @@ -23,8 +28,8 @@ import { ConversationMessageHistoryRetriever } from './conversation_message_hist void describe('Bedrock converse adapter', () => { const commonEvent: Readonly = { - conversationId: '', - currentMessageId: '', + conversationId: 'testConversationId', + currentMessageId: 'testCurrentMessageId', graphqlApiEndpoint: '', messageHistoryQuery: { getQueryName: '', @@ -70,315 +75,702 @@ void describe('Bedrock converse adapter', () => { } ); - void it('calls bedrock to get conversation response', async () => { - const event: ConversationTurnEvent = { - ...commonEvent, - }; + [false, true].forEach((streamResponse) => { + // This is a common set of use cases that both streaming and non-streaming version must support. + void describe(`${streamResponse ? 'with' : 'without'} streaming`, () => { + void it('calls bedrock to get conversation response', async () => { + const event: ConversationTurnEvent = { + ...commonEvent, + }; - const bedrockClient = new BedrockRuntimeClient(); - const bedrockResponse: ConverseCommandOutput = { - $metadata: {}, - metrics: undefined, - output: { - message: { - role: 'assistant', - content: [ + const bedrockClient = new BedrockRuntimeClient(); + const content = [{ text: 'block1' }, { text: 'block2' }]; + const bedrockResponse = mockBedrockResponse(content, streamResponse); + const bedrockClientSendMock = mock.method(bedrockClient, 'send', () => + Promise.resolve(bedrockResponse) + ); + + const adapter = new BedrockConverseAdapter( + event, + [], + bedrockClient, + undefined, + messageHistoryRetriever + ); + + if (streamResponse) { + const chunks: Array = + await askBedrockWithStreaming(adapter); + // Assertion below is verbose on purpose to assert that correct indexes are rendered. + // See mockConverseStreamCommandOutput below of how split chunks are mocked. + assert.deepStrictEqual(chunks, [ + { + conversationId: event.conversationId, + associatedUserMessageId: event.currentMessageId, + contentBlockText: 'b', + contentBlockIndex: 0, + contentBlockDeltaIndex: 0, + }, + { + conversationId: event.conversationId, + associatedUserMessageId: event.currentMessageId, + contentBlockText: 'lock1', + contentBlockIndex: 0, + contentBlockDeltaIndex: 1, + }, + { + conversationId: event.conversationId, + associatedUserMessageId: event.currentMessageId, + contentBlockIndex: 0, + contentBlockDoneAtIndex: 1, + }, + { + conversationId: event.conversationId, + associatedUserMessageId: event.currentMessageId, + contentBlockText: 'b', + contentBlockIndex: 1, + contentBlockDeltaIndex: 0, + }, + { + conversationId: event.conversationId, + associatedUserMessageId: event.currentMessageId, + contentBlockText: 'lock2', + contentBlockIndex: 1, + contentBlockDeltaIndex: 1, + }, + { + conversationId: event.conversationId, + associatedUserMessageId: event.currentMessageId, + contentBlockIndex: 1, + contentBlockDoneAtIndex: 1, + }, { - text: 'block1', + conversationId: event.conversationId, + associatedUserMessageId: event.currentMessageId, + contentBlockIndex: 1, + stopReason: 'end_turn', }, + ]); + } else { + const responseContent = await adapter.askBedrock(); + assert.deepStrictEqual(responseContent, content); + } + + assert.strictEqual(bedrockClientSendMock.mock.calls.length, 1); + const bedrockRequest = bedrockClientSendMock.mock.calls[0] + .arguments[0] as unknown as ConverseCommand; + const expectedBedrockInput: ConverseCommandInput = { + messages: messages as Array, + modelId: event.modelConfiguration.modelId, + inferenceConfig: event.modelConfiguration.inferenceConfiguration, + system: [ { - text: 'block2', + text: event.modelConfiguration.systemPrompt, }, ], - }, - }, - stopReason: 'end_turn', - usage: undefined, - }; - const bedrockClientSendMock = mock.method(bedrockClient, 'send', () => - Promise.resolve(bedrockResponse) - ); + toolConfig: undefined, + }; + assert.deepStrictEqual(bedrockRequest.input, expectedBedrockInput); + }); - const responseContent = await new BedrockConverseAdapter( - event, - [], - bedrockClient, - undefined, - messageHistoryRetriever - ).askBedrock(); + void it('uses executable tools while calling bedrock', async () => { + const additionalToolOutput: ToolResultContentBlock = { + text: 'additionalToolOutput', + }; + const additionalTool: ExecutableTool = { + name: 'additionalTool', + description: 'additional tool description', + inputSchema: { + json: { + required: ['additionalToolRequiredProperty'], + }, + }, + execute: () => Promise.resolve(additionalToolOutput), + }; + const eventToolOutput: ToolResultContentBlock = { + text: 'eventToolOutput', + }; + const eventTool: ExecutableTool = { + name: 'eventTool', + description: 'event tool description', + inputSchema: { + json: { + required: ['eventToolRequiredProperty'], + }, + }, + execute: () => Promise.resolve(eventToolOutput), + }; - assert.deepStrictEqual( - responseContent, - bedrockResponse.output?.message?.content - ); + const event: ConversationTurnEvent = { + ...commonEvent, + }; - assert.strictEqual(bedrockClientSendMock.mock.calls.length, 1); - const bedrockRequest = bedrockClientSendMock.mock.calls[0] - .arguments[0] as unknown as ConverseCommand; - const expectedBedrockInput: ConverseCommandInput = { - messages: messages as Array, - modelId: event.modelConfiguration.modelId, - inferenceConfig: event.modelConfiguration.inferenceConfiguration, - system: [ - { - text: event.modelConfiguration.systemPrompt, - }, - ], - toolConfig: undefined, - }; - assert.deepStrictEqual(bedrockRequest.input, expectedBedrockInput); - }); + const bedrockClient = new BedrockRuntimeClient(); + const bedrockResponseQueue: Array< + ConverseCommandOutput | ConverseStreamCommandOutput + > = []; + const additionalToolUse1 = { + toolUseId: randomUUID().toString(), + name: additionalTool.name, + input: 'additionalToolInput1', + }; + const additionalToolUse2 = { + toolUseId: randomUUID().toString(), + name: additionalTool.name, + input: 'additionalToolInput2', + }; + const additionalToolUseBedrockResponse = mockBedrockResponse( + [ + { + toolUse: additionalToolUse1, + }, + { + toolUse: additionalToolUse2, + }, + ], + streamResponse + ); + bedrockResponseQueue.push(additionalToolUseBedrockResponse); + const eventToolUse1 = { + toolUseId: randomUUID().toString(), + name: eventTool.name, + input: 'eventToolInput1', + }; + const eventToolUse2 = { + toolUseId: randomUUID().toString(), + name: eventTool.name, + input: 'eventToolInput2', + }; + const eventToolUseBedrockResponse = mockBedrockResponse( + [ + { + toolUse: eventToolUse1, + }, + { + toolUse: eventToolUse2, + }, + ], + streamResponse + ); + bedrockResponseQueue.push(eventToolUseBedrockResponse); + const content = [ + { + text: 'finalResponse', + }, + ]; + const finalBedrockResponse = mockBedrockResponse( + content, + streamResponse + ); + bedrockResponseQueue.push(finalBedrockResponse); - void it('uses executable tools while calling bedrock', async () => { - const additionalToolOutput: ToolResultContentBlock = { - text: 'additionalToolOutput', - }; - const additionalTool: ExecutableTool = { - name: 'additionalTool', - description: 'additional tool description', - inputSchema: { - json: { - required: ['additionalToolRequiredProperty'], - }, - }, - execute: () => Promise.resolve(additionalToolOutput), - }; - const eventToolOutput: ToolResultContentBlock = { - text: 'eventToolOutput', - }; - const eventTool: ExecutableTool = { - name: 'eventTool', - description: 'event tool description', - inputSchema: { - json: { - required: ['eventToolRequiredProperty'], - }, - }, - execute: () => Promise.resolve(eventToolOutput), - }; + const bedrockClientSendMock = mock.method(bedrockClient, 'send', () => + Promise.resolve(bedrockResponseQueue.shift()) + ); - const event: ConversationTurnEvent = { - ...commonEvent, - }; + const eventToolsProvider = new ConversationTurnEventToolsProvider( + event + ); + mock.method(eventToolsProvider, 'getEventTools', () => [eventTool]); - const bedrockClient = new BedrockRuntimeClient(); - const bedrockResponseQueue: Array = []; - const additionalToolUseBedrockResponse: ConverseCommandOutput = { - $metadata: {}, - metrics: undefined, - output: { - message: { - role: 'assistant', - content: [ + const adapter = new BedrockConverseAdapter( + event, + [additionalTool], + bedrockClient, + eventToolsProvider, + messageHistoryRetriever + ); + if (streamResponse) { + const chunks: Array = + await askBedrockWithStreaming(adapter); + const responseText = chunks.reduce((acc, next) => { + if (next.contentBlockText) { + acc += next.contentBlockText; + } + return acc; + }, ''); + assert.strictEqual(responseText, 'finalResponse'); + } else { + const responseContent = await adapter.askBedrock(); + assert.deepStrictEqual(responseContent, content); + } + + assert.strictEqual(bedrockClientSendMock.mock.calls.length, 3); + const expectedToolConfig: ToolConfiguration = { + tools: [ { - toolUse: { - toolUseId: randomUUID().toString(), - name: additionalTool.name, - input: 'additionalToolInput1', + toolSpec: { + name: eventTool.name, + description: eventTool.description, + inputSchema: eventTool.inputSchema as ToolInputSchema, }, }, { - toolUse: { - toolUseId: randomUUID().toString(), + toolSpec: { name: additionalTool.name, - input: 'additionalToolInput2', + description: additionalTool.description, + inputSchema: additionalTool.inputSchema as ToolInputSchema, }, }, ], - }, - }, - stopReason: 'tool_use', - usage: undefined, - }; - bedrockResponseQueue.push(additionalToolUseBedrockResponse); - const eventToolUseBedrockResponse: ConverseCommandOutput = { - $metadata: {}, - metrics: undefined, - output: { - message: { - role: 'assistant', - content: [ + }; + const expectedBedrockInputCommonProperties = { + modelId: event.modelConfiguration.modelId, + inferenceConfig: event.modelConfiguration.inferenceConfiguration, + system: [ { - toolUse: { - toolUseId: randomUUID().toString(), - name: eventTool.name, - input: 'eventToolToolInput1', - }, + text: event.modelConfiguration.systemPrompt, }, + ], + toolConfig: expectedToolConfig, + }; + const bedrockRequest1 = bedrockClientSendMock.mock.calls[0] + .arguments[0] as unknown as ConverseCommand; + const expectedBedrockInput1: ConverseCommandInput = { + messages: messages as Array, + ...expectedBedrockInputCommonProperties, + }; + assert.deepStrictEqual(bedrockRequest1.input, expectedBedrockInput1); + const bedrockRequest2 = bedrockClientSendMock.mock.calls[1] + .arguments[0] as unknown as ConverseCommand; + const expectedBedrockInput2: ConverseCommandInput = { + messages: [ + ...(messages as Array), { - toolUse: { - toolUseId: randomUUID().toString(), - name: eventTool.name, - input: 'eventToolToolInput2', - }, + role: 'assistant', + content: [ + { toolUse: additionalToolUse1 }, + { toolUse: additionalToolUse2 }, + ], + }, + { + role: 'user', + content: [ + { + toolResult: { + content: [additionalToolOutput], + status: 'success', + toolUseId: additionalToolUse1.toolUseId, + }, + }, + { + toolResult: { + content: [additionalToolOutput], + status: 'success', + toolUseId: additionalToolUse2.toolUseId, + }, + }, + ], }, ], - }, - }, - stopReason: 'tool_use', - usage: undefined, - }; - bedrockResponseQueue.push(eventToolUseBedrockResponse); - const finalBedrockResponse: ConverseCommandOutput = { - $metadata: {}, - metrics: undefined, - output: { - message: { - role: 'assistant', - content: [ + ...expectedBedrockInputCommonProperties, + }; + assert.deepStrictEqual(bedrockRequest2.input, expectedBedrockInput2); + const bedrockRequest3 = bedrockClientSendMock.mock.calls[2] + .arguments[0] as unknown as ConverseCommand; + assert.ok(expectedBedrockInput2.messages); + const expectedBedrockInput3: ConverseCommandInput = { + messages: [ + ...expectedBedrockInput2.messages, { - text: 'block1', + role: 'assistant', + content: [{ toolUse: eventToolUse1 }, { toolUse: eventToolUse2 }], }, { - text: 'block2', + role: 'user', + content: [ + { + toolResult: { + content: [eventToolOutput], + status: 'success', + toolUseId: eventToolUse1.toolUseId, + }, + }, + { + toolResult: { + content: [eventToolOutput], + status: 'success', + toolUseId: eventToolUse2.toolUseId, + }, + }, + ], }, ], - }, - }, - stopReason: 'end_turn', - usage: undefined, - }; - bedrockResponseQueue.push(finalBedrockResponse); + ...expectedBedrockInputCommonProperties, + }; + assert.deepStrictEqual(bedrockRequest3.input, expectedBedrockInput3); + }); - const bedrockClientSendMock = mock.method(bedrockClient, 'send', () => - Promise.resolve(bedrockResponseQueue.shift()) - ); + void it('executable tool error is reported to bedrock', async () => { + const tool: ExecutableTool = { + name: 'testTool', + description: 'tool description', + inputSchema: { + json: {}, + }, + execute: () => Promise.reject(new Error('Test tool error')), + }; - const eventToolsProvider = new ConversationTurnEventToolsProvider(event); - mock.method(eventToolsProvider, 'getEventTools', () => [eventTool]); + const event: ConversationTurnEvent = { + ...commonEvent, + }; - const responseContent = await new BedrockConverseAdapter( - event, - [additionalTool], - bedrockClient, - eventToolsProvider, - messageHistoryRetriever - ).askBedrock(); + const bedrockClient = new BedrockRuntimeClient(); + const bedrockResponseQueue: Array< + ConverseCommandOutput | ConverseStreamCommandOutput + > = []; + const toolUse = { + toolUseId: randomUUID().toString(), + name: tool.name, + input: 'testTool', + }; + const toolUseBedrockResponse = mockBedrockResponse( + [ + { + toolUse, + }, + ], + streamResponse + ); + bedrockResponseQueue.push(toolUseBedrockResponse); + const content = [{ text: 'finalResponse' }]; + const finalBedrockResponse = mockBedrockResponse( + content, + streamResponse + ); + bedrockResponseQueue.push(finalBedrockResponse); - assert.deepStrictEqual( - responseContent, - finalBedrockResponse.output?.message?.content - ); + const bedrockClientSendMock = mock.method(bedrockClient, 'send', () => + Promise.resolve(bedrockResponseQueue.shift()) + ); - assert.strictEqual(bedrockClientSendMock.mock.calls.length, 3); - const expectedToolConfig: ToolConfiguration = { - tools: [ - { - toolSpec: { - name: eventTool.name, - description: eventTool.description, - inputSchema: eventTool.inputSchema as ToolInputSchema, - }, - }, - { - toolSpec: { - name: additionalTool.name, - description: additionalTool.description, - inputSchema: additionalTool.inputSchema as ToolInputSchema, - }, - }, - ], - }; - const expectedBedrockInputCommonProperties = { - modelId: event.modelConfiguration.modelId, - inferenceConfig: event.modelConfiguration.inferenceConfiguration, - system: [ - { - text: event.modelConfiguration.systemPrompt, - }, - ], - toolConfig: expectedToolConfig, - }; - const bedrockRequest1 = bedrockClientSendMock.mock.calls[0] - .arguments[0] as unknown as ConverseCommand; - const expectedBedrockInput1: ConverseCommandInput = { - messages: messages as Array, - ...expectedBedrockInputCommonProperties, - }; - assert.deepStrictEqual(bedrockRequest1.input, expectedBedrockInput1); - const bedrockRequest2 = bedrockClientSendMock.mock.calls[1] - .arguments[0] as unknown as ConverseCommand; - assert.ok(additionalToolUseBedrockResponse.output?.message?.content); - assert.ok( - additionalToolUseBedrockResponse.output?.message?.content[0].toolUse - ?.toolUseId - ); - assert.ok( - additionalToolUseBedrockResponse.output?.message?.content[1].toolUse - ?.toolUseId - ); - const expectedBedrockInput2: ConverseCommandInput = { - messages: [ - ...(messages as Array), - additionalToolUseBedrockResponse.output?.message, - { + const adapter = new BedrockConverseAdapter( + event, + [tool], + bedrockClient, + undefined, + messageHistoryRetriever + ); + if (streamResponse) { + const chunks: Array = + await askBedrockWithStreaming(adapter); + const responseText = chunks.reduce((acc, next) => { + if (next.contentBlockText) { + acc += next.contentBlockText; + } + return acc; + }, ''); + assert.strictEqual(responseText, 'finalResponse'); + } else { + const responseContent = await adapter.askBedrock(); + assert.deepStrictEqual(responseContent, content); + } + + assert.strictEqual(bedrockClientSendMock.mock.calls.length, 2); + const bedrockRequest2 = bedrockClientSendMock.mock.calls[1] + .arguments[0] as unknown as ConverseCommand; + assert.deepStrictEqual(bedrockRequest2.input.messages?.pop(), { role: 'user', content: [ { toolResult: { - content: [additionalToolOutput], - status: 'success', - toolUseId: - additionalToolUseBedrockResponse.output?.message.content[0] - .toolUse.toolUseId, + content: [ + { + text: 'Error: Test tool error', + }, + ], + status: 'error', + toolUseId: toolUse.toolUseId, }, }, + ], + } as Message); + }); + + void it('executable tool error of unknown type is reported to bedrock', async () => { + const tool: ExecutableTool = { + name: 'testTool', + description: 'tool description', + inputSchema: { + json: {}, + }, + // This is intentional to cover logical branch that test for error type. + // eslint-disable-next-line prefer-promise-reject-errors + execute: () => Promise.reject('Test tool error'), + }; + + const event: ConversationTurnEvent = { + ...commonEvent, + }; + + const bedrockClient = new BedrockRuntimeClient(); + const bedrockResponseQueue: Array< + ConverseCommandOutput | ConverseStreamCommandOutput + > = []; + const toolUse = { + toolUseId: randomUUID().toString(), + name: tool.name, + input: 'testTool', + }; + const toolUseBedrockResponse = mockBedrockResponse( + [ { - toolResult: { - content: [additionalToolOutput], - status: 'success', - toolUseId: - additionalToolUseBedrockResponse.output?.message.content[1] - .toolUse.toolUseId, - }, + toolUse, }, ], - }, - ], - ...expectedBedrockInputCommonProperties, - }; - assert.deepStrictEqual(bedrockRequest2.input, expectedBedrockInput2); - const bedrockRequest3 = bedrockClientSendMock.mock.calls[2] - .arguments[0] as unknown as ConverseCommand; - assert.ok(eventToolUseBedrockResponse.output?.message?.content); - assert.ok( - eventToolUseBedrockResponse.output?.message?.content[0].toolUse?.toolUseId - ); - assert.ok( - eventToolUseBedrockResponse.output?.message?.content[1].toolUse?.toolUseId - ); - assert.ok(expectedBedrockInput2.messages); - const expectedBedrockInput3: ConverseCommandInput = { - messages: [ - ...expectedBedrockInput2.messages, - eventToolUseBedrockResponse.output?.message, - { + streamResponse + ); + bedrockResponseQueue.push(toolUseBedrockResponse); + const content = [{ text: 'finalResponse' }]; + const finalBedrockResponse = mockBedrockResponse( + content, + streamResponse + ); + bedrockResponseQueue.push(finalBedrockResponse); + + const bedrockClientSendMock = mock.method(bedrockClient, 'send', () => + Promise.resolve(bedrockResponseQueue.shift()) + ); + + const adapter = new BedrockConverseAdapter( + event, + [tool], + bedrockClient, + undefined, + messageHistoryRetriever + ); + if (streamResponse) { + const chunks: Array = + await askBedrockWithStreaming(adapter); + const responseText = chunks.reduce((acc, next) => { + if (next.contentBlockText) { + acc += next.contentBlockText; + } + return acc; + }, ''); + assert.strictEqual(responseText, 'finalResponse'); + } else { + const responseContent = await adapter.askBedrock(); + assert.deepStrictEqual(responseContent, content); + } + + assert.strictEqual(bedrockClientSendMock.mock.calls.length, 2); + const bedrockRequest2 = bedrockClientSendMock.mock.calls[1] + .arguments[0] as unknown as ConverseCommand; + assert.deepStrictEqual(bedrockRequest2.input.messages?.pop(), { role: 'user', content: [ { toolResult: { - content: [eventToolOutput], - status: 'success', - toolUseId: - eventToolUseBedrockResponse.output?.message.content[0].toolUse - .toolUseId, + content: [ + { + text: 'unknown error occurred', + }, + ], + status: 'error', + toolUseId: toolUse.toolUseId, }, }, + ], + } as Message); + }); + + void it('returns client tool input block when client tool is requested and ignores executable tools', async () => { + const additionalToolOutput: ToolResultContentBlock = { + text: 'additionalToolOutput', + }; + const additionalTool: ExecutableTool = { + name: 'additionalTool', + description: 'additional tool description', + inputSchema: { + json: { + required: ['additionalToolRequiredProperty'], + }, + }, + execute: () => Promise.resolve(additionalToolOutput), + }; + const clientTool: ToolDefinition = { + name: 'clientTool', + description: 'client tool description', + inputSchema: { + json: { + required: ['clientToolRequiredProperty'], + }, + }, + }; + + const event: ConversationTurnEvent = { + ...commonEvent, + toolsConfiguration: { + clientTools: [clientTool], + }, + }; + + const bedrockClient = new BedrockRuntimeClient(); + const bedrockResponseQueue: Array< + ConverseCommandOutput | ConverseStreamCommandOutput + > = []; + const additionalToolUse = { + toolUseId: randomUUID().toString(), + name: additionalTool.name, + input: 'additionalToolInput', + }; + const clientToolUse = { + toolUseId: randomUUID().toString(), + name: clientTool.name, + input: 'clientToolInput', + }; + const toolUseBedrockResponse = mockBedrockResponse( + [ { - toolResult: { - content: [eventToolOutput], - status: 'success', - toolUseId: - eventToolUseBedrockResponse.output?.message.content[1].toolUse - .toolUseId, + toolUse: additionalToolUse, + }, + { toolUse: clientToolUse }, + ], + streamResponse + ); + bedrockResponseQueue.push(toolUseBedrockResponse); + + const bedrockClientSendMock = mock.method(bedrockClient, 'send', () => + Promise.resolve(bedrockResponseQueue.shift()) + ); + + const adapter = new BedrockConverseAdapter( + event, + [additionalTool], + bedrockClient, + undefined, + messageHistoryRetriever + ); + + if (streamResponse) { + const chunks: Array = + await askBedrockWithStreaming(adapter); + assert.deepStrictEqual(chunks, [ + { + conversationId: event.conversationId, + associatedUserMessageId: event.currentMessageId, + contentBlockIndex: 0, + contentBlockToolUse: JSON.stringify({ toolUse: clientToolUse }), + }, + { + conversationId: event.conversationId, + associatedUserMessageId: event.currentMessageId, + contentBlockIndex: 0, + stopReason: 'tool_use', + }, + ]); + } else { + const responseContent = await adapter.askBedrock(); + assert.deepStrictEqual(responseContent, [ + { + toolUse: clientToolUse, + }, + ]); + } + + assert.strictEqual(bedrockClientSendMock.mock.calls.length, 1); + const expectedToolConfig: ToolConfiguration = { + tools: [ + { + toolSpec: { + name: additionalTool.name, + description: additionalTool.description, + inputSchema: additionalTool.inputSchema as ToolInputSchema, + }, + }, + { + toolSpec: { + name: clientTool.name, + description: clientTool.description, + inputSchema: clientTool.inputSchema as ToolInputSchema, }, }, ], - }, - ], - ...expectedBedrockInputCommonProperties, - }; - assert.deepStrictEqual(bedrockRequest3.input, expectedBedrockInput3); + }; + const expectedBedrockInputCommonProperties = { + modelId: event.modelConfiguration.modelId, + inferenceConfig: event.modelConfiguration.inferenceConfiguration, + system: [ + { + text: event.modelConfiguration.systemPrompt, + }, + ], + toolConfig: expectedToolConfig, + }; + const bedrockRequest = bedrockClientSendMock.mock.calls[0] + .arguments[0] as unknown as ConverseCommand; + const expectedBedrockInput: ConverseCommandInput = { + messages: messages as Array, + ...expectedBedrockInputCommonProperties, + }; + assert.deepStrictEqual(bedrockRequest.input, expectedBedrockInput); + }); + + void it('decodes base64 encoded images', async () => { + const event: ConversationTurnEvent = { + ...commonEvent, + }; + + const fakeImagePayload = randomBytes(32); + + messageHistoryRetrieverMockGetEventMessages.mock.mockImplementationOnce( + () => { + return Promise.resolve([ + { + id: '', + conversationId: '', + role: 'user', + content: [ + { + image: { + format: 'png', + source: { + bytes: fakeImagePayload.toString('base64'), + }, + }, + }, + ], + }, + ]); + } + ); + + const bedrockClient = new BedrockRuntimeClient(); + const content = [{ text: 'block1' }, { text: 'block2' }]; + const bedrockResponse = mockBedrockResponse(content, streamResponse); + const bedrockClientSendMock = mock.method(bedrockClient, 'send', () => + Promise.resolve(bedrockResponse) + ); + + await new BedrockConverseAdapter( + event, + [], + bedrockClient, + undefined, + messageHistoryRetriever + ).askBedrock(); + + assert.strictEqual(bedrockClientSendMock.mock.calls.length, 1); + const bedrockRequest = bedrockClientSendMock.mock.calls[0] + .arguments[0] as unknown as ConverseCommand; + assert.deepStrictEqual(bedrockRequest.input.messages, [ + { + role: 'user', + content: [ + { + image: { + format: 'png', + source: { + bytes: fakeImagePayload, + }, + }, + }, + ], + }, + ]); + }); + }); }); void it('throws if tool is duplicated', () => { @@ -451,396 +843,6 @@ void describe('Bedrock converse adapter', () => { ); }); - void it('executable tool error is reported to bedrock', async () => { - const tool: ExecutableTool = { - name: 'testTool', - description: 'tool description', - inputSchema: { - json: {}, - }, - execute: () => Promise.reject(new Error('Test tool error')), - }; - - const event: ConversationTurnEvent = { - ...commonEvent, - }; - - const bedrockClient = new BedrockRuntimeClient(); - const bedrockResponseQueue: Array = []; - const toolUseBedrockResponse: ConverseCommandOutput = { - $metadata: {}, - metrics: undefined, - output: { - message: { - role: 'assistant', - content: [ - { - toolUse: { - toolUseId: randomUUID().toString(), - name: tool.name, - input: 'testTool', - }, - }, - ], - }, - }, - stopReason: 'tool_use', - usage: undefined, - }; - bedrockResponseQueue.push(toolUseBedrockResponse); - const finalBedrockResponse: ConverseCommandOutput = { - $metadata: {}, - metrics: undefined, - output: { - message: { - role: 'assistant', - content: [ - { - text: 'finalResponse', - }, - ], - }, - }, - stopReason: 'end_turn', - usage: undefined, - }; - bedrockResponseQueue.push(finalBedrockResponse); - - const bedrockClientSendMock = mock.method(bedrockClient, 'send', () => - Promise.resolve(bedrockResponseQueue.shift()) - ); - - const responseContent = await new BedrockConverseAdapter( - event, - [tool], - bedrockClient, - undefined, - messageHistoryRetriever - ).askBedrock(); - - assert.deepStrictEqual( - responseContent, - finalBedrockResponse.output?.message?.content - ); - - assert.strictEqual(bedrockClientSendMock.mock.calls.length, 2); - const bedrockRequest2 = bedrockClientSendMock.mock.calls[1] - .arguments[0] as unknown as ConverseCommand; - assert.ok(toolUseBedrockResponse.output?.message?.content); - assert.deepStrictEqual(bedrockRequest2.input.messages?.pop(), { - role: 'user', - content: [ - { - toolResult: { - content: [ - { - text: 'Error: Test tool error', - }, - ], - status: 'error', - toolUseId: - toolUseBedrockResponse.output?.message.content[0].toolUse - ?.toolUseId, - }, - }, - ], - } as Message); - }); - - void it('executable tool error of unknown type is reported to bedrock', async () => { - const tool: ExecutableTool = { - name: 'testTool', - description: 'tool description', - inputSchema: { - json: {}, - }, - // This is intentional to cover logical branch that test for error type. - // eslint-disable-next-line prefer-promise-reject-errors - execute: () => Promise.reject('Test tool error'), - }; - - const event: ConversationTurnEvent = { - ...commonEvent, - }; - - const bedrockClient = new BedrockRuntimeClient(); - const bedrockResponseQueue: Array = []; - const toolUseBedrockResponse: ConverseCommandOutput = { - $metadata: {}, - metrics: undefined, - output: { - message: { - role: 'assistant', - content: [ - { - toolUse: { - toolUseId: randomUUID().toString(), - name: tool.name, - input: 'testTool', - }, - }, - ], - }, - }, - stopReason: 'tool_use', - usage: undefined, - }; - bedrockResponseQueue.push(toolUseBedrockResponse); - const finalBedrockResponse: ConverseCommandOutput = { - $metadata: {}, - metrics: undefined, - output: { - message: { - role: 'assistant', - content: [ - { - text: 'finalResponse', - }, - ], - }, - }, - stopReason: 'end_turn', - usage: undefined, - }; - bedrockResponseQueue.push(finalBedrockResponse); - - const bedrockClientSendMock = mock.method(bedrockClient, 'send', () => - Promise.resolve(bedrockResponseQueue.shift()) - ); - - const responseContent = await new BedrockConverseAdapter( - event, - [tool], - bedrockClient, - undefined, - messageHistoryRetriever - ).askBedrock(); - - assert.deepStrictEqual( - responseContent, - finalBedrockResponse.output?.message?.content - ); - - assert.strictEqual(bedrockClientSendMock.mock.calls.length, 2); - const bedrockRequest2 = bedrockClientSendMock.mock.calls[1] - .arguments[0] as unknown as ConverseCommand; - assert.ok(toolUseBedrockResponse.output?.message?.content); - assert.deepStrictEqual(bedrockRequest2.input.messages?.pop(), { - role: 'user', - content: [ - { - toolResult: { - content: [ - { - text: 'unknown error occurred', - }, - ], - status: 'error', - toolUseId: - toolUseBedrockResponse.output?.message.content[0].toolUse - ?.toolUseId, - }, - }, - ], - } as Message); - }); - - void it('returns client tool input block when client tool is requested and ignores executable tools', async () => { - const additionalToolOutput: ToolResultContentBlock = { - text: 'additionalToolOutput', - }; - const additionalTool: ExecutableTool = { - name: 'additionalTool', - description: 'additional tool description', - inputSchema: { - json: { - required: ['additionalToolRequiredProperty'], - }, - }, - execute: () => Promise.resolve(additionalToolOutput), - }; - const clientTool: ToolDefinition = { - name: 'clientTool', - description: 'client tool description', - inputSchema: { - json: { - required: ['clientToolRequiredProperty'], - }, - }, - }; - - const event: ConversationTurnEvent = { - ...commonEvent, - toolsConfiguration: { - clientTools: [clientTool], - }, - }; - - const bedrockClient = new BedrockRuntimeClient(); - const bedrockResponseQueue: Array = []; - const clientToolUseBlock = { - toolUse: { - toolUseId: randomUUID().toString(), - name: clientTool.name, - input: 'clientToolInput', - }, - }; - const toolUseBedrockResponse: ConverseCommandOutput = { - $metadata: {}, - metrics: undefined, - output: { - message: { - role: 'assistant', - content: [ - { - toolUse: { - toolUseId: randomUUID().toString(), - name: additionalTool.name, - input: 'additionalToolInput', - }, - }, - clientToolUseBlock, - ], - }, - }, - stopReason: 'tool_use', - usage: undefined, - }; - bedrockResponseQueue.push(toolUseBedrockResponse); - - const bedrockClientSendMock = mock.method(bedrockClient, 'send', () => - Promise.resolve(bedrockResponseQueue.shift()) - ); - - const responseContent = await new BedrockConverseAdapter( - event, - [additionalTool], - bedrockClient, - undefined, - messageHistoryRetriever - ).askBedrock(); - - assert.deepStrictEqual(responseContent, [clientToolUseBlock]); - - assert.strictEqual(bedrockClientSendMock.mock.calls.length, 1); - const expectedToolConfig: ToolConfiguration = { - tools: [ - { - toolSpec: { - name: additionalTool.name, - description: additionalTool.description, - inputSchema: additionalTool.inputSchema as ToolInputSchema, - }, - }, - { - toolSpec: { - name: clientTool.name, - description: clientTool.description, - inputSchema: clientTool.inputSchema as ToolInputSchema, - }, - }, - ], - }; - const expectedBedrockInputCommonProperties = { - modelId: event.modelConfiguration.modelId, - inferenceConfig: event.modelConfiguration.inferenceConfiguration, - system: [ - { - text: event.modelConfiguration.systemPrompt, - }, - ], - toolConfig: expectedToolConfig, - }; - const bedrockRequest = bedrockClientSendMock.mock.calls[0] - .arguments[0] as unknown as ConverseCommand; - const expectedBedrockInput: ConverseCommandInput = { - messages: messages as Array, - ...expectedBedrockInputCommonProperties, - }; - assert.deepStrictEqual(bedrockRequest.input, expectedBedrockInput); - }); - - void it('decodes base64 encoded images', async () => { - const event: ConversationTurnEvent = { - ...commonEvent, - }; - - const fakeImagePayload = randomBytes(32); - - messageHistoryRetrieverMockGetEventMessages.mock.mockImplementationOnce( - () => { - return Promise.resolve([ - { - id: '', - conversationId: '', - role: 'user', - content: [ - { - image: { - format: 'png', - source: { - bytes: fakeImagePayload.toString('base64'), - }, - }, - }, - ], - }, - ]); - } - ); - - const bedrockClient = new BedrockRuntimeClient(); - const bedrockResponse: ConverseCommandOutput = { - $metadata: {}, - metrics: undefined, - output: { - message: { - role: 'assistant', - content: [ - { - text: 'block1', - }, - { - text: 'block2', - }, - ], - }, - }, - stopReason: 'end_turn', - usage: undefined, - }; - const bedrockClientSendMock = mock.method(bedrockClient, 'send', () => - Promise.resolve(bedrockResponse) - ); - - await new BedrockConverseAdapter( - event, - [], - bedrockClient, - undefined, - messageHistoryRetriever - ).askBedrock(); - - assert.strictEqual(bedrockClientSendMock.mock.calls.length, 1); - const bedrockRequest = bedrockClientSendMock.mock.calls[0] - .arguments[0] as unknown as ConverseCommand; - assert.deepStrictEqual(bedrockRequest.input.messages, [ - { - role: 'user', - content: [ - { - image: { - format: 'png', - source: { - bytes: fakeImagePayload, - }, - }, - }, - ], - }, - ]); - }); - void it('adds user agent middleware', async () => { const event: ConversationTurnEvent = { ...commonEvent, @@ -880,3 +882,157 @@ void describe('Bedrock converse adapter', () => { ); }); }); + +const askBedrockWithStreaming = async ( + adapter: BedrockConverseAdapter +): Promise> => { + const chunks: Array = []; + for await (const chunk of adapter.askBedrockStreaming()) { + chunks.push(chunk); + } + return chunks; +}; + +const mockBedrockResponse = ( + contentBlocks: + | Array + | Array, + streamResponse: boolean +): ConverseStreamCommandOutput | ConverseCommandOutput => { + if (streamResponse) { + return mockConverseStreamCommandOutput(contentBlocks); + } + return mockConverseCommandOutput(contentBlocks); +}; +const mockConverseCommandOutput = ( + contentBlocks: + | Array + | Array +): ConverseCommandOutput => { + let stopReason: StopReason = 'end_turn'; + if (contentBlocks.find((block) => block.toolUse)) { + stopReason = 'tool_use'; + } + return { + $metadata: {}, + metrics: undefined, + output: { + message: { + role: 'assistant', + content: contentBlocks, + }, + }, + stopReason, + usage: undefined, + }; +}; + +const mockConverseStreamCommandOutput = ( + contentBlocks: + | Array + | Array +): ConverseStreamCommandOutput => { + const streamItems: Array = []; + let stopReason: StopReason | undefined; + streamItems.push({ + messageStart: { + role: 'assistant', + }, + }); + for (let i = 0; i < contentBlocks.length; i++) { + const block = contentBlocks[i]; + if (block.toolUse) { + stopReason = 'tool_use'; + streamItems.push({ + contentBlockStart: { + contentBlockIndex: i, + start: { + toolUse: { + toolUseId: block.toolUse.toolUseId, + name: block.toolUse.name, + }, + }, + }, + }); + const input = JSON.stringify(block.toolUse.input); + streamItems.push({ + contentBlockDelta: { + contentBlockIndex: i, + delta: { + toolUse: { + // simulate chunked input + input: input.substring(0, 1), + }, + }, + }, + }); + if (input.length > 1) { + streamItems.push({ + contentBlockDelta: { + contentBlockIndex: i, + delta: { + toolUse: { + // simulate chunked input + input: input.substring(1), + }, + }, + }, + }); + } + streamItems.push({ + contentBlockStop: { + contentBlockIndex: i, + }, + }); + } else if (block.text) { + stopReason = 'end_turn'; + streamItems.push({ + contentBlockStart: { + contentBlockIndex: i, + start: undefined, + }, + }); + const input = block.text; + streamItems.push({ + contentBlockDelta: { + contentBlockIndex: i, + delta: { + // simulate chunked input + text: input.substring(0, 1), + }, + }, + }); + if (input.length > 1) { + streamItems.push({ + contentBlockDelta: { + contentBlockIndex: i, + delta: { + // simulate chunked input + text: input.substring(1), + }, + }, + }); + } + streamItems.push({ + contentBlockStop: { + contentBlockIndex: i, + }, + }); + } else { + throw new Error('Unsupported block type'); + } + } + streamItems.push({ + messageStop: { + stopReason: stopReason, + }, + }); + return { + $metadata: {}, + stream: (async function* (): AsyncGenerator { + for (const streamItem of streamItems) { + yield streamItem; + } + })(), + }; +}; diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts index 2a8bc77bfc9..d4da37d4b8f 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts @@ -4,6 +4,9 @@ import { ConverseCommand, ConverseCommandInput, ConverseCommandOutput, + ConverseStreamCommand, + ConverseStreamCommandInput, + ConverseStreamCommandOutput, Message, Tool, ToolConfiguration, @@ -12,6 +15,7 @@ import { import { ConversationTurnEvent, ExecutableTool, + StreamingResponseChunk, ToolDefinition, } from './types.js'; import { ConversationTurnEventToolsProvider } from './event-tools-provider'; @@ -153,6 +157,169 @@ export class BedrockConverseAdapter { return bedrockResponse.output?.message?.content ?? []; }; + /** + * Asks Bedrock for response using streaming version of Converse API. + */ + async *askBedrockStreaming(): AsyncGenerator { + const { modelId, systemPrompt, inferenceConfiguration } = + this.event.modelConfiguration; + + const messages: Array = + await this.getEventMessagesAsBedrockMessages(); + + let bedrockResponse: ConverseStreamCommandOutput; + // keep our own indexing for blocks instead of using Bedrock's indexes + // since we stream subset of these upstream. + let blockIndex = 0; + let lastBlockIndex = 0; + let stopReason = ''; + do { + const toolConfig = this.createToolConfiguration(); + const converseCommandInput: ConverseStreamCommandInput = { + modelId, + messages: [...messages], + system: [{ text: systemPrompt }], + inferenceConfig: inferenceConfiguration, + toolConfig, + }; + this.logger.info('Sending Bedrock Converse Stream request'); + this.logger.debug( + 'Bedrock Converse Stream request:', + converseCommandInput + ); + bedrockResponse = await this.bedrockClient.send( + new ConverseStreamCommand(converseCommandInput) + ); + this.logger.info( + `Received Bedrock Converse Stream response, requestId=${bedrockResponse.$metadata.requestId}` + ); + if (!bedrockResponse.stream) { + throw new Error('Bedrock response is missing stream'); + } + let toolUseBlock: ContentBlock.ToolUseMember | undefined; + let clientToolsRequested = false; + let text: string = ''; + let toolUseInput: string = ''; + let blockDeltaIndex = 0; + let lastBlockDeltaIndex = 0; + const accumulatedAssistantMessage: Message = { + role: undefined, + content: [], + }; + for await (const chunk of bedrockResponse.stream) { + this.logger.debug('Bedrock Converse Stream response chunk:', chunk); + if (chunk.messageStart) { + accumulatedAssistantMessage.role = chunk.messageStart.role; + } else if (chunk.contentBlockStart) { + blockDeltaIndex = 0; + lastBlockDeltaIndex = 0; + if (chunk.contentBlockStart.start?.toolUse) { + toolUseBlock = { + toolUse: { + ...chunk.contentBlockStart.start?.toolUse, + input: undefined, + }, + }; + } + } else if (chunk.contentBlockDelta) { + if (chunk.contentBlockDelta.delta?.toolUse) { + if (!chunk.contentBlockDelta.delta.toolUse.input) { + toolUseInput = ''; + } + toolUseInput += chunk.contentBlockDelta.delta.toolUse.input; + } else if (chunk.contentBlockDelta.delta?.text) { + text += chunk.contentBlockDelta.delta.text; + yield { + conversationId: this.event.conversationId, + associatedUserMessageId: this.event.currentMessageId, + contentBlockText: chunk.contentBlockDelta.delta.text, + contentBlockIndex: blockIndex, + contentBlockDeltaIndex: blockDeltaIndex, + }; + lastBlockDeltaIndex = blockDeltaIndex; + blockDeltaIndex++; + } + } else if (chunk.contentBlockStop) { + if (toolUseBlock) { + toolUseBlock.toolUse.input = JSON.parse(toolUseInput); + accumulatedAssistantMessage.content?.push(toolUseBlock); + if ( + toolUseBlock.toolUse.name && + this.clientToolByName.has(toolUseBlock.toolUse.name) + ) { + clientToolsRequested = true; + yield { + conversationId: this.event.conversationId, + associatedUserMessageId: this.event.currentMessageId, + contentBlockIndex: blockIndex, + contentBlockToolUse: JSON.stringify(toolUseBlock), + }; + lastBlockIndex = blockIndex; + blockIndex++; + } + toolUseBlock = undefined; + toolUseInput = ''; + } else { + accumulatedAssistantMessage.content?.push({ + text, + }); + yield { + conversationId: this.event.conversationId, + associatedUserMessageId: this.event.currentMessageId, + contentBlockIndex: blockIndex, + contentBlockDoneAtIndex: lastBlockDeltaIndex, + }; + text = ''; + lastBlockIndex = blockIndex; + blockIndex++; + } + } else if (chunk.messageStop) { + stopReason = chunk.messageStop.stopReason ?? ''; + } + } + this.logger.debug( + 'Accumulated Bedrock Converse Stream response:', + accumulatedAssistantMessage + ); + if (clientToolsRequested) { + // For now if any of client tools is used we ignore executable tools + // and propagate result back to client. + yield { + conversationId: this.event.conversationId, + associatedUserMessageId: this.event.currentMessageId, + contentBlockIndex: lastBlockIndex, + stopReason: stopReason, + }; + return; + } + messages.push(accumulatedAssistantMessage); + if (stopReason === 'tool_use') { + const responseContentBlocks = accumulatedAssistantMessage.content ?? []; + const toolUseBlocks = responseContentBlocks.filter( + (block) => 'toolUse' in block + ) as Array; + const toolResponseContentBlocks: Array = []; + for (const responseContentBlock of toolUseBlocks) { + const toolUseBlock = + responseContentBlock as ContentBlock.ToolUseMember; + const toolResultContentBlock = await this.executeTool(toolUseBlock); + toolResponseContentBlocks.push(toolResultContentBlock); + } + messages.push({ + role: 'user', + content: toolResponseContentBlocks, + }); + } + } while (stopReason === 'tool_use'); + + yield { + conversationId: this.event.conversationId, + associatedUserMessageId: this.event.currentMessageId, + contentBlockIndex: lastBlockIndex, + stopReason: stopReason, + }; + } + /** * Maps event messages to Bedrock types. * 1. Makes a copy so that we don't mutate event. diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts index ad799fede9b..acd3a3e923e 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts @@ -1,10 +1,11 @@ import { describe, it, mock } from 'node:test'; import assert from 'node:assert'; import { ConversationTurnExecutor } from './conversation_turn_executor'; -import { ConversationTurnEvent } from './types'; +import { ConversationTurnEvent, StreamingResponseChunk } from './types'; import { BedrockConverseAdapter } from './bedrock_converse_adapter'; import { ContentBlock } from '@aws-sdk/client-bedrock-runtime'; import { ConversationTurnResponseSender } from './conversation_turn_response_sender'; +import { ConversationTurnStreamingResponseSender } from './conversation_turn_streaming_response_sender'; void describe('Conversation turn executor', () => { const event: ConversationTurnEvent = { @@ -44,6 +45,15 @@ void describe('Conversation turn executor', () => { () => Promise.resolve() ); + const streamResponseSender = new ConversationTurnStreamingResponseSender( + event + ); + const streamResponseSenderSendResponseMock = mock.method( + streamResponseSender, + 'sendResponseChunk', + () => Promise.resolve() + ); + const consoleErrorMock = mock.fn(); const consoleLogMock = mock.fn(); const consoleDebugMock = mock.fn(); @@ -58,6 +68,7 @@ void describe('Conversation turn executor', () => { [], bedrockConverseAdapter, responseSender, + streamResponseSender, consoleMock ).execute(); @@ -65,6 +76,10 @@ void describe('Conversation turn executor', () => { bedrockConverseAdapterAskBedrockMock.mock.calls.length, 1 ); + assert.strictEqual( + streamResponseSenderSendResponseMock.mock.calls.length, + 0 + ); assert.strictEqual(responseSenderSendResponseMock.mock.calls.length, 1); assert.deepStrictEqual( responseSenderSendResponseMock.mock.calls[0].arguments[0], @@ -84,6 +99,107 @@ void describe('Conversation turn executor', () => { assert.strictEqual(consoleErrorMock.mock.calls.length, 0); }); + void it('executes turn successfully with streaming response', async () => { + const streamingEvent: ConversationTurnEvent = { + ...event, + streamResponse: true, + }; + const bedrockConverseAdapter = new BedrockConverseAdapter( + streamingEvent, + [] + ); + const chunks: Array = [ + { + contentBlockText: 'chunk1', + contentBlockIndex: 0, + contentBlockDeltaIndex: 1, + conversationId: 'testConversationId', + associatedUserMessageId: 'testCurrentMessageId', + }, + { + contentBlockText: 'chunk2', + contentBlockIndex: 0, + contentBlockDeltaIndex: 1, + conversationId: 'testConversationId', + associatedUserMessageId: 'testCurrentMessageId', + }, + ]; + const bedrockConverseAdapterAskBedrockMock = mock.method( + bedrockConverseAdapter, + 'askBedrockStreaming', + () => + (async function* (): AsyncGenerator { + for (const chunk of chunks) { + yield chunk; + } + })() + ); + const responseSender = new ConversationTurnResponseSender(streamingEvent); + const responseSenderSendResponseMock = mock.method( + responseSender, + 'sendResponse', + () => Promise.resolve() + ); + + const streamResponseSender = new ConversationTurnStreamingResponseSender( + event + ); + const streamResponseSenderSendResponseMock = mock.method( + streamResponseSender, + 'sendResponseChunk', + () => Promise.resolve() + ); + + const consoleErrorMock = mock.fn(); + const consoleLogMock = mock.fn(); + const consoleDebugMock = mock.fn(); + const consoleMock = { + error: consoleErrorMock, + log: consoleLogMock, + debug: consoleDebugMock, + } as unknown as Console; + + await new ConversationTurnExecutor( + streamingEvent, + [], + bedrockConverseAdapter, + responseSender, + streamResponseSender, + consoleMock + ).execute(); + + assert.strictEqual( + bedrockConverseAdapterAskBedrockMock.mock.calls.length, + 1 + ); + assert.strictEqual( + streamResponseSenderSendResponseMock.mock.calls.length, + 2 + ); + assert.deepStrictEqual( + streamResponseSenderSendResponseMock.mock.calls[0].arguments[0], + chunks[0] + ); + assert.deepStrictEqual( + streamResponseSenderSendResponseMock.mock.calls[1].arguments[0], + chunks[1] + ); + + assert.strictEqual(responseSenderSendResponseMock.mock.calls.length, 0); + + assert.strictEqual(consoleLogMock.mock.calls.length, 2); + assert.strictEqual( + consoleLogMock.mock.calls[0].arguments[0], + 'Handling conversation turn event, currentMessageId=testCurrentMessageId, conversationId=testConversationId' + ); + assert.strictEqual( + consoleLogMock.mock.calls[1].arguments[0], + 'Conversation turn event handled successfully, currentMessageId=testCurrentMessageId, conversationId=testConversationId' + ); + + assert.strictEqual(consoleErrorMock.mock.calls.length, 0); + }); + void it('logs and propagates error if bedrock adapter throws', async () => { const bedrockConverseAdapter = new BedrockConverseAdapter(event, []); const bedrockError = new Error('Bedrock failed'); @@ -99,6 +215,15 @@ void describe('Conversation turn executor', () => { () => Promise.resolve() ); + const streamResponseSender = new ConversationTurnStreamingResponseSender( + event + ); + const streamResponseSenderSendResponseMock = mock.method( + streamResponseSender, + 'sendResponseChunk', + () => Promise.resolve() + ); + const consoleErrorMock = mock.fn(); const consoleLogMock = mock.fn(); const consoleDebugMock = mock.fn(); @@ -115,6 +240,7 @@ void describe('Conversation turn executor', () => { [], bedrockConverseAdapter, responseSender, + streamResponseSender, consoleMock ).execute(), (error: Error) => { @@ -127,6 +253,10 @@ void describe('Conversation turn executor', () => { bedrockConverseAdapterAskBedrockMock.mock.calls.length, 1 ); + assert.strictEqual( + streamResponseSenderSendResponseMock.mock.calls.length, + 0 + ); assert.strictEqual(responseSenderSendResponseMock.mock.calls.length, 0); assert.strictEqual(consoleLogMock.mock.calls.length, 1); @@ -165,6 +295,15 @@ void describe('Conversation turn executor', () => { () => Promise.reject(responseSenderError) ); + const streamResponseSender = new ConversationTurnStreamingResponseSender( + event + ); + const streamResponseSenderSendResponseMock = mock.method( + streamResponseSender, + 'sendResponseChunk', + () => Promise.resolve() + ); + const consoleErrorMock = mock.fn(); const consoleLogMock = mock.fn(); const consoleDebugMock = mock.fn(); @@ -181,6 +320,7 @@ void describe('Conversation turn executor', () => { [], bedrockConverseAdapter, responseSender, + streamResponseSender, consoleMock ).execute(), (error: Error) => { @@ -193,6 +333,10 @@ void describe('Conversation turn executor', () => { bedrockConverseAdapterAskBedrockMock.mock.calls.length, 1 ); + assert.strictEqual( + streamResponseSenderSendResponseMock.mock.calls.length, + 0 + ); assert.strictEqual(responseSenderSendResponseMock.mock.calls.length, 1); assert.strictEqual(consoleLogMock.mock.calls.length, 1); diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts index 99e66c5f741..01c19d857e4 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts @@ -1,6 +1,7 @@ import { ConversationTurnResponseSender } from './conversation_turn_response_sender.js'; import { ConversationTurnEvent, ExecutableTool, JSONSchema } from './types.js'; import { BedrockConverseAdapter } from './bedrock_converse_adapter.js'; +import { ConversationTurnStreamingResponseSender } from './conversation_turn_streaming_response_sender'; /** * This class is responsible for orchestrating conversation turn execution. @@ -21,6 +22,9 @@ export class ConversationTurnExecutor { additionalTools ), private readonly responseSender = new ConversationTurnResponseSender(event), + private readonly streamingResponseSender = new ConversationTurnStreamingResponseSender( + event + ), private readonly logger = console ) {} @@ -31,9 +35,16 @@ export class ConversationTurnExecutor { ); this.logger.debug('Event received:', this.event); - const assistantResponse = await this.bedrockConverseAdapter.askBedrock(); - - await this.responseSender.sendResponse(assistantResponse); + if (this.event.streamResponse) { + const chunks = this.bedrockConverseAdapter.askBedrockStreaming(); + for await (const chunk of chunks) { + await this.streamingResponseSender.sendResponseChunk(chunk); + } + } else { + const assistantResponse = + await this.bedrockConverseAdapter.askBedrock(); + await this.responseSender.sendResponse(assistantResponse); + } this.logger.log( `Conversation turn event handled successfully, currentMessageId=${this.event.currentMessageId}, conversationId=${this.event.conversationId}` diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.test.ts new file mode 100644 index 00000000000..c94b8b5ff0a --- /dev/null +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.test.ts @@ -0,0 +1,72 @@ +import { describe, it, mock } from 'node:test'; +import assert from 'node:assert'; +import { ConversationTurnEvent, StreamingResponseChunk } from './types'; +import { + GraphqlRequest, + GraphqlRequestExecutor, +} from './graphql_request_executor'; +import { + ConversationTurnStreamingResponseSender, + MutationStreamingResponseInput, +} from './conversation_turn_streaming_response_sender'; + +void describe('Conversation turn streaming response sender', () => { + const event: ConversationTurnEvent = { + conversationId: 'testConversationId', + currentMessageId: 'testCurrentMessageId', + graphqlApiEndpoint: 'http://fake.endpoint/', + messageHistoryQuery: { + getQueryName: '', + getQueryInputTypeName: '', + listQueryName: '', + listQueryInputTypeName: '', + }, + modelConfiguration: { modelId: '', systemPrompt: '' }, + request: { headers: { authorization: 'testToken' } }, + responseMutation: { + name: 'testResponseMutationName', + inputTypeName: 'testResponseMutationInputTypeName', + selectionSet: 'testSelectionSet', + }, + }; + + void it('sends streaming response chunk back to appsync', async () => { + const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); + const executeGraphqlMock = mock.method( + graphqlRequestExecutor, + 'executeGraphql', + () => + // Mock successful Appsync response + Promise.resolve() + ); + const sender = new ConversationTurnStreamingResponseSender( + event, + graphqlRequestExecutor + ); + const chunk: StreamingResponseChunk = { + associatedUserMessageId: 'testAssociatedUserMessageId', + contentBlockIndex: 1, + contentBlockDeltaIndex: 2, + conversationId: 'testConversationId', + contentBlockText: 'testBlockText', + }; + await sender.sendResponseChunk(chunk); + + assert.strictEqual(executeGraphqlMock.mock.calls.length, 1); + const request = executeGraphqlMock.mock.calls[0] + .arguments[0] as GraphqlRequest; + assert.deepStrictEqual(request, { + query: + '\n' + + ' mutation PublishModelResponse($input: testResponseMutationInputTypeName!) {\n' + + ' testResponseMutationName(input: $input) {\n' + + ' testSelectionSet\n' + + ' }\n' + + ' }\n' + + ' ', + variables: { + input: chunk, + }, + }); + }); +}); diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.ts new file mode 100644 index 00000000000..f5ccefac088 --- /dev/null +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.ts @@ -0,0 +1,48 @@ +import { ConversationTurnEvent, StreamingResponseChunk } from './types.js'; +import { GraphqlRequestExecutor } from './graphql_request_executor'; + +export type MutationStreamingResponseInput = { + input: StreamingResponseChunk; +}; + +/** + * This class is responsible for sending response chunks produced by Bedrock back to AppSync + * in a form of mutation. + */ +export class ConversationTurnStreamingResponseSender { + /** + * Creates conversation turn response sender. + */ + constructor( + private readonly event: ConversationTurnEvent, + private readonly graphqlRequestExecutor = new GraphqlRequestExecutor( + event.graphqlApiEndpoint, + event.request.headers.authorization, + event.request.headers['x-amz-user-agent'] + ), + private readonly logger = console + ) {} + + sendResponseChunk = async (chunk: StreamingResponseChunk) => { + const responseMutationRequest = this.createMutationRequest(chunk); + this.logger.debug('Sending response mutation:', responseMutationRequest); + await this.graphqlRequestExecutor.executeGraphql< + MutationStreamingResponseInput, + void + >(responseMutationRequest); + }; + + private createMutationRequest = (chunk: StreamingResponseChunk) => { + const query = ` + mutation PublishModelResponse($input: ${this.event.responseMutation.inputTypeName}!) { + ${this.event.responseMutation.name}(input: $input) { + ${this.event.responseMutation.selectionSet} + } + } + `; + const variables: MutationStreamingResponseInput = { + input: chunk, + }; + return { query, variables }; + }; +} diff --git a/packages/ai-constructs/src/conversation/runtime/types.ts b/packages/ai-constructs/src/conversation/runtime/types.ts index c05e8abc5c8..01d10a0975d 100644 --- a/packages/ai-constructs/src/conversation/runtime/types.ts +++ b/packages/ai-constructs/src/conversation/runtime/types.ts @@ -47,6 +47,7 @@ export type ToolDefinition = { export type ConversationTurnEvent = { conversationId: string; currentMessageId: string; + streamResponse?: boolean; responseMutation: { name: string; inputTypeName: string; @@ -93,3 +94,43 @@ export type ExecutableTool< > = ToolDefinition & { execute: (input: TToolInput) => Promise; }; + +export type StreamingResponseChunk = { + // always required + conversationId: string; + associatedUserMessageId: string; + contentBlockIndex: number; +} & ( + | { + // text chunk + contentBlockText: string; + contentBlockDeltaIndex: number; + contentBlockDoneAtIndex?: never; + contentBlockToolUse?: never; + stopReason?: never; + } + | { + // end of block. applicable to text blocks + contentBlockDoneAtIndex: number; + contentBlockText?: never; + contentBlockDeltaIndex?: never; + contentBlockToolUse?: never; + stopReason?: never; + } + | { + // tool use + contentBlockToolUse: string; // serialized json with full tool use block + contentBlockDoneAtIndex?: never; + contentBlockText?: never; + contentBlockDeltaIndex?: never; + stopReason?: never; + } + | { + // turn complete + stopReason: string; + contentBlockDoneAtIndex?: never; + contentBlockText?: never; + contentBlockDeltaIndex?: never; + contentBlockToolUse?: never; + } +); diff --git a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts index ea5bfb8619e..8dc05092dfe 100644 --- a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts +++ b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts @@ -77,29 +77,15 @@ type CreateConversationMessageChatInput = ConversationMessage & { associatedUserMessageId?: string; }; -const commonEventProperties = { - responseMutation: { - name: 'createConversationMessageAssistantResponse', - inputTypeName: 'CreateConversationMessageAssistantResponseInput', - selectionSet: [ - 'id', - 'conversationId', - 'content', - 'owner', - 'createdAt', - 'updatedAt', - ].join('\n'), - }, - messageHistoryQuery: { - getQueryName: 'getConversationMessageChat', - getQueryInputTypeName: 'ID', - listQueryName: 'listConversationMessageChats', - listQueryInputTypeName: 'ModelConversationMessageChatFilterInput', - }, - modelConfiguration: { - modelId: bedrockModelId, - systemPrompt: 'You are helpful bot.', - }, +type ConversationTurnAppSyncResponseChunk = { + conversationId: string; + associatedUserMessageId: string; + contentBlockIndex: number; + contentBlockText?: string; + contentBlockDeltaIndex?: number; + contentBlockDoneAtIndex?: number; + contentBlockToolUse?: string; + stopReason?: string; }; /** @@ -223,7 +209,16 @@ class ConversationHandlerTestProject extends TestProjectBase { backendId, authenticatedUserCredentials.accessToken, clientConfig.data.url, - apolloClient + apolloClient, + false + ); + + await this.assertDefaultConversationHandlerCanExecuteTurn( + backendId, + authenticatedUserCredentials.accessToken, + clientConfig.data.url, + apolloClient, + true ); await this.assertDefaultConversationHandlerCanExecuteTurn( @@ -231,6 +226,7 @@ class ConversationHandlerTestProject extends TestProjectBase { authenticatedUserCredentials.accessToken, clientConfig.data.url, apolloClient, + false, // Simulate eventual consistency true ); @@ -239,28 +235,64 @@ class ConversationHandlerTestProject extends TestProjectBase { backendId, authenticatedUserCredentials.accessToken, clientConfig.data.url, - apolloClient + apolloClient, + false + ); + + await this.assertCustomConversationHandlerCanExecuteTurn( + backendId, + authenticatedUserCredentials.accessToken, + clientConfig.data.url, + apolloClient, + true ); await this.assertDefaultConversationHandlerCanExecuteTurnWithDataTool( backendId, authenticatedUserCredentials.accessToken, clientConfig.data.url, - apolloClient + apolloClient, + false + ); + + await this.assertDefaultConversationHandlerCanExecuteTurnWithDataTool( + backendId, + authenticatedUserCredentials.accessToken, + clientConfig.data.url, + apolloClient, + true ); await this.assertDefaultConversationHandlerCanExecuteTurnWithClientTool( backendId, authenticatedUserCredentials.accessToken, clientConfig.data.url, - apolloClient + apolloClient, + false + ); + + await this.assertDefaultConversationHandlerCanExecuteTurnWithClientTool( + backendId, + authenticatedUserCredentials.accessToken, + clientConfig.data.url, + apolloClient, + true ); await this.assertDefaultConversationHandlerCanExecuteTurnWithImage( backendId, authenticatedUserCredentials.accessToken, clientConfig.data.url, - apolloClient + apolloClient, + false + ); + + await this.assertDefaultConversationHandlerCanExecuteTurnWithImage( + backendId, + authenticatedUserCredentials.accessToken, + clientConfig.data.url, + apolloClient, + true ); } @@ -269,6 +301,7 @@ class ConversationHandlerTestProject extends TestProjectBase { accessToken: string, graphqlApiEndpoint: string, apolloClient: ApolloClient, + streamResponse: boolean, withoutMessageAvailableInTheMessageList = false ): Promise => { const defaultConversationHandlerFunction = ( @@ -298,7 +331,7 @@ class ConversationHandlerTestProject extends TestProjectBase { request: { headers: { authorization: accessToken }, }, - ...commonEventProperties, + ...this.getCommonEventProperties(streamResponse), }; if (withoutMessageAvailableInTheMessageList) { @@ -309,19 +342,20 @@ class ConversationHandlerTestProject extends TestProjectBase { } await this.insertMessage(apolloClient, message); - const response = await this.executeConversationTurn( + const responseContent = await this.executeConversationTurn( event, defaultConversationHandlerFunction, apolloClient ); - assert.match(response.content, /3\.14/); + assert.match(responseContent, /3\.14/); }; private assertDefaultConversationHandlerCanExecuteTurnWithImage = async ( backendId: BackendIdentifier, accessToken: string, graphqlApiEndpoint: string, - apolloClient: ApolloClient + apolloClient: ApolloClient, + streamResponse: boolean ): Promise => { const defaultConversationHandlerFunction = ( await this.resourceFinder.findByBackendIdentifier( @@ -370,25 +404,25 @@ class ConversationHandlerTestProject extends TestProjectBase { request: { headers: { authorization: accessToken }, }, - ...commonEventProperties, + ...this.getCommonEventProperties(streamResponse), }; - await this.insertMessage(apolloClient, message); - const response = await this.executeConversationTurn( + const responseContent = await this.executeConversationTurn( event, defaultConversationHandlerFunction, apolloClient ); // The image contains a logo of AWS. Responses may vary, but they should always contain statements below. - assert.match(response.content, /logo/); - assert.match(response.content, /(aws)|(AWS)|(Amazon Web Services)/); + assert.match(responseContent, /logo/); + assert.match(responseContent, /(aws)|(AWS)|(Amazon Web Services)/); }; private assertDefaultConversationHandlerCanExecuteTurnWithDataTool = async ( backendId: BackendIdentifier, accessToken: string, graphqlApiEndpoint: string, - apolloClient: ApolloClient + apolloClient: ApolloClient, + streamResponse: boolean ): Promise => { const defaultConversationHandlerFunction = ( await this.resourceFinder.findByBackendIdentifier( @@ -445,16 +479,16 @@ class ConversationHandlerTestProject extends TestProjectBase { }, ], }, - ...commonEventProperties, + ...this.getCommonEventProperties(streamResponse), }; - const response = await this.executeConversationTurn( + const responseContent = await this.executeConversationTurn( event, defaultConversationHandlerFunction, apolloClient ); // Assert that tool was used. I.e. that LLM used value returned by the tool. assert.match( - response.content, + responseContent, new RegExp(expectedTemperatureInDataToolScenario.toString()) ); }; @@ -463,7 +497,8 @@ class ConversationHandlerTestProject extends TestProjectBase { backendId: BackendIdentifier, accessToken: string, graphqlApiEndpoint: string, - apolloClient: ApolloClient + apolloClient: ApolloClient, + streamResponse: boolean ): Promise => { const defaultConversationHandlerFunction = ( await this.resourceFinder.findByBackendIdentifier( @@ -513,9 +548,9 @@ class ConversationHandlerTestProject extends TestProjectBase { }, ], }, - ...commonEventProperties, + ...this.getCommonEventProperties(streamResponse), }; - const response = await this.executeConversationTurn( + const responseContent = await this.executeConversationTurn( event, defaultConversationHandlerFunction, apolloClient @@ -523,17 +558,157 @@ class ConversationHandlerTestProject extends TestProjectBase { // Assert that tool use content blocks are emitted in case LLM selects client tool. // The content blocks are string serialized, but not as a proper JSON, // hence string matching is employed below to detect some signals that tool use blocks kinds were emitted. - assert.match(response.content, /toolUse/); - assert.match(response.content, /toolUseId/); + assert.match(responseContent, /toolUse/); + assert.match(responseContent, /toolUseId/); // Assert that LLM attempts to pass parameter when asking for tool use. - assert.match(response.content, /"city":"Seattle"/); + assert.match(responseContent, /"city":"Seattle"/); + }; + + private executeConversationTurn = async ( + event: ConversationTurnEvent, + functionName: string, + apolloClient: ApolloClient + ): Promise => { + await this.lambdaClient.send( + new InvokeCommand({ + FunctionName: functionName, + Payload: Buffer.from(JSON.stringify(event)), + }) + ); + + // assert that response came back + if (event.streamResponse) { + let nextToken: string | undefined; + const chunks: Array = []; + do { + const queryResult = await apolloClient.query<{ + listConversationMessageAssistantStreamingResponses: { + items: Array; + nextToken: string | undefined; + }; + }>({ + query: gql` + query ListMessageChunks( + $conversationId: ID + $associatedUserMessageId: ID + $nextToken: String + ) { + listConversationMessageAssistantStreamingResponses( + limit: 1000 + nextToken: $nextToken + filter: { + conversationId: { eq: $conversationId } + associatedUserMessageId: { eq: $associatedUserMessageId } + } + ) { + items { + associatedUserMessageId + contentBlockDeltaIndex + contentBlockDoneAtIndex + contentBlockIndex + contentBlockText + contentBlockToolUse + conversationId + createdAt + id + owner + stopReason + updatedAt + } + nextToken + } + } + `, + variables: { + conversationId: event.conversationId, + associatedUserMessageId: event.currentMessageId, + nextToken, + }, + fetchPolicy: 'no-cache', + }); + nextToken = + queryResult.data.listConversationMessageAssistantStreamingResponses + .nextToken; + chunks.push( + ...queryResult.data.listConversationMessageAssistantStreamingResponses + .items + ); + } while (nextToken); + + assert.ok(chunks); + + chunks.sort((a, b) => { + // This is very simplified sort by message,block and delta indexes; + let aValue = 1000 * 1000 * a.contentBlockIndex; + if (a.contentBlockDeltaIndex) { + aValue += a.contentBlockDeltaIndex; + } + let bValue = 1000 * 1000 * b.contentBlockIndex; + if (b.contentBlockDeltaIndex) { + bValue += b.contentBlockDeltaIndex; + } + return aValue - bValue; + }); + + const content = chunks.reduce((accumulated, current) => { + if (current.contentBlockText) { + accumulated += current.contentBlockText; + } + if (current.contentBlockToolUse) { + accumulated += current.contentBlockToolUse; + } + return accumulated; + }, ''); + + return content; + } + const queryResult = await apolloClient.query<{ + listConversationMessageAssistantResponses: { + items: Array; + }; + }>({ + query: gql` + query ListMessage($conversationId: ID, $associatedUserMessageId: ID) { + listConversationMessageAssistantResponses( + filter: { + conversationId: { eq: $conversationId } + associatedUserMessageId: { eq: $associatedUserMessageId } + } + ) { + items { + conversationId + id + updatedAt + createdAt + content + associatedUserMessageId + } + nextToken + } + } + `, + variables: { + conversationId: event.conversationId, + associatedUserMessageId: event.currentMessageId, + }, + fetchPolicy: 'no-cache', + }); + assert.strictEqual( + 1, + queryResult.data.listConversationMessageAssistantResponses.items.length + ); + const response = + queryResult.data.listConversationMessageAssistantResponses.items[0]; + assert.ok(response.content); + return response.content; }; private assertCustomConversationHandlerCanExecuteTurn = async ( backendId: BackendIdentifier, accessToken: string, graphqlApiEndpoint: string, - apolloClient: ApolloClient + apolloClient: ApolloClient, + streamResponse: boolean ): Promise => { const customConversationHandlerFunction = ( await this.resourceFinder.findByBackendIdentifier( @@ -549,7 +724,7 @@ class ConversationHandlerTestProject extends TestProjectBase { role: 'user', content: [ { - text: 'What is the temperature in Seattle, Boston and Miami?', + text: 'What is the temperature in Seattle and Boston?', }, ], }; @@ -563,75 +738,26 @@ class ConversationHandlerTestProject extends TestProjectBase { request: { headers: { authorization: accessToken }, }, - ...commonEventProperties, + ...this.getCommonEventProperties(streamResponse), }; - const response = await this.executeConversationTurn( + const responseContent = await this.executeConversationTurn( event, customConversationHandlerFunction, apolloClient ); // Assert that tool was used. I.e. LLM used value provided by the tool. assert.match( - response.content, + responseContent, new RegExp( expectedTemperaturesInProgrammaticToolScenario.Seattle.toString() ) ); assert.match( - response.content, + responseContent, new RegExp( expectedTemperaturesInProgrammaticToolScenario.Boston.toString() ) ); - assert.match( - response.content, - new RegExp( - expectedTemperaturesInProgrammaticToolScenario.Miami.toString() - ) - ); - }; - - private executeConversationTurn = async ( - event: ConversationTurnEvent, - functionName: string, - apolloClient: ApolloClient - ): Promise => { - await this.lambdaClient.send( - new InvokeCommand({ - FunctionName: functionName, - Payload: Buffer.from(JSON.stringify(event)), - }) - ); - - // assert that response came back - - const queryResult = await apolloClient.query<{ - listConversationMessageAssistantResponses: { - items: Array; - }; - }>({ - query: gql` - query ListMessages { - listConversationMessageAssistantResponses(limit: 1000) { - items { - conversationId - id - updatedAt - createdAt - content - associatedUserMessageId - } - } - } - `, - fetchPolicy: 'no-cache', - }); - const response = - queryResult.data.listConversationMessageAssistantResponses.items.find( - (item) => item.associatedUserMessageId === event.currentMessageId - ); - assert.ok(response); - return response; }; private insertMessage = async ( @@ -651,4 +777,42 @@ class ConversationHandlerTestProject extends TestProjectBase { }, }); }; + + private getCommonEventProperties = (streamResponse: boolean) => { + const responseMutation = streamResponse + ? { + name: 'createConversationMessageAssistantStreamingResponse', + inputTypeName: + 'CreateConversationMessageAssistantStreamingResponseInput', + selectionSet: ['id', 'conversationId', 'createdAt', 'updatedAt'].join( + '\n' + ), + } + : { + name: 'createConversationMessageAssistantResponse', + inputTypeName: 'CreateConversationMessageAssistantResponseInput', + selectionSet: [ + 'id', + 'conversationId', + 'content', + 'owner', + 'createdAt', + 'updatedAt', + ].join('\n'), + }; + return { + streamResponse, + responseMutation, + messageHistoryQuery: { + getQueryName: 'getConversationMessageChat', + getQueryInputTypeName: 'ID', + listQueryName: 'listConversationMessageChats', + listQueryInputTypeName: 'ModelConversationMessageChatFilterInput', + }, + modelConfiguration: { + modelId: bedrockModelId, + systemPrompt: 'You are helpful bot.', + }, + }; + }; } diff --git a/packages/integration-tests/src/test-projects/conversation-handler/amplify/constants.ts b/packages/integration-tests/src/test-projects/conversation-handler/amplify/constants.ts index 32bd11a5e2d..60961d61308 100644 --- a/packages/integration-tests/src/test-projects/conversation-handler/amplify/constants.ts +++ b/packages/integration-tests/src/test-projects/conversation-handler/amplify/constants.ts @@ -11,7 +11,6 @@ export const bedrockModelId = 'anthropic.claude-3-haiku-20240307-v1:0'; export const expectedTemperaturesInProgrammaticToolScenario = { Seattle: 75, Boston: 58, - Miami: 97, }; export const expectedTemperatureInDataToolScenario = 85; diff --git a/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts b/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts index 922bbf3fd73..add8b2c80b6 100644 --- a/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts +++ b/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts @@ -21,7 +21,7 @@ const thermometer = createExecutableTool( }, (input) => { const city = input.city; - if (city === 'Seattle' || city === 'Boston' || city === 'Miami') { + if (city === 'Seattle' || city === 'Boston') { return Promise.resolve({ // We use this value in test assertion. // LLM uses tool to get temperature and serves this value in final response. diff --git a/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts b/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts index f6985743100..938b71980e6 100644 --- a/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts +++ b/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts @@ -94,6 +94,27 @@ const schema = a.schema({ }) .authorization((allow) => [allow.authenticated(), allow.owner()]), + ConversationMessageAssistantStreamingResponse: a + .model({ + // always + conversationId: a.id().required(), + associatedUserMessageId: a.id().required(), + contentBlockIndex: a.integer().required(), + + // these describe chunks or end of block + contentBlockText: a.string(), + contentBlockToolUse: a.string(), + contentBlockDeltaIndex: a.integer(), + contentBlockDoneAtIndex: a.integer(), + + // when message is complete + stopReason: a.string(), + }) + .secondaryIndexes((index) => [ + index('conversationId').sortKeys(['associatedUserMessageId']), + ]) + .authorization((allow) => [allow.authenticated(), allow.owner()]), + ConversationMessageChat: a .model({ conversationId: a.id(), @@ -103,6 +124,9 @@ const schema = a.schema({ aiContext: a.json(), toolConfiguration: a.ref('MockToolConfiguration'), }) + .secondaryIndexes((index) => [ + index('conversationId').sortKeys(['associatedUserMessageId']), + ]) .authorization((allow) => [allow.authenticated(), allow.owner()]), }); From a5d0efc5b57dbaa59f0094da235a97407ae07e48 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 21:03:53 +0000 Subject: [PATCH 048/199] Version Packages (#2117) Co-authored-by: github-actions[bot] --- .changeset/thirty-cheetahs-relate.md | 5 ----- packages/ai-constructs/CHANGELOG.md | 6 ++++++ packages/ai-constructs/package.json | 2 +- packages/backend-ai/CHANGELOG.md | 7 +++++++ packages/backend-ai/package.json | 4 ++-- packages/integration-tests/package.json | 4 ++-- 6 files changed, 18 insertions(+), 10 deletions(-) delete mode 100644 .changeset/thirty-cheetahs-relate.md diff --git a/.changeset/thirty-cheetahs-relate.md b/.changeset/thirty-cheetahs-relate.md deleted file mode 100644 index 97811042d4d..00000000000 --- a/.changeset/thirty-cheetahs-relate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': minor ---- - -Stream Bedrock responses diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index 99b9d0dfc4d..58de0de5218 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/ai-constructs +## 0.6.0 + +### Minor Changes + +- b6761b0: Stream Bedrock responses + ## 0.5.0 ### Minor Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index 08a29e43f82..753a61af171 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "0.5.0", + "version": "0.6.0", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index a1a93d41443..8fe5a022e55 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,12 @@ # @aws-amplify/backend-ai +## 0.3.2 + +### Patch Changes + +- Updated dependencies [b6761b0] + - @aws-amplify/ai-constructs@0.6.0 + ## 0.3.1 ### Patch Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index aea3b22f795..78b578dfde4 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "publishConfig": { "access": "public" @@ -22,7 +22,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.5.0", + "@aws-amplify/ai-constructs": "^0.6.0", "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.0.2", "@aws-amplify/platform-core": "^1.1.0", diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index f9f39d6988f..692dd9a25c3 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -5,10 +5,10 @@ "type": "module", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.5.0", + "@aws-amplify/ai-constructs": "^0.6.0", "@aws-amplify/auth-construct": "^1.3.1", "@aws-amplify/backend": "^1.5.0", - "@aws-amplify/backend-ai": "^0.3.1", + "@aws-amplify/backend-ai": "^0.3.2", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/data-schema": "^1.0.0", From fdf28bde5a51b9592de68e8764f23e1d87796472 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Thu, 17 Oct 2024 19:54:27 +0200 Subject: [PATCH 049/199] fix: detect deploymentType from Stack Tags (#2116) * fix: detect deploymentType from Stack Tags * add another test --- .changeset/purple-dodos-kneel.md | 5 + .../src/deployed_backend_client.ts | 31 +--- ...d_client_list_delete_failed_stacks.test.ts | 140 ++---------------- ...oyed_backend_client_list_sandboxes.test.ts | 117 +++++---------- 4 files changed, 61 insertions(+), 232 deletions(-) create mode 100644 .changeset/purple-dodos-kneel.md diff --git a/.changeset/purple-dodos-kneel.md b/.changeset/purple-dodos-kneel.md new file mode 100644 index 00000000000..71d24222b50 --- /dev/null +++ b/.changeset/purple-dodos-kneel.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/deployed-backend-client': patch +--- + +fix: detect deploymentType from Stack Tags diff --git a/packages/deployed-backend-client/src/deployed_backend_client.ts b/packages/deployed-backend-client/src/deployed_backend_client.ts index 94b18def4ce..2ff69efe31b 100644 --- a/packages/deployed-backend-client/src/deployed_backend_client.ts +++ b/packages/deployed-backend-client/src/deployed_backend_client.ts @@ -15,11 +15,7 @@ import { ListBackendsResponse, } from './deployed_backend_client_factory.js'; import { BackendIdentifierConversions } from '@aws-amplify/platform-core'; -import { - BackendOutputClient, - BackendOutputClientError, - BackendOutputClientErrorType, -} from './backend_output_client_factory.js'; +import { BackendOutputClient } from './backend_output_client_factory.js'; import { CloudFormationClient, DeleteStackCommand, @@ -158,26 +154,13 @@ export class DefaultDeployedBackendClient implements DeployedBackendClient { private tryGetDeploymentType = async ( stackSummary: StackSummary ): Promise => { - const backendIdentifier = { - stackName: stackSummary.StackName as string, - }; + const stackDescription = await this.cfnClient.send( + new DescribeStacksCommand({ StackName: stackSummary.StackName }) + ); - try { - const backendOutput: BackendOutput = - await this.backendOutputClient.getOutput(backendIdentifier); - - return backendOutput[platformOutputKey].payload - .deploymentType as DeploymentType; - } catch (error) { - if ( - (error as BackendOutputClientError).code === - BackendOutputClientErrorType.METADATA_RETRIEVAL_ERROR - ) { - // Ignore stacks where metadata cannot be retrieved. These are not Amplify stacks, or not compatible with this library. - return; - } - throw error; - } + return stackDescription.Stacks?.[0].Tags?.find( + (tag) => tag.Key === 'amplify:deployment-type' + )?.Value as DeploymentType; }; private listStacks = async ( diff --git a/packages/deployed-backend-client/src/deployed_backend_client_list_delete_failed_stacks.test.ts b/packages/deployed-backend-client/src/deployed_backend_client_list_delete_failed_stacks.test.ts index 96afbc73c2b..116042ad1c1 100644 --- a/packages/deployed-backend-client/src/deployed_backend_client_list_delete_failed_stacks.test.ts +++ b/packages/deployed-backend-client/src/deployed_backend_client_list_delete_failed_stacks.test.ts @@ -6,15 +6,9 @@ import { ListStacksCommand, StackStatus, } from '@aws-sdk/client-cloudformation'; -import { platformOutputKey } from '@aws-amplify/backend-output-schemas'; import { DefaultBackendOutputClient } from './backend_output_client.js'; import { DefaultDeployedBackendClient } from './deployed_backend_client.js'; import { BackendStatus } from './deployed_backend_client_factory.js'; -import { - BackendOutputClientError, - BackendOutputClientErrorType, - StackIdentifier, -} from './index.js'; import { AmplifyClient } from '@aws-sdk/client-amplify'; import { S3 } from '@aws-sdk/client-s3'; import { DeployedResourcesEnumerator } from './deployed-backend-client/deployed_resources_enumerator.js'; @@ -34,14 +28,6 @@ const listStacksMock = { ], }; -const getOutputMockResponse = { - [platformOutputKey]: { - payload: { - deploymentType: 'branch', - }, - }, -}; - void describe('Deployed Backend Client list delete failed stacks', () => { const mockCfnClient = new CloudFormation(); const mockS3Client = new S3(); @@ -56,9 +42,19 @@ void describe('Deployed Backend Client list delete failed stacks', () => { const matchingStack = listStacksMock.StackSummaries.find((stack) => { return stack.StackName === request.input.StackName; }); - const stack = matchingStack; + // Add tags that are used to detect deployment type return { - Stacks: [stack], + Stacks: [ + { + ...matchingStack, + Tags: [ + { + Key: 'amplify:deployment-type', + Value: 'branch', + }, + ], + }, + ], }; } throw request; @@ -83,23 +79,6 @@ void describe('Deployed Backend Client list delete failed stacks', () => { mockCfnClient, new AmplifyClient() ); - const getOutputMock = mock.method( - mockBackendOutputClient, - 'getOutput', - (backendIdentifier: StackIdentifier) => { - if (backendIdentifier.stackName === 'amplify-test-not-a-sandbox') { - return { - ...getOutputMockResponse, - [platformOutputKey]: { - payload: { - deploymentType: 'branch', - }, - }, - }; - } - return getOutputMockResponse; - } - ); const returnedDeleteFailedStacks = [ { deploymentType: 'branch', @@ -116,7 +95,6 @@ void describe('Deployed Backend Client list delete failed stacks', () => { ]; beforeEach(() => { - getOutputMock.mock.resetCalls(); listStacksMockFn.mock.resetCalls(); cfnClientSendMock.mock.resetCalls(); const deployedResourcesEnumerator = new DeployedResourcesEnumerator( @@ -171,98 +149,4 @@ void describe('Deployed Backend Client list delete failed stacks', () => { assert.equal(listStacksMockFn.mock.callCount(), 2); }); - - void it('paginates listBackends when one page contains stacks, but it gets filtered due to not deleted failed status', async () => { - listStacksMockFn.mock.mockImplementationOnce(() => { - return { - StackSummaries: [], - NextToken: 'abc', - }; - }); - const failedStacks = deployedBackendClient.listBackends({ - deploymentType: 'branch', - backendStatusFilters: [BackendStatus.DELETE_FAILED], - }); - assert.deepEqual( - (await failedStacks.getBackendSummaryByPage().next()).value, - returnedDeleteFailedStacks - ); - - assert.equal(listStacksMockFn.mock.callCount(), 2); - }); - - void it('paginates listBackends when one page contains stacks, but it gets filtered due to sandbox deploymentType', async () => { - listStacksMockFn.mock.mockImplementationOnce(() => { - return { - StackSummaries: [], - NextToken: 'abc', - }; - }); - const failedStacks = deployedBackendClient.listBackends({ - deploymentType: 'branch', - backendStatusFilters: [BackendStatus.DELETE_FAILED], - }); - assert.deepEqual( - (await failedStacks.getBackendSummaryByPage().next()).value, - returnedDeleteFailedStacks - ); - - assert.equal(listStacksMockFn.mock.callCount(), 2); - }); - - void it('paginates listBackends when one page contains a stack, but it gets filtered due to not having gen2 outputs', async () => { - getOutputMock.mock.mockImplementationOnce(() => { - throw new BackendOutputClientError( - BackendOutputClientErrorType.METADATA_RETRIEVAL_ERROR, - 'Test metadata retrieval error' - ); - }); - listStacksMockFn.mock.mockImplementationOnce(() => { - return { - StackSummaries: [ - { - StackName: 'amplify-123-name-branch-testHash', - StackStatus: StackStatus.DELETE_FAILED, - CreationTime: new Date(0), - LastUpdatedTime: new Date(1), - }, - ], - NextToken: 'abc', - }; - }); - const failedStacks = deployedBackendClient.listBackends({ - deploymentType: 'branch', - backendStatusFilters: [BackendStatus.DELETE_FAILED], - }); - assert.deepEqual( - (await failedStacks.getBackendSummaryByPage().next()).value, - returnedDeleteFailedStacks - ); - - assert.equal(listStacksMockFn.mock.callCount(), 2); - }); - - void it('does not paginate listBackends when one page throws an unexpected error fetching gen2 outputs', async () => { - getOutputMock.mock.mockImplementationOnce(() => { - throw new Error('Unexpected Error!'); - }); - listStacksMockFn.mock.mockImplementationOnce(() => { - return { - StackSummaries: [ - { - StackName: 'amplify-123-name-branch-testHash', - StackStatus: StackStatus.DELETE_FAILED, - CreationTime: new Date(0), - LastUpdatedTime: new Date(1), - }, - ], - NextToken: 'abc', - }; - }); - const listBackendsPromise = deployedBackendClient.listBackends({ - deploymentType: 'branch', - backendStatusFilters: [BackendStatus.DELETE_FAILED], - }); - await assert.rejects(listBackendsPromise.getBackendSummaryByPage().next()); - }); }); diff --git a/packages/deployed-backend-client/src/deployed_backend_client_list_sandboxes.test.ts b/packages/deployed-backend-client/src/deployed_backend_client_list_sandboxes.test.ts index 12a9e024f94..fb8d6900375 100644 --- a/packages/deployed-backend-client/src/deployed_backend_client_list_sandboxes.test.ts +++ b/packages/deployed-backend-client/src/deployed_backend_client_list_sandboxes.test.ts @@ -7,14 +7,8 @@ import { StackStatus, } from '@aws-sdk/client-cloudformation'; import { BackendDeploymentStatus } from './deployed_backend_client_factory.js'; -import { platformOutputKey } from '@aws-amplify/backend-output-schemas'; import { DefaultBackendOutputClient } from './backend_output_client.js'; import { DefaultDeployedBackendClient } from './deployed_backend_client.js'; -import { - BackendOutputClientError, - BackendOutputClientErrorType, - StackIdentifier, -} from './index.js'; import { AmplifyClient } from '@aws-sdk/client-amplify'; import { S3 } from '@aws-sdk/client-s3'; import { DeployedResourcesEnumerator } from './deployed-backend-client/deployed_resources_enumerator.js'; @@ -34,14 +28,6 @@ const listStacksMock = { ], }; -const getOutputMockResponse = { - [platformOutputKey]: { - payload: { - deploymentType: 'sandbox', - }, - }, -}; - void describe('Deployed Backend Client list sandboxes', () => { const mockCfnClient = new CloudFormation(); const mockS3Client = new S3(); @@ -56,9 +42,18 @@ void describe('Deployed Backend Client list sandboxes', () => { const matchingStack = listStacksMock.StackSummaries.find((stack) => { return stack.StackName === request.input.StackName; }); - const stack = matchingStack; return { - Stacks: [stack], + Stacks: [ + { + ...matchingStack, + Tags: [ + { + Key: 'amplify:deployment-type', + Value: 'sandbox', + }, + ], + }, + ], }; } throw request; @@ -84,23 +79,7 @@ void describe('Deployed Backend Client list sandboxes', () => { mockCfnClient, new AmplifyClient() ); - const getOutputMock = mock.method( - mockBackendOutputClient, - 'getOutput', - (backendIdentifier: StackIdentifier) => { - if (backendIdentifier.stackName === 'amplify-test-not-a-sandbox') { - return { - ...getOutputMockResponse, - [platformOutputKey]: { - payload: { - deploymentType: 'branch', - }, - }, - }; - } - return getOutputMockResponse; - } - ); + const returnedSandboxes = [ { deploymentType: 'sandbox', @@ -117,7 +96,6 @@ void describe('Deployed Backend Client list sandboxes', () => { ]; beforeEach(() => { - getOutputMock.mock.resetCalls(); listStacksMockFn.mock.resetCalls(); cfnClientSendMock.mock.resetCalls(); const deployedResourcesEnumerator = new DeployedResourcesEnumerator( @@ -209,57 +187,36 @@ void describe('Deployed Backend Client list sandboxes', () => { assert.equal(listStacksMockFn.mock.callCount(), 2); }); - void it('paginates listBackends when one page contains a stack, but it gets filtered due to not having gen2 outputs', async () => { - getOutputMock.mock.mockImplementationOnce(() => { - throw new BackendOutputClientError( - BackendOutputClientErrorType.METADATA_RETRIEVAL_ERROR, - 'Test metadata retrieval error' - ); - }); - listStacksMockFn.mock.mockImplementationOnce(() => { - return { - StackSummaries: [ - { - StackName: 'amplify-test-name-sandbox-testHash', - StackStatus: StackStatus.CREATE_COMPLETE, - CreationTime: new Date(0), - LastUpdatedTime: new Date(1), - }, - ], - NextToken: 'abc', - }; - }); + void it('filter stacks that do not have deploymentType tag in it', async () => { + cfnClientSendMock.mock.mockImplementation( + (request: ListStacksCommand | DescribeStacksCommand) => { + if (request instanceof ListStacksCommand) { + return listStacksMockFn(request.input); + } + if (request instanceof DescribeStacksCommand) { + const matchingStack = listStacksMock.StackSummaries.find((stack) => { + return stack.StackName === request.input.StackName; + }); + return { + Stacks: [ + { + ...matchingStack, + Tags: [], + }, + ], + }; + } + throw request; + } + ); const sandboxes = deployedBackendClient.listBackends({ deploymentType: 'sandbox', }); assert.deepEqual( - (await sandboxes.getBackendSummaryByPage().next()).value, - returnedSandboxes + (await sandboxes.getBackendSummaryByPage().next()).done, + true ); - assert.equal(listStacksMockFn.mock.callCount(), 2); - }); - - void it('does not paginate listBackends when one page throws an unexpected error fetching gen2 outputs', async () => { - getOutputMock.mock.mockImplementationOnce(() => { - throw new Error('Unexpected Error!'); - }); - listStacksMockFn.mock.mockImplementationOnce(() => { - return { - StackSummaries: [ - { - StackName: 'amplify-test-name-sandbox-testHash', - StackStatus: StackStatus.CREATE_COMPLETE, - CreationTime: new Date(0), - LastUpdatedTime: new Date(1), - }, - ], - NextToken: 'abc', - }; - }); - const listBackendsPromise = deployedBackendClient.listBackends({ - deploymentType: 'sandbox', - }); - await assert.rejects(listBackendsPromise.getBackendSummaryByPage().next()); + assert.equal(listStacksMockFn.mock.callCount(), 1); }); }); From 91e7f3c3b3ff41831873df705fb4311c8c69b7d5 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 17 Oct 2024 11:29:27 -0700 Subject: [PATCH 050/199] Parse client side tool json elements (#2120) --- .changeset/quiet-elephants-do.md | 5 ++ ...ersation_message_history_retriever.test.ts | 55 +++++++++++++++++++ .../conversation_message_history_retriever.ts | 17 ++++++ 3 files changed, 77 insertions(+) create mode 100644 .changeset/quiet-elephants-do.md diff --git a/.changeset/quiet-elephants-do.md b/.changeset/quiet-elephants-do.md new file mode 100644 index 00000000000..4fcb7a60f28 --- /dev/null +++ b/.changeset/quiet-elephants-do.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': patch +--- + +Parse client side tool json elements diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts index 0c349a34e24..a25b4fae176 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts @@ -394,6 +394,61 @@ void describe('Conversation message history retriever', () => { }, ], }, + { + name: 'Parses client tools json elements', + mockListResponseMessages: [ + { + id: event.currentMessageId, + conversationId: event.conversationId, + role: 'user', + content: [ + { + toolUse: { + name: 'testToolUse', + toolUseId: 'testToolUseId', + input: '{ "testKey": "testValue" }', + }, + }, + { + toolResult: { + status: 'success', + toolUseId: 'testToolUseId', + content: [ + { + json: '{ "testKey": "testValue" }', + }, + ], + }, + }, + ], + }, + ], + expectedMessages: [ + { + role: 'user', + content: [ + { + toolUse: { + name: 'testToolUse', + toolUseId: 'testToolUseId', + input: { testKey: 'testValue' }, + }, + }, + { + toolResult: { + status: 'success', + toolUseId: 'testToolUseId', + content: [ + { + json: { testKey: 'testValue' }, + }, + ], + }, + }, + ], + }, + ], + }, ]; for (const testCase of testCases) { diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts index 38ee05b07c2..e39b1d69442 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts @@ -246,6 +246,23 @@ export class ConversationMessageHistoryRetriever { contentBlock[property] = undefined; } } + + if (typeof contentBlock.toolUse?.input === 'string') { + // toolUse.input may come as serialized JSON for Client Tools. + // Parse it in that case. + contentBlock.toolUse.input = JSON.parse(contentBlock.toolUse.input); + } + if (contentBlock.toolResult?.content) { + contentBlock.toolResult.content.forEach((toolResultContentBlock) => { + if (typeof toolResultContentBlock.json === 'string') { + // toolResult.content[].json may come as serialized JSON for Client Tools. + // Parse it in that case. + toolResultContentBlock.json = JSON.parse( + toolResultContentBlock.json + ); + } + }); + } }); }); From 22aa7a659168c6e04a39343e4a52b712b6372e9c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:18:54 +0200 Subject: [PATCH 051/199] Version Packages (#2121) Co-authored-by: github-actions[bot] --- .changeset/purple-dodos-kneel.md | 5 ----- .changeset/quiet-elephants-do.md | 5 ----- packages/ai-constructs/CHANGELOG.md | 6 ++++++ packages/ai-constructs/package.json | 2 +- packages/deployed-backend-client/CHANGELOG.md | 6 ++++++ packages/deployed-backend-client/package.json | 2 +- 6 files changed, 14 insertions(+), 12 deletions(-) delete mode 100644 .changeset/purple-dodos-kneel.md delete mode 100644 .changeset/quiet-elephants-do.md diff --git a/.changeset/purple-dodos-kneel.md b/.changeset/purple-dodos-kneel.md deleted file mode 100644 index 71d24222b50..00000000000 --- a/.changeset/purple-dodos-kneel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/deployed-backend-client': patch ---- - -fix: detect deploymentType from Stack Tags diff --git a/.changeset/quiet-elephants-do.md b/.changeset/quiet-elephants-do.md deleted file mode 100644 index 4fcb7a60f28..00000000000 --- a/.changeset/quiet-elephants-do.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': patch ---- - -Parse client side tool json elements diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index 58de0de5218..42ca57f0a1c 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/ai-constructs +## 0.6.1 + +### Patch Changes + +- 91e7f3c: Parse client side tool json elements + ## 0.6.0 ### Minor Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index 753a61af171..e1d1c9575a7 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "0.6.0", + "version": "0.6.1", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/deployed-backend-client/CHANGELOG.md b/packages/deployed-backend-client/CHANGELOG.md index 7ee529cc2b6..49a70a81904 100644 --- a/packages/deployed-backend-client/CHANGELOG.md +++ b/packages/deployed-backend-client/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/deployed-backend-client +## 1.4.2 + +### Patch Changes + +- fdf28bd: fix: detect deploymentType from Stack Tags + ## 1.4.1 ### Patch Changes diff --git a/packages/deployed-backend-client/package.json b/packages/deployed-backend-client/package.json index ef86b6e30e2..e1df5238d24 100644 --- a/packages/deployed-backend-client/package.json +++ b/packages/deployed-backend-client/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/deployed-backend-client", - "version": "1.4.1", + "version": "1.4.2", "type": "module", "publishConfig": { "access": "public" From 5f46d8dc1f76d567065b2aa724ec571592bbfa1f Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 18 Oct 2024 11:32:19 -0700 Subject: [PATCH 052/199] add user groups to outputs (#2081) * add user groups to outputs * change groups to array * fix it * try this * test * fix e2e * remove new auth contributor * Revert "remove new auth contributor" This reverts commit 936d54f46f36c9de4b0582a3a7cdfe188403a462. --- .changeset/strange-eggs-lie.md | 9 + packages/auth-construct/src/construct.test.ts | 29 + packages/auth-construct/src/construct.ts | 22 + packages/backend-output-schemas/API.md | 12 + .../backend-output-schemas/src/auth/v1.ts | 1 + packages/backend/src/backend_factory.test.ts | 2 +- packages/backend/src/backend_factory.ts | 2 +- .../engine/custom_outputs_accumulator.test.ts | 6 +- .../outputs/generate_outputs_command.test.ts | 12 +- .../commands/sandbox/sandbox_command.test.ts | 6 +- .../sandbox_event_handler_factory.test.ts | 6 +- packages/client-config/API.md | 186 ++++++- .../client_config_contributor_factory.ts | 20 +- .../client_config_contributor_v1.test.ts | 97 +++- .../client_config_contributor_v1.ts | 190 ++++++- .../client_config_v1.3.ts | 282 ++++++++++ .../src/client-config-schema/schema_v1.3.json | 500 ++++++++++++++++++ .../src/client-config-types/client_config.ts | 16 +- .../client_config_formatter_default.test.ts | 4 +- .../client_config_formatter_legacy.test.ts | 2 +- .../client_config_to_legacy_converter.test.ts | 18 +- .../client_config_to_legacy_converter.ts | 14 +- .../client_config_writer.test.ts | 2 +- ...nerate_empty_client_config_to_file.test.ts | 6 +- .../generate_empty_client_config_to_file.ts | 2 +- .../unified_client_config_generator.test.ts | 148 +++++- .../src/amplify_auth_credentials_factory.ts | 2 +- .../access_testing_project.ts | 12 +- .../cdk/auth_cdk_project.ts | 2 +- .../data_storage_auth_with_triggers.ts | 17 +- .../custom-outputs/amplify/backend.ts | 6 +- .../amplify/auth/resource.ts | 2 +- .../amplify/backend.ts | 3 + 33 files changed, 1540 insertions(+), 98 deletions(-) create mode 100644 .changeset/strange-eggs-lie.md create mode 100644 packages/client-config/src/client-config-schema/client_config_v1.3.ts create mode 100644 packages/client-config/src/client-config-schema/schema_v1.3.json diff --git a/.changeset/strange-eggs-lie.md b/.changeset/strange-eggs-lie.md new file mode 100644 index 00000000000..b4f16ffd00e --- /dev/null +++ b/.changeset/strange-eggs-lie.md @@ -0,0 +1,9 @@ +--- +'@aws-amplify/backend-output-schemas': minor +'@aws-amplify/client-config': minor +'@aws-amplify/auth-construct': patch +'@aws-amplify/backend': patch +'@aws-amplify/backend-cli': patch +--- + +add user groups to outputs diff --git a/packages/auth-construct/src/construct.test.ts b/packages/auth-construct/src/construct.test.ts index c2a268f7d27..d597c0222e9 100644 --- a/packages/auth-construct/src/construct.test.ts +++ b/packages/auth-construct/src/construct.test.ts @@ -1098,6 +1098,7 @@ void describe('Auth construct', () => { 'oauthRedirectSignOut', 'oauthResponseType', 'oauthClientId', + 'groups', ], }, }, @@ -1480,6 +1481,34 @@ void describe('Auth construct', () => { const outputs = template.findOutputs('*'); assert.equal(outputs['socialProviders']['Value'], `["GOOGLE"]`); }); + void it('can override group precedence and correctly updates stored output', () => { + const app = new App(); + const stack = new Stack(app); + const auth = new AmplifyAuth(stack, 'test', { + loginWith: { email: true }, + groups: ['admins', 'managers'], + }); + auth.resources.groups['admins'].cfnUserGroup.precedence = 2; + const expectedGroups = [ + { + admins: { + precedence: 2, + }, + }, + { + managers: { + precedence: 1, + }, + }, + ]; + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Cognito::UserPoolGroup', { + GroupName: 'admins', + Precedence: 2, + }); + const outputs = template.findOutputs('*'); + assert.equal(outputs['groups']['Value'], JSON.stringify(expectedGroups)); + }); }); void describe('Auth external login', () => { diff --git a/packages/auth-construct/src/construct.ts b/packages/auth-construct/src/construct.ts index 6c033aaf5b8..13c6680b652 100644 --- a/packages/auth-construct/src/construct.ts +++ b/packages/auth-construct/src/construct.ts @@ -1194,6 +1194,28 @@ export class AmplifyAuth }, }); + // user group precedence can be overwritten, so they are exposed via cdk LAZY + output.groups = Lazy.string({ + produce: () => { + const groupsArray: { + [key: string]: { + precedence?: number; + }; + }[] = []; + Object.keys(this.resources.groups).forEach((groupName) => { + const precedence = + this.resources.groups[groupName].cfnUserGroup.precedence; + groupsArray.push({ + [groupName]: { + precedence, + }, + }); + }, {} as Record); + + return JSON.stringify(groupsArray); + }, + }); + outputStorageStrategy.addBackendOutputEntry(authOutputKey, { version: '1', payload: output, diff --git a/packages/backend-output-schemas/API.md b/packages/backend-output-schemas/API.md index 6fd3f07d275..23c5a41d89f 100644 --- a/packages/backend-output-schemas/API.md +++ b/packages/backend-output-schemas/API.md @@ -133,6 +133,7 @@ export const unifiedBackendOutputSchema: z.ZodObject<{ oauthRedirectSignOut: z.ZodOptional; oauthClientId: z.ZodOptional; oauthResponseType: z.ZodOptional; + groups: z.ZodOptional; }, "strip", z.ZodTypeAny, { authRegion: string; userPoolId: string; @@ -153,6 +154,7 @@ export const unifiedBackendOutputSchema: z.ZodObject<{ oauthRedirectSignOut?: string | undefined; oauthClientId?: string | undefined; oauthResponseType?: string | undefined; + groups?: string | undefined; }, { authRegion: string; userPoolId: string; @@ -173,6 +175,7 @@ export const unifiedBackendOutputSchema: z.ZodObject<{ oauthRedirectSignOut?: string | undefined; oauthClientId?: string | undefined; oauthResponseType?: string | undefined; + groups?: string | undefined; }>; }, "strip", z.ZodTypeAny, { version: "1"; @@ -196,6 +199,7 @@ export const unifiedBackendOutputSchema: z.ZodObject<{ oauthRedirectSignOut?: string | undefined; oauthClientId?: string | undefined; oauthResponseType?: string | undefined; + groups?: string | undefined; }; }, { version: "1"; @@ -219,6 +223,7 @@ export const unifiedBackendOutputSchema: z.ZodObject<{ oauthRedirectSignOut?: string | undefined; oauthClientId?: string | undefined; oauthResponseType?: string | undefined; + groups?: string | undefined; }; }>]>>; "AWS::Amplify::GraphQL": z.ZodOptional; oauthClientId: z.ZodOptional; oauthResponseType: z.ZodOptional; + groups: z.ZodOptional; }, "strip", z.ZodTypeAny, { authRegion: string; userPoolId: string; @@ -575,6 +583,7 @@ export const versionedAuthOutputSchema: z.ZodDiscriminatedUnion<"version", [z.Zo oauthRedirectSignOut?: string | undefined; oauthClientId?: string | undefined; oauthResponseType?: string | undefined; + groups?: string | undefined; }, { authRegion: string; userPoolId: string; @@ -595,6 +604,7 @@ export const versionedAuthOutputSchema: z.ZodDiscriminatedUnion<"version", [z.Zo oauthRedirectSignOut?: string | undefined; oauthClientId?: string | undefined; oauthResponseType?: string | undefined; + groups?: string | undefined; }>; }, "strip", z.ZodTypeAny, { version: "1"; @@ -618,6 +628,7 @@ export const versionedAuthOutputSchema: z.ZodDiscriminatedUnion<"version", [z.Zo oauthRedirectSignOut?: string | undefined; oauthClientId?: string | undefined; oauthResponseType?: string | undefined; + groups?: string | undefined; }; }, { version: "1"; @@ -641,6 +652,7 @@ export const versionedAuthOutputSchema: z.ZodDiscriminatedUnion<"version", [z.Zo oauthRedirectSignOut?: string | undefined; oauthClientId?: string | undefined; oauthResponseType?: string | undefined; + groups?: string | undefined; }; }>]>; diff --git a/packages/backend-output-schemas/src/auth/v1.ts b/packages/backend-output-schemas/src/auth/v1.ts index af4d65cc8c3..a17c2cd2370 100644 --- a/packages/backend-output-schemas/src/auth/v1.ts +++ b/packages/backend-output-schemas/src/auth/v1.ts @@ -27,5 +27,6 @@ export const authOutputSchema = z.object({ oauthRedirectSignOut: z.string().optional(), oauthClientId: z.string().optional(), oauthResponseType: z.string().optional(), + groups: z.string().optional(), // JSON array as string }), }); diff --git a/packages/backend/src/backend_factory.test.ts b/packages/backend/src/backend_factory.test.ts index cfc01a9e8b9..17567b85a8b 100644 --- a/packages/backend/src/backend_factory.test.ts +++ b/packages/backend/src/backend_factory.test.ts @@ -196,7 +196,7 @@ void describe('Backend', () => { const backend = new BackendFactory({}, rootStack); const clientConfigPartial: DeepPartialAmplifyGeneratedConfigs = { - version: '1.2', + version: '1.3', custom: { someCustomOutput: 'someCustomOutputValue', }, diff --git a/packages/backend/src/backend_factory.ts b/packages/backend/src/backend_factory.ts index d2c7662df35..3a7218b3aa4 100644 --- a/packages/backend/src/backend_factory.ts +++ b/packages/backend/src/backend_factory.ts @@ -33,7 +33,7 @@ const rootStackTypeIdentifier = 'root'; // Client config version that is used by `backend.addOutput()` const DEFAULT_CLIENT_CONFIG_VERSION_FOR_BACKEND_ADD_OUTPUT = - ClientConfigVersionOption.V1_2; + ClientConfigVersionOption.V1_3; /** * Factory that collects and instantiates all the Amplify backend constructs diff --git a/packages/backend/src/engine/custom_outputs_accumulator.test.ts b/packages/backend/src/engine/custom_outputs_accumulator.test.ts index dc89639ab8a..77c4e4896d0 100644 --- a/packages/backend/src/engine/custom_outputs_accumulator.test.ts +++ b/packages/backend/src/engine/custom_outputs_accumulator.test.ts @@ -59,11 +59,11 @@ void describe('Custom outputs accumulator', () => { ); const configPart1: DeepPartialAmplifyGeneratedConfigs = { - version: '1.2', + version: '1.3', custom: { output1: 'val1' }, }; const configPart2: DeepPartialAmplifyGeneratedConfigs = { - version: '1.2', + version: '1.3', custom: { output2: 'val2' }, }; accumulator.addOutput(configPart1); @@ -115,7 +115,7 @@ void describe('Custom outputs accumulator', () => { assert.throws( () => - accumulator.addOutput({ version: '1.2', custom: { output1: 'val1' } }), + accumulator.addOutput({ version: '1.3', custom: { output1: 'val1' } }), (error: AmplifyUserError) => { assert.strictEqual( error.message, diff --git a/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts b/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts index b6aa2065d76..84b4a0f8e6c 100644 --- a/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts +++ b/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts @@ -74,7 +74,7 @@ void describe('generate outputs command', () => { assert.equal(generateClientConfigMock.mock.callCount(), 1); assert.deepEqual( generateClientConfigMock.mock.calls[0].arguments[1], - '1.2' // default version + '1.3' // default version ); assert.deepEqual( generateClientConfigMock.mock.calls[0].arguments[2], @@ -97,7 +97,7 @@ void describe('generate outputs command', () => { assert.equal(generateClientConfigMock.mock.callCount(), 1); assert.deepEqual( generateClientConfigMock.mock.calls[0].arguments[1], - '1.2' // default version + '1.3' // default version ); assert.deepStrictEqual( generateClientConfigMock.mock.calls[0].arguments[2], @@ -118,7 +118,7 @@ void describe('generate outputs command', () => { namespace: 'app_id', type: 'branch', }, - '1.2', + '1.3', '/foo/bar', undefined, ] @@ -136,7 +136,7 @@ void describe('generate outputs command', () => { { stackName: 'stack_name', }, - '1.2', + '1.3', '/foo/bar', undefined, ] @@ -154,7 +154,7 @@ void describe('generate outputs command', () => { { stackName: 'stack_name', }, - '1.2', + '1.3', 'foo/bar', undefined, ] @@ -172,7 +172,7 @@ void describe('generate outputs command', () => { { stackName: 'stack_name', }, - '1.2', + '1.3', 'foo/bar', ClientConfigFormat.DART, ] diff --git a/packages/cli/src/commands/sandbox/sandbox_command.test.ts b/packages/cli/src/commands/sandbox/sandbox_command.test.ts index c4854565496..ce5e2ca9575 100644 --- a/packages/cli/src/commands/sandbox/sandbox_command.test.ts +++ b/packages/cli/src/commands/sandbox/sandbox_command.test.ts @@ -427,15 +427,15 @@ void describe('sandbox command', () => { ); }); - void it('sandbox creates an empty client config file if one does not already exist for version 1.2', async (contextual) => { + void it('sandbox creates an empty client config file if one does not already exist for version 1.3', async (contextual) => { contextual.mock.method(fs, 'existsSync', () => false); const writeFileMock = contextual.mock.method(fsp, 'writeFile', () => true); - await commandRunner.runCommand('sandbox --outputs-version 1.2'); + await commandRunner.runCommand('sandbox --outputs-version 1.3'); assert.equal(sandboxStartMock.mock.callCount(), 1); assert.equal(writeFileMock.mock.callCount(), 1); assert.deepStrictEqual( writeFileMock.mock.calls[0].arguments[1], - `{\n "version": "1.2"\n}` + `{\n "version": "1.3"\n}` ); assert.deepStrictEqual( writeFileMock.mock.calls[0].arguments[0], diff --git a/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.test.ts b/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.test.ts index 2ba2996a0ea..e977b00f51a 100644 --- a/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.test.ts +++ b/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.test.ts @@ -23,7 +23,7 @@ void describe('sandbox_event_handler_factory', () => { } as unknown as ClientConfigGeneratorAdapter; const clientConfigLifecycleHandler = new ClientConfigLifecycleHandler( clientConfigGeneratorAdapterMock, - '1.2', + '1.3', 'test-out', ClientConfigFormat.JSON ); @@ -73,7 +73,7 @@ void describe('sandbox_event_handler_factory', () => { namespace: 'test', name: 'name', }, - '1.2', + '1.3', 'test-out', 'json', ]); @@ -185,7 +185,7 @@ void describe('sandbox_event_handler_factory', () => { namespace: 'test', name: 'name', }, - '1.2', + '1.3', 'test-out', 'json', ]); diff --git a/packages/client-config/API.md b/packages/client-config/API.md index 397419f74fd..cccafacd46b 100644 --- a/packages/client-config/API.md +++ b/packages/client-config/API.md @@ -19,6 +19,9 @@ type AmazonCognitoStandardAttributes_2 = 'address' | 'birthdate' | 'email' | 'fa // @public type AmazonCognitoStandardAttributes_3 = 'address' | 'birthdate' | 'email' | 'family_name' | 'gender' | 'given_name' | 'locale' | 'middle_name' | 'name' | 'nickname' | 'phone_number' | 'picture' | 'preferred_username' | 'profile' | 'sub' | 'updated_at' | 'website' | 'zoneinfo'; +// @public +type AmazonCognitoStandardAttributes_4 = 'address' | 'birthdate' | 'email' | 'family_name' | 'gender' | 'given_name' | 'locale' | 'middle_name' | 'name' | 'nickname' | 'phone_number' | 'picture' | 'preferred_username' | 'profile' | 'sub' | 'updated_at' | 'website' | 'zoneinfo'; + // @public interface AmazonLocationServiceConfig { name?: string; @@ -37,6 +40,12 @@ interface AmazonLocationServiceConfig_3 { style?: string; } +// @public +interface AmazonLocationServiceConfig_4 { + name?: string; + style?: string; +} + // @public type AmazonPinpointChannels = 'IN_APP_MESSAGING' | 'FCM' | 'APNS' | 'EMAIL' | 'SMS'; @@ -46,9 +55,15 @@ type AmazonPinpointChannels_2 = 'IN_APP_MESSAGING' | 'FCM' | 'APNS' | 'EMAIL' | // @public type AmazonPinpointChannels_3 = 'IN_APP_MESSAGING' | 'FCM' | 'APNS' | 'EMAIL' | 'SMS'; +// @public +type AmazonPinpointChannels_4 = 'IN_APP_MESSAGING' | 'FCM' | 'APNS' | 'EMAIL' | 'SMS'; + // @public (undocumented) type AmplifyStorageAccessActions = 'read' | 'get' | 'list' | 'write' | 'delete'; +// @public (undocumented) +type AmplifyStorageAccessActions_2 = 'read' | 'get' | 'list' | 'write' | 'delete'; + // @public interface AmplifyStorageAccessRule { // (undocumented) @@ -63,6 +78,20 @@ interface AmplifyStorageAccessRule { resource?: AmplifyStorageAccessActions[]; } +// @public +interface AmplifyStorageAccessRule_2 { + // (undocumented) + authenticated?: AmplifyStorageAccessActions_2[]; + // (undocumented) + entity?: AmplifyStorageAccessActions_2[]; + // (undocumented) + groups?: AmplifyStorageAccessActions_2[]; + // (undocumented) + guest?: AmplifyStorageAccessActions_2[]; + // (undocumented) + resource?: AmplifyStorageAccessActions_2[]; +} + // @public (undocumented) interface AmplifyStorageBucket { // (undocumented) @@ -85,6 +114,26 @@ interface AmplifyStorageBucket_2 { bucket_name: string; // (undocumented) name: string; + // (undocumented) + paths?: { + [k: string]: AmplifyStorageAccessRule_2; + }; +} + +// @public (undocumented) +interface AmplifyStorageBucket_3 { + // (undocumented) + aws_region: string; + // (undocumented) + bucket_name: string; + // (undocumented) + name: string; +} + +// @public +interface AmplifyUserGroupConfig { + // (undocumented) + precedence?: number; } // @public (undocumented) @@ -161,6 +210,9 @@ interface AWSAmplifyBackendOutputs { unauthenticated_identities_enabled?: boolean; mfa_configuration?: 'NONE' | 'OPTIONAL' | 'REQUIRED'; mfa_methods?: ('SMS' | 'TOTP')[]; + groups?: { + [k: string]: AmplifyUserGroupConfig; + }[]; }; custom?: { [k: string]: unknown; @@ -202,19 +254,19 @@ interface AWSAmplifyBackendOutputs { bucket_name: string; buckets?: AmplifyStorageBucket[]; }; - version: '1.2'; + version: '1.3'; } // @public interface AWSAmplifyBackendOutputs_2 { analytics?: { amazon_pinpoint?: { - aws_region: AwsRegion_2; + aws_region: string; app_id: string; }; }; auth?: { - aws_region: AwsRegion_2; + aws_region: string; user_pool_id: string; user_pool_client_id: string; identity_pool_id?: string; @@ -254,7 +306,7 @@ interface AWSAmplifyBackendOutputs_2 { authorization_types: AwsAppsyncAuthorizationType_2[]; }; geo?: { - aws_region: AwsRegion_2; + aws_region: string; maps?: { items: { [k: string]: AmazonLocationServiceConfig_2; @@ -280,7 +332,7 @@ interface AWSAmplifyBackendOutputs_2 { bucket_name: string; buckets?: AmplifyStorageBucket_2[]; }; - version: '1.1'; + version: '1.2'; } // @public @@ -356,6 +408,84 @@ interface AWSAmplifyBackendOutputs_3 { storage?: { aws_region: AwsRegion_3; bucket_name: string; + buckets?: AmplifyStorageBucket_3[]; + }; + version: '1.1'; +} + +// @public +interface AWSAmplifyBackendOutputs_4 { + analytics?: { + amazon_pinpoint?: { + aws_region: AwsRegion_4; + app_id: string; + }; + }; + auth?: { + aws_region: AwsRegion_4; + user_pool_id: string; + user_pool_client_id: string; + identity_pool_id?: string; + password_policy?: { + min_length: number; + require_numbers: boolean; + require_lowercase: boolean; + require_uppercase: boolean; + require_symbols: boolean; + }; + oauth?: { + identity_providers: ('GOOGLE' | 'FACEBOOK' | 'LOGIN_WITH_AMAZON' | 'SIGN_IN_WITH_APPLE')[]; + domain: string; + scopes: string[]; + redirect_sign_in_uri: string[]; + redirect_sign_out_uri: string[]; + response_type: 'code' | 'token'; + }; + standard_required_attributes?: AmazonCognitoStandardAttributes_4[]; + username_attributes?: ('email' | 'phone_number' | 'username')[]; + user_verification_types?: ('email' | 'phone_number')[]; + unauthenticated_identities_enabled?: boolean; + mfa_configuration?: 'NONE' | 'OPTIONAL' | 'REQUIRED'; + mfa_methods?: ('SMS' | 'TOTP')[]; + }; + custom?: { + [k: string]: unknown; + }; + data?: { + aws_region: AwsRegion_4; + url: string; + model_introspection?: { + [k: string]: unknown; + }; + api_key?: string; + default_authorization_type: AwsAppsyncAuthorizationType_4; + authorization_types: AwsAppsyncAuthorizationType_4[]; + }; + geo?: { + aws_region: AwsRegion_4; + maps?: { + items: { + [k: string]: AmazonLocationServiceConfig_4; + }; + default: string; + }; + search_indices?: { + items: string[]; + default: string; + }; + geofence_collections?: { + items: string[]; + default: string; + }; + }; + notifications?: { + aws_region: AwsRegion_4; + amazon_pinpoint_app_id: string; + channels: AmazonPinpointChannels_4[]; + }; + storage?: { + aws_region: AwsRegion_4; + bucket_name: string; }; version: '1'; } @@ -369,6 +499,9 @@ type AwsAppsyncAuthorizationType_2 = 'AMAZON_COGNITO_USER_POOLS' | 'API_KEY' | ' // @public type AwsAppsyncAuthorizationType_3 = 'AMAZON_COGNITO_USER_POOLS' | 'API_KEY' | 'AWS_IAM' | 'AWS_LAMBDA' | 'OPENID_CONNECT'; +// @public +type AwsAppsyncAuthorizationType_4 = 'AMAZON_COGNITO_USER_POOLS' | 'API_KEY' | 'AWS_IAM' | 'AWS_LAMBDA' | 'OPENID_CONNECT'; + // @public (undocumented) type AwsRegion = string; @@ -378,8 +511,11 @@ type AwsRegion_2 = string; // @public (undocumented) type AwsRegion_3 = string; +// @public (undocumented) +type AwsRegion_4 = string; + // @public -export type ClientConfig = clientConfigTypesV1_2.AWSAmplifyBackendOutputs | clientConfigTypesV1_1.AWSAmplifyBackendOutputs | clientConfigTypesV1.AWSAmplifyBackendOutputs; +export type ClientConfig = clientConfigTypesV1_3.AWSAmplifyBackendOutputs | clientConfigTypesV1_2.AWSAmplifyBackendOutputs | clientConfigTypesV1_1.AWSAmplifyBackendOutputs | clientConfigTypesV1.AWSAmplifyBackendOutputs; // @public (undocumented) export enum ClientConfigFileBaseName { @@ -407,31 +543,46 @@ export enum ClientConfigFormat { export type ClientConfigLegacy = Partial; declare namespace clientConfigTypesV1 { + export { + AmazonCognitoStandardAttributes_4 as AmazonCognitoStandardAttributes, + AwsRegion_4 as AwsRegion, + AwsAppsyncAuthorizationType_4 as AwsAppsyncAuthorizationType, + AmazonPinpointChannels_4 as AmazonPinpointChannels, + AWSAmplifyBackendOutputs_4 as AWSAmplifyBackendOutputs, + AmazonLocationServiceConfig_4 as AmazonLocationServiceConfig + } +} +export { clientConfigTypesV1 } + +declare namespace clientConfigTypesV1_1 { export { AmazonCognitoStandardAttributes_3 as AmazonCognitoStandardAttributes, AwsRegion_3 as AwsRegion, AwsAppsyncAuthorizationType_3 as AwsAppsyncAuthorizationType, AmazonPinpointChannels_3 as AmazonPinpointChannels, AWSAmplifyBackendOutputs_3 as AWSAmplifyBackendOutputs, - AmazonLocationServiceConfig_3 as AmazonLocationServiceConfig + AmazonLocationServiceConfig_3 as AmazonLocationServiceConfig, + AmplifyStorageBucket_3 as AmplifyStorageBucket } } -export { clientConfigTypesV1 } +export { clientConfigTypesV1_1 } -declare namespace clientConfigTypesV1_1 { +declare namespace clientConfigTypesV1_2 { export { AmazonCognitoStandardAttributes_2 as AmazonCognitoStandardAttributes, AwsRegion_2 as AwsRegion, AwsAppsyncAuthorizationType_2 as AwsAppsyncAuthorizationType, AmazonPinpointChannels_2 as AmazonPinpointChannels, + AmplifyStorageAccessActions_2 as AmplifyStorageAccessActions, AWSAmplifyBackendOutputs_2 as AWSAmplifyBackendOutputs, AmazonLocationServiceConfig_2 as AmazonLocationServiceConfig, - AmplifyStorageBucket_2 as AmplifyStorageBucket + AmplifyStorageBucket_2 as AmplifyStorageBucket, + AmplifyStorageAccessRule_2 as AmplifyStorageAccessRule } } -export { clientConfigTypesV1_1 } +export { clientConfigTypesV1_2 } -declare namespace clientConfigTypesV1_2 { +declare namespace clientConfigTypesV1_3 { export { AmazonCognitoStandardAttributes, AwsRegion, @@ -439,12 +590,13 @@ declare namespace clientConfigTypesV1_2 { AmazonPinpointChannels, AmplifyStorageAccessActions, AWSAmplifyBackendOutputs, + AmplifyUserGroupConfig, AmazonLocationServiceConfig, AmplifyStorageBucket, AmplifyStorageAccessRule } } -export { clientConfigTypesV1_2 } +export { clientConfigTypesV1_3 } // @public (undocumented) export type ClientConfigVersion = `${ClientConfigVersionOption}`; @@ -458,11 +610,13 @@ export enum ClientConfigVersionOption { // (undocumented) V1_1 = "1.1", // (undocumented) - V1_2 = "1.2" + V1_2 = "1.2", + // (undocumented) + V1_3 = "1.3" } // @public -export type ClientConfigVersionTemplateType = T extends '1.2' ? clientConfigTypesV1_2.AWSAmplifyBackendOutputs : T extends '1.1' ? clientConfigTypesV1_1.AWSAmplifyBackendOutputs : T extends '1' ? clientConfigTypesV1.AWSAmplifyBackendOutputs : never; +export type ClientConfigVersionTemplateType = T extends '1.3' ? clientConfigTypesV1_3.AWSAmplifyBackendOutputs : T extends '1.2' ? clientConfigTypesV1_2.AWSAmplifyBackendOutputs : T extends '1.1' ? clientConfigTypesV1_1.AWSAmplifyBackendOutputs : T extends '1' ? clientConfigTypesV1.AWSAmplifyBackendOutputs : never; // @public (undocumented) export type CustomClientConfig = { @@ -473,7 +627,7 @@ export type CustomClientConfig = { export const DEFAULT_CLIENT_CONFIG_VERSION: ClientConfigVersion; // @public -export const generateClientConfig: (backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{ +export const generateClientConfig: (backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{ getS3Client: S3Client; getAmplifyClient: AmplifyClient; getCloudFormationClient: CloudFormationClient; diff --git a/packages/client-config/src/client-config-contributor/client_config_contributor_factory.ts b/packages/client-config/src/client-config-contributor/client_config_contributor_factory.ts index 6c8fb49d45b..50d07f4d0ad 100644 --- a/packages/client-config/src/client-config-contributor/client_config_contributor_factory.ts +++ b/packages/client-config/src/client-config-contributor/client_config_contributor_factory.ts @@ -1,14 +1,16 @@ // Versions of config schemas supported by this package version import { - AuthClientConfigContributor as Auth1_1, + AuthClientConfigContributorV1_1 as Auth1_1, + AuthClientConfigContributor as Auth1_3, CustomClientConfigContributor as Custom1_1, DataClientConfigContributor as Data1_1, StorageClientConfigContributorV1 as Storage1, StorageClientConfigContributorV1_1 as Storage1_1, StorageClientConfigContributor as Storage1_2, - VersionContributor as VersionContributor1_2, + VersionContributor as VersionContributor1_3, VersionContributorV1, VersionContributorV1_1, + VersionContributorV1_2, } from './client_config_contributor_v1.js'; import { ClientConfigContributor } from '../client-config-types/client_config_contributor.js'; @@ -33,11 +35,19 @@ export class ClientConfigContributorFactory { private readonly modelIntrospectionSchemaAdapter: ModelIntrospectionSchemaAdapter ) { this.versionedClientConfigContributors = { + [ClientConfigVersionOption.V1_3]: [ + new Auth1_3(), + new Data1_1(this.modelIntrospectionSchemaAdapter), + new Storage1_2(), + new VersionContributor1_3(), + new Custom1_1(), + ], + [ClientConfigVersionOption.V1_2]: [ new Auth1_1(), new Data1_1(this.modelIntrospectionSchemaAdapter), new Storage1_2(), - new VersionContributor1_2(), + new VersionContributorV1_2(), new Custom1_1(), ], @@ -58,12 +68,12 @@ export class ClientConfigContributorFactory { new Custom1_1(), ], - // Legacy config is derived from V1.2 (latest) of unified default config + // Legacy config is derived from V1.3 (latest) of unified default config [ClientConfigVersionOption.V0]: [ new Auth1_1(), new Data1_1(this.modelIntrospectionSchemaAdapter), new Storage1_2(), - new VersionContributor1_2(), + new VersionContributor1_3(), new Custom1_1(), ], }; diff --git a/packages/client-config/src/client-config-contributor/client_config_contributor_v1.test.ts b/packages/client-config/src/client-config-contributor/client_config_contributor_v1.test.ts index 022bea9dd49..8a00d4ebb4f 100644 --- a/packages/client-config/src/client-config-contributor/client_config_contributor_v1.test.ts +++ b/packages/client-config/src/client-config-contributor/client_config_contributor_v1.test.ts @@ -8,7 +8,7 @@ import { } from './client_config_contributor_v1.js'; import { ClientConfig, - clientConfigTypesV1_2, + clientConfigTypesV1_3, } from '../client-config-types/client_config.js'; import assert from 'node:assert'; import { @@ -74,7 +74,7 @@ void describe('auth client config contributor v1', () => { identity_pool_id: 'testIdentityPoolId', unauthenticated_identities_enabled: true, }, - } as Partial + } as Partial ); }); @@ -99,7 +99,7 @@ void describe('auth client config contributor v1', () => { aws_region: 'testRegion', identity_pool_id: 'testIdentityPoolId', }, - } as Partial + } as Partial ); }); @@ -133,7 +133,7 @@ void describe('auth client config contributor v1', () => { require_uppercase: true, }, }, - } as Partial + } as Partial ); }); @@ -166,11 +166,23 @@ void describe('auth client config contributor v1', () => { require_uppercase: false, }, }, - } as Partial + } as Partial ); }); void it('returns translated config when output has auth with zero-config attributes', () => { + const groups = [ + { + ADMINS: { + precedence: 0, + }, + }, + { + EDITORS: { + precedence: 1, + }, + }, + ]; const contributor = new AuthClientConfigContributor(); assert.deepStrictEqual( contributor.contribute({ @@ -197,6 +209,7 @@ void describe('auth client config contributor v1', () => { oauthRedirectSignOut: 'http://logout.com,http://logout2.com', oauthResponseType: 'code', socialProviders: `["GOOGLE","FACEBOOK","SIGN_IN_WITH_APPLE","LOGIN_WITH_AMAZON","GITHUB","DISCORD"]`, + groups: JSON.stringify(groups), }, }, }), @@ -235,12 +248,36 @@ void describe('auth client config contributor v1', () => { redirect_sign_out_uri: ['http://logout.com', 'http://logout2.com'], response_type: 'code', }, + groups: [ + { + ADMINS: { + precedence: 0, + }, + }, + { + EDITORS: { + precedence: 1, + }, + }, + ], }, - } as Partial + } as Partial ); }); void it('returns translated config when output has oauth settings but no social providers', () => { + const groups = [ + { + ADMINS: { + precedence: 0, + }, + }, + { + EDITORS: { + precedence: 1, + }, + }, + ]; const contributor = new AuthClientConfigContributor(); assert.deepStrictEqual( contributor.contribute({ @@ -266,6 +303,7 @@ void describe('auth client config contributor v1', () => { oauthRedirectSignIn: 'http://callback.com,http://callback2.com', oauthRedirectSignOut: 'http://logout.com,http://logout2.com', oauthResponseType: 'code', + groups: JSON.stringify(groups), }, }, }), @@ -299,12 +337,36 @@ void describe('auth client config contributor v1', () => { redirect_sign_out_uri: ['http://logout.com', 'http://logout2.com'], response_type: 'code', }, + groups: [ + { + ADMINS: { + precedence: 0, + }, + }, + { + EDITORS: { + precedence: 1, + }, + }, + ], }, - } as Partial + } as Partial ); }); void describe('auth outputs with mfa', () => { + const groups = [ + { + ADMINS: { + precedence: 0, + }, + }, + { + EDITORS: { + precedence: 1, + }, + }, + ]; const contribution = { version: '1' as const, payload: { @@ -327,6 +389,7 @@ void describe('auth client config contributor v1', () => { oauthRedirectSignIn: 'http://callback.com,http://callback2.com', oauthRedirectSignOut: 'http://logout.com,http://logout2.com', oauthResponseType: 'code', + groups: JSON.stringify(groups), }, }; @@ -357,8 +420,20 @@ void describe('auth client config contributor v1', () => { redirect_sign_out_uri: ['http://logout.com', 'http://logout2.com'], response_type: 'code', }, + groups: [ + { + ADMINS: { + precedence: 0, + }, + }, + { + EDITORS: { + precedence: 1, + }, + }, + ], }, - } as Pick; + } as Pick; void it('returns translated config when mfa is disabled', () => { const contributor = new AuthClientConfigContributor(); @@ -459,7 +534,7 @@ void describe('data client config contributor v1', () => { url: 'testApiEndpoint', aws_region: 'us-east-1', }, - } as Partial); + } as Partial); }); void it('returns translated config with model introspection when resolvable', async () => { @@ -507,7 +582,7 @@ void describe('data client config contributor v1', () => { enums: {}, }, }, - } as Partial); + } as Partial); }); }); @@ -625,6 +700,6 @@ void describe('Custom client config contributor v1', () => { void describe('Custom client config contributor v1', () => { void it('contributes the version correctly', () => { - assert.deepEqual(new VersionContributor().contribute(), { version: '1.2' }); + assert.deepEqual(new VersionContributor().contribute(), { version: '1.3' }); }); }); diff --git a/packages/client-config/src/client-config-contributor/client_config_contributor_v1.ts b/packages/client-config/src/client-config-contributor/client_config_contributor_v1.ts index 425775a9783..4bd0879e0b4 100644 --- a/packages/client-config/src/client-config-contributor/client_config_contributor_v1.ts +++ b/packages/client-config/src/client-config-contributor/client_config_contributor_v1.ts @@ -12,6 +12,7 @@ import { clientConfigTypesV1, clientConfigTypesV1_1, clientConfigTypesV1_2, + clientConfigTypesV1_3, } from '../client-config-types/client_config.js'; import { ModelIntrospectionSchemaAdapter } from '../model_introspection_schema_adapter.js'; import { AwsAppsyncAuthorizationType } from '../client-config-schema/client_config_v1.1.js'; @@ -21,9 +22,22 @@ import { AmplifyStorageAccessRule } from '../client-config-schema/client_config_ // the same schema (version and other types) /** - * Translator for the version number of ClientConfig of V1.2 + * Translator for the version number of ClientConfig of V1.3 */ export class VersionContributor implements ClientConfigContributor { + /** + * Return the version of the schema types that this contributor uses + */ + contribute = (): ClientConfig => { + return { version: ClientConfigVersionOption.V1_3 }; + }; +} + +/** + * Translator for the version number of ClientConfig of V1.2 + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export class VersionContributorV1_2 implements ClientConfigContributor { /** * Return the version of the schema types that this contributor uses */ @@ -58,9 +72,181 @@ export class VersionContributorV1 implements ClientConfigContributor { } /** - * Translator for the Auth portion of ClientConfig + * Translator for the Auth portion of ClientConfig in V1.3 */ export class AuthClientConfigContributor implements ClientConfigContributor { + /** + * Given some BackendOutput, contribute the Auth portion of the ClientConfig + */ + contribute = ({ + [authOutputKey]: authOutput, + }: UnifiedBackendOutput): Partial | Record => { + if (authOutput === undefined) { + return {}; + } + + const parseAndAssignObject = ( + obj: T, + key: keyof T, + value: string | undefined + ) => { + if (value == null) { + return; + } + obj[key] = JSON.parse(value); + }; + + const authClientConfig: Partial = + {}; + + authClientConfig.auth = { + user_pool_id: authOutput.payload.userPoolId, + aws_region: authOutput.payload.authRegion, + user_pool_client_id: authOutput.payload.webClientId, + }; + + if (authOutput.payload.identityPoolId) { + authClientConfig.auth.identity_pool_id = + authOutput.payload.identityPoolId; + } + + parseAndAssignObject( + authClientConfig.auth, + 'mfa_methods', + authOutput.payload.mfaTypes + ); + + parseAndAssignObject( + authClientConfig.auth, + 'standard_required_attributes', + authOutput.payload.signupAttributes + ); + + parseAndAssignObject( + authClientConfig.auth, + 'username_attributes', + authOutput.payload.usernameAttributes + ); + + parseAndAssignObject( + authClientConfig.auth, + 'user_verification_types', + authOutput.payload.verificationMechanisms + ); + + parseAndAssignObject( + authClientConfig.auth, + 'groups', + authOutput.payload.groups + ); + + if (authOutput.payload.mfaConfiguration) { + switch (authOutput.payload.mfaConfiguration) { + case 'OFF': { + authClientConfig.auth.mfa_configuration = 'NONE'; + break; + } + case 'OPTIONAL': { + authClientConfig.auth.mfa_configuration = 'OPTIONAL'; + break; + } + case 'ON': { + authClientConfig.auth.mfa_configuration = 'REQUIRED'; + } + } + } + + if ( + authOutput.payload.passwordPolicyMinLength || + authOutput.payload.passwordPolicyRequirements + ) { + authClientConfig.auth.password_policy = { + min_length: 8, // This is the default that is matching what construct defines. + // Values below are set to false instead of being undefined as libraries expect defined values. + // They are overridden below with construct outputs (default or not) if applicable. + require_lowercase: false, + require_numbers: false, + require_symbols: false, + require_uppercase: false, + }; + if (authOutput.payload.passwordPolicyMinLength) { + authClientConfig.auth.password_policy.min_length = Number.parseInt( + authOutput.payload.passwordPolicyMinLength + ); + } + if (authOutput.payload.passwordPolicyRequirements) { + const requirements = JSON.parse( + authOutput.payload.passwordPolicyRequirements + ) as string[]; + for (const requirement of requirements) { + switch (requirement) { + case 'REQUIRES_NUMBERS': + authClientConfig.auth.password_policy.require_numbers = true; + break; + case 'REQUIRES_LOWERCASE': + authClientConfig.auth.password_policy.require_lowercase = true; + break; + case 'REQUIRES_UPPERCASE': + authClientConfig.auth.password_policy.require_uppercase = true; + break; + case 'REQUIRES_SYMBOLS': + authClientConfig.auth.password_policy.require_symbols = true; + break; + } + } + } + } + + // OAuth settings are present if both oauthRedirectSignIn and oauthRedirectSignOut are. + if ( + authOutput.payload.oauthRedirectSignIn && + authOutput.payload.oauthRedirectSignOut + ) { + let socialProviders = authOutput.payload.socialProviders + ? JSON.parse(authOutput.payload.socialProviders) + : []; + if (Array.isArray(socialProviders)) { + socialProviders = socialProviders.filter(this.isValidIdentityProvider); + } + authClientConfig.auth.oauth = { + identity_providers: socialProviders, + redirect_sign_in_uri: authOutput.payload.oauthRedirectSignIn.split(','), + redirect_sign_out_uri: + authOutput.payload.oauthRedirectSignOut.split(','), + response_type: authOutput.payload.oauthResponseType as 'code' | 'token', + scopes: authOutput.payload.oauthScope + ? JSON.parse(authOutput.payload.oauthScope) + : [], + domain: authOutput.payload.oauthCognitoDomain ?? '', + }; + } + + if (authOutput.payload.allowUnauthenticatedIdentities) { + authClientConfig.auth.unauthenticated_identities_enabled = + authOutput.payload.allowUnauthenticatedIdentities === 'true'; + } + + return authClientConfig; + }; + + // Define a type guard function to check if a value is a valid IdentityProvider + isValidIdentityProvider = (identityProvider: string): boolean => { + return [ + 'GOOGLE', + 'FACEBOOK', + 'LOGIN_WITH_AMAZON', + 'SIGN_IN_WITH_APPLE', + ].includes(identityProvider); + }; +} + +/** + * Translator for the Auth portion of ClientConfig in V1.2 + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export class AuthClientConfigContributorV1_1 + implements ClientConfigContributor +{ /** * Given some BackendOutput, contribute the Auth portion of the ClientConfig */ diff --git a/packages/client-config/src/client-config-schema/client_config_v1.3.ts b/packages/client-config/src/client-config-schema/client_config_v1.3.ts new file mode 100644 index 00000000000..560b0773ec5 --- /dev/null +++ b/packages/client-config/src/client-config-schema/client_config_v1.3.ts @@ -0,0 +1,282 @@ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +/** + * Amazon Cognito standard attributes for users -- https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html + */ +export type AmazonCognitoStandardAttributes = + | 'address' + | 'birthdate' + | 'email' + | 'family_name' + | 'gender' + | 'given_name' + | 'locale' + | 'middle_name' + | 'name' + | 'nickname' + | 'phone_number' + | 'picture' + | 'preferred_username' + | 'profile' + | 'sub' + | 'updated_at' + | 'website' + | 'zoneinfo'; +export type AwsRegion = string; +/** + * List of supported auth types for AWS AppSync + */ +export type AwsAppsyncAuthorizationType = + | 'AMAZON_COGNITO_USER_POOLS' + | 'API_KEY' + | 'AWS_IAM' + | 'AWS_LAMBDA' + | 'OPENID_CONNECT'; +/** + * supported channels for Amazon Pinpoint + */ +export type AmazonPinpointChannels = + | 'IN_APP_MESSAGING' + | 'FCM' + | 'APNS' + | 'EMAIL' + | 'SMS'; +export type AmplifyStorageAccessActions = + | 'read' + | 'get' + | 'list' + | 'write' + | 'delete'; + +/** + * Config format for Amplify Gen 2 client libraries to communicate with backend services. + */ +export interface AWSAmplifyBackendOutputs { + /** + * Version of this schema + */ + version: '1.3'; + /** + * Outputs manually specified by developers for use with frontend library + */ + analytics?: { + amazon_pinpoint?: { + /** + * AWS Region of Amazon Pinpoint resources + */ + aws_region: string; + app_id: string; + }; + }; + /** + * Outputs generated from defineAuth + */ + auth?: { + /** + * AWS Region of Amazon Cognito resources + */ + aws_region: string; + /** + * Cognito User Pool ID + */ + user_pool_id: string; + /** + * Cognito User Pool Client ID + */ + user_pool_client_id: string; + /** + * Cognito Identity Pool ID + */ + identity_pool_id?: string; + /** + * Cognito User Pool password policy + */ + password_policy?: { + min_length: number; + require_numbers: boolean; + require_lowercase: boolean; + require_uppercase: boolean; + require_symbols: boolean; + }; + oauth?: { + /** + * Identity providers set on Cognito User Pool + * + * @minItems 0 + */ + identity_providers: ( + | 'GOOGLE' + | 'FACEBOOK' + | 'LOGIN_WITH_AMAZON' + | 'SIGN_IN_WITH_APPLE' + )[]; + /** + * Domain used for identity providers + */ + domain: string; + /** + * @minItems 0 + */ + scopes: string[]; + /** + * URIs used to redirect after signing in using an identity provider + * + * @minItems 1 + */ + redirect_sign_in_uri: string[]; + /** + * URIs used to redirect after signing out + * + * @minItems 1 + */ + redirect_sign_out_uri: string[]; + response_type: 'code' | 'token'; + }; + /** + * Cognito User Pool standard attributes required for signup + * + * @minItems 0 + */ + standard_required_attributes?: AmazonCognitoStandardAttributes[]; + /** + * Cognito User Pool username attributes + * + * @minItems 1 + */ + username_attributes?: ('email' | 'phone_number' | 'username')[]; + user_verification_types?: ('email' | 'phone_number')[]; + unauthenticated_identities_enabled?: boolean; + mfa_configuration?: 'NONE' | 'OPTIONAL' | 'REQUIRED'; + mfa_methods?: ('SMS' | 'TOTP')[]; + groups?: { + [k: string]: AmplifyUserGroupConfig; + }[]; + }; + /** + * Outputs generated from defineData + */ + data?: { + aws_region: AwsRegion; + /** + * AppSync endpoint URL + */ + url: string; + /** + * generated model introspection schema for use with generateClient + */ + model_introspection?: { + [k: string]: unknown; + }; + api_key?: string; + default_authorization_type: AwsAppsyncAuthorizationType; + authorization_types: AwsAppsyncAuthorizationType[]; + }; + /** + * Outputs manually specified by developers for use with frontend library + */ + geo?: { + /** + * AWS Region of Amazon Location Service resources + */ + aws_region: string; + /** + * Maps from Amazon Location Service + */ + maps?: { + items: { + [k: string]: AmazonLocationServiceConfig; + }; + default: string; + }; + /** + * Location search (search by places, addresses, coordinates) + */ + search_indices?: { + /** + * @minItems 1 + */ + items: string[]; + default: string; + }; + /** + * Geofencing (visualize virtual perimeters) + */ + geofence_collections?: { + /** + * @minItems 1 + */ + items: string[]; + default: string; + }; + }; + /** + * Outputs manually specified by developers for use with frontend library + */ + notifications?: { + aws_region: AwsRegion; + amazon_pinpoint_app_id: string; + /** + * @minItems 1 + */ + channels: AmazonPinpointChannels[]; + }; + /** + * Outputs generated from defineStorage + */ + storage?: { + aws_region: AwsRegion; + bucket_name: string; + buckets?: AmplifyStorageBucket[]; + }; + /** + * Outputs generated from backend.addOutput({ custom: }) + */ + custom?: { + [k: string]: unknown; + }; +} +/** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` ".*". + */ +export interface AmplifyUserGroupConfig { + precedence?: number; +} +/** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` ".*". + */ +export interface AmazonLocationServiceConfig { + /** + * Map resource name + */ + name?: string; + /** + * Map style + */ + style?: string; +} +export interface AmplifyStorageBucket { + name: string; + bucket_name: string; + aws_region: string; + paths?: { + [k: string]: AmplifyStorageAccessRule; + }; +} +/** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` ".*". + */ +export interface AmplifyStorageAccessRule { + guest?: AmplifyStorageAccessActions[]; + authenticated?: AmplifyStorageAccessActions[]; + groups?: AmplifyStorageAccessActions[]; + entity?: AmplifyStorageAccessActions[]; + resource?: AmplifyStorageAccessActions[]; +} diff --git a/packages/client-config/src/client-config-schema/schema_v1.3.json b/packages/client-config/src/client-config-schema/schema_v1.3.json new file mode 100644 index 00000000000..bf89de55040 --- /dev/null +++ b/packages/client-config/src/client-config-schema/schema_v1.3.json @@ -0,0 +1,500 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://amplify.aws/2024-02/outputs-schema.json", + "title": "AWS Amplify Backend Outputs", + "description": "Config format for Amplify Gen 2 client libraries to communicate with backend services.", + "type": "object", + "additionalProperties": false, + "properties": { + "$schema": { + "description": "JSON schema", + "type": "string" + }, + "version": { + "description": "Version of this schema", + "const": "1.3" + }, + "analytics": { + "description": "Outputs manually specified by developers for use with frontend library", + "type": "object", + "additionalProperties": false, + "properties": { + "amazon_pinpoint": { + "type": "object", + "additionalProperties": false, + "properties": { + "aws_region": { + "description": "AWS Region of Amazon Pinpoint resources", + "$ref": "#/$defs/aws_region" + }, + "app_id": { + "type": "string" + } + }, + "required": ["aws_region", "app_id"] + } + } + }, + "auth": { + "description": "Outputs generated from defineAuth", + "type": "object", + "additionalProperties": false, + "properties": { + "aws_region": { + "description": "AWS Region of Amazon Cognito resources", + "$ref": "#/$defs/aws_region" + }, + "user_pool_id": { + "description": "Cognito User Pool ID", + "type": "string" + }, + "user_pool_client_id": { + "description": "Cognito User Pool Client ID", + "type": "string" + }, + "identity_pool_id": { + "description": "Cognito Identity Pool ID", + "type": "string" + }, + "password_policy": { + "description": "Cognito User Pool password policy", + "type": "object", + "additionalProperties": false, + "properties": { + "min_length": { + "type": "integer", + "minimum": 6, + "maximum": 99 + }, + "require_numbers": { + "type": "boolean" + }, + "require_lowercase": { + "type": "boolean" + }, + "require_uppercase": { + "type": "boolean" + }, + "require_symbols": { + "type": "boolean" + } + }, + "required": [ + "min_length", + "require_numbers", + "require_lowercase", + "require_uppercase", + "require_symbols" + ] + }, + "oauth": { + "type": "object", + "additionalProperties": false, + "properties": { + "identity_providers": { + "description": "Identity providers set on Cognito User Pool", + "type": "array", + "items": { + "type": "string", + "enum": [ + "GOOGLE", + "FACEBOOK", + "LOGIN_WITH_AMAZON", + "SIGN_IN_WITH_APPLE" + ] + }, + "minItems": 0, + "uniqueItems": true + }, + "domain": { + "description": "Domain used for identity providers", + "type": "string" + }, + "scopes": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 0, + "uniqueItems": true + }, + "redirect_sign_in_uri": { + "description": "URIs used to redirect after signing in using an identity provider", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "redirect_sign_out_uri": { + "description": "URIs used to redirect after signing out", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "response_type": { + "type": "string", + "enum": ["code", "token"] + } + }, + "required": [ + "identity_providers", + "domain", + "scopes", + "redirect_sign_in_uri", + "redirect_sign_out_uri", + "response_type" + ] + }, + "standard_required_attributes": { + "description": "Cognito User Pool standard attributes required for signup", + "type": "array", + "items": { + "$ref": "#/$defs/amazon_cognito_standard_attributes" + }, + "minItems": 0, + "uniqueItems": true + }, + "username_attributes": { + "description": "Cognito User Pool username attributes", + "type": "array", + "items": { + "type": "string", + "enum": ["email", "phone_number", "username"] + }, + "minItems": 1, + "uniqueItems": true + }, + "user_verification_types": { + "type": "array", + "items": { + "type": "string", + "enum": ["email", "phone_number"] + } + }, + "unauthenticated_identities_enabled": { + "type": "boolean", + "default": true + }, + "mfa_configuration": { + "type": "string", + "enum": ["NONE", "OPTIONAL", "REQUIRED"] + }, + "mfa_methods": { + "type": "array", + "items": { + "enum": ["SMS", "TOTP"] + } + }, + "groups": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "propertyNames": { + "type": "string" + }, + "patternProperties": { + ".*": { + "$ref": "#/$defs/amplify_user_group_config" + } + } + } + } + }, + "required": ["aws_region", "user_pool_id", "user_pool_client_id"] + }, + "data": { + "description": "Outputs generated from defineData", + "type": "object", + "additionalProperties": false, + "properties": { + "aws_region": { + "$ref": "#/$defs/aws_region" + }, + "url": { + "description": "AppSync endpoint URL", + "type": "string" + }, + "model_introspection": { + "description": "generated model introspection schema for use with generateClient", + "type": "object" + }, + "api_key": { + "type": "string" + }, + "default_authorization_type": { + "$ref": "#/$defs/aws_appsync_authorization_type" + }, + "authorization_types": { + "type": "array", + "items": { + "$ref": "#/$defs/aws_appsync_authorization_type" + } + } + }, + "required": [ + "aws_region", + "url", + "default_authorization_type", + "authorization_types" + ] + }, + "geo": { + "description": "Outputs manually specified by developers for use with frontend library", + "type": "object", + "additionalProperties": false, + "properties": { + "aws_region": { + "description": "AWS Region of Amazon Location Service resources", + "$ref": "#/$defs/aws_region" + }, + "maps": { + "description": "Maps from Amazon Location Service", + "type": "object", + "additionalProperties": false, + "properties": { + "items": { + "type": "object", + "additionalProperties": false, + "propertyNames": { + "description": "Amazon Location Service Map name", + "type": "string" + }, + "patternProperties": { + ".*": { + "$ref": "#/$defs/amazon_location_service_config" + } + } + }, + "default": { + "type": "string" + } + }, + "required": ["items", "default"] + }, + "search_indices": { + "description": "Location search (search by places, addresses, coordinates)", + "type": "object", + "additionalProperties": false, + "properties": { + "items": { + "type": "array", + "uniqueItems": true, + "minItems": 1, + "items": { + "description": "Actual search name", + "type": "string" + } + }, + "default": { + "type": "string" + } + }, + "required": ["items", "default"] + }, + "geofence_collections": { + "description": "Geofencing (visualize virtual perimeters)", + "type": "object", + "additionalProperties": false, + "properties": { + "items": { + "type": "array", + "uniqueItems": true, + "minItems": 1, + "items": { + "description": "Geofence name", + "type": "string" + } + }, + "default": { + "type": "string" + } + }, + "required": ["items", "default"] + } + }, + "required": ["aws_region"] + }, + "notifications": { + "type": "object", + "description": "Outputs manually specified by developers for use with frontend library", + "additionalProperties": false, + "properties": { + "aws_region": { + "$ref": "#/$defs/aws_region" + }, + "amazon_pinpoint_app_id": { + "type": "string" + }, + "channels": { + "type": "array", + "items": { + "$ref": "#/$defs/amazon_pinpoint_channels" + }, + "minItems": 1, + "uniqueItems": true + } + }, + "required": ["aws_region", "amazon_pinpoint_app_id", "channels"] + }, + "storage": { + "type": "object", + "description": "Outputs generated from defineStorage", + "additionalProperties": false, + "properties": { + "aws_region": { + "$ref": "#/$defs/aws_region" + }, + "bucket_name": { + "type": "string" + }, + "buckets": { + "type": "array", + "items": { + "$ref": "#/$defs/amplify_storage_bucket" + } + } + }, + "required": ["aws_region", "bucket_name"] + }, + "custom": { + "description": "Outputs generated from backend.addOutput({ custom: })", + "type": "object" + } + }, + "required": ["version"], + "$defs": { + "amplify_storage_access_actions": { + "type": "string", + "enum": ["read", "get", "list", "write", "delete"] + }, + "amplify_storage_access_rule": { + "type": "object", + "additionalProperties": false, + "properties": { + "guest": { + "type": "array", + "items": { + "$ref": "#/$defs/amplify_storage_access_actions" + } + }, + "authenticated": { + "type": "array", + "items": { + "$ref": "#/$defs/amplify_storage_access_actions" + } + }, + "groups": { + "type": "array", + "items": { + "$ref": "#/$defs/amplify_storage_access_actions" + } + }, + "entity": { + "type": "array", + "items": { + "$ref": "#/$defs/amplify_storage_access_actions" + } + }, + "resource": { + "type": "array", + "items": { + "$ref": "#/$defs/amplify_storage_access_actions" + } + } + } + }, + "amplify_storage_bucket": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "bucket_name": { + "type": "string" + }, + "aws_region": { + "type": "string" + }, + "paths": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + ".*": { + "$ref": "#/$defs/amplify_storage_access_rule" + } + } + } + }, + "required": ["bucket_name", "aws_region", "name"] + }, + "aws_region": { + "type": "string" + }, + "amazon_cognito_standard_attributes": { + "description": "Amazon Cognito standard attributes for users -- https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html", + "type": "string", + "enum": [ + "address", + "birthdate", + "email", + "family_name", + "gender", + "given_name", + "locale", + "middle_name", + "name", + "nickname", + "phone_number", + "picture", + "preferred_username", + "profile", + "sub", + "updated_at", + "website", + "zoneinfo" + ] + }, + "aws_appsync_authorization_type": { + "description": "List of supported auth types for AWS AppSync", + "type": "string", + "enum": [ + "AMAZON_COGNITO_USER_POOLS", + "API_KEY", + "AWS_IAM", + "AWS_LAMBDA", + "OPENID_CONNECT" + ] + }, + "amazon_location_service_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "style": { + "description": "Map style", + "type": "string" + } + } + }, + "amazon_pinpoint_channels": { + "description": "supported channels for Amazon Pinpoint", + "type": "string", + "enum": ["IN_APP_MESSAGING", "FCM", "APNS", "EMAIL", "SMS"] + }, + "amplify_user_group_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "precedence": { + "type": "integer" + } + } + } + } +} diff --git a/packages/client-config/src/client-config-types/client_config.ts b/packages/client-config/src/client-config-types/client_config.ts index 5467484d672..501d52792b7 100644 --- a/packages/client-config/src/client-config-types/client_config.ts +++ b/packages/client-config/src/client-config-types/client_config.ts @@ -12,6 +12,7 @@ import * as clientConfigTypesV1 from '../client-config-schema/client_config_v1.j /* eslint-disable @typescript-eslint/naming-convention */ import * as clientConfigTypesV1_1 from '../client-config-schema/client_config_v1.1.js'; import * as clientConfigTypesV1_2 from '../client-config-schema/client_config_v1.2.js'; +import * as clientConfigTypesV1_3 from '../client-config-schema/client_config_v1.3.js'; /* eslint-enable @typescript-eslint/naming-convention */ /** @@ -34,24 +35,31 @@ export type ClientConfigLegacy = Partial< * ClientConfig = clientConfigTypesV1.AWSAmplifyBackendOutputs | clientConfigTypesV2.AWSAmplifyBackendOutputs; */ export type ClientConfig = + | clientConfigTypesV1_3.AWSAmplifyBackendOutputs | clientConfigTypesV1_2.AWSAmplifyBackendOutputs | clientConfigTypesV1_1.AWSAmplifyBackendOutputs | clientConfigTypesV1.AWSAmplifyBackendOutputs; -export { clientConfigTypesV1, clientConfigTypesV1_1, clientConfigTypesV1_2 }; +export { + clientConfigTypesV1, + clientConfigTypesV1_1, + clientConfigTypesV1_2, + clientConfigTypesV1_3, +}; export enum ClientConfigVersionOption { V0 = '0', // Legacy client config V1 = '1', V1_1 = '1.1', V1_2 = '1.2', + V1_3 = '1.3', } export type ClientConfigVersion = `${ClientConfigVersionOption}`; // Client config version that is generated by default if customers didn't specify one export const DEFAULT_CLIENT_CONFIG_VERSION: ClientConfigVersion = - ClientConfigVersionOption.V1_2; + ClientConfigVersionOption.V1_3; /** * Return type of `getClientConfig`. This types narrow the returned client config version @@ -64,7 +72,9 @@ export const DEFAULT_CLIENT_CONFIG_VERSION: ClientConfigVersion = * ? clientConfigTypesV2.AWSAmplifyBackendOutputs * : never; */ -export type ClientConfigVersionTemplateType = T extends '1.2' +export type ClientConfigVersionTemplateType = T extends '1.3' + ? clientConfigTypesV1_3.AWSAmplifyBackendOutputs + : T extends '1.2' ? clientConfigTypesV1_2.AWSAmplifyBackendOutputs : T extends '1.1' ? clientConfigTypesV1_1.AWSAmplifyBackendOutputs diff --git a/packages/client-config/src/client-config-writer/client_config_formatter_default.test.ts b/packages/client-config/src/client-config-writer/client_config_formatter_default.test.ts index e605ef8967a..b805229c07e 100644 --- a/packages/client-config/src/client-config-writer/client_config_formatter_default.test.ts +++ b/packages/client-config/src/client-config-writer/client_config_formatter_default.test.ts @@ -13,7 +13,7 @@ void describe('client config formatter', () => { const sampleIdentityPoolId = 'test_identity_pool_id'; const sampleUserPoolClientId = 'test_user_pool_client_id'; const clientConfig: ClientConfig = { - version: '1.2', + version: '1.3', auth: { aws_region: sampleRegion, identity_pool_id: sampleIdentityPoolId, @@ -23,7 +23,7 @@ void describe('client config formatter', () => { }; const expectedConfigReturned: ClientConfig = { - version: '1.2', + version: '1.3', auth: { aws_region: sampleRegion, identity_pool_id: sampleIdentityPoolId, diff --git a/packages/client-config/src/client-config-writer/client_config_formatter_legacy.test.ts b/packages/client-config/src/client-config-writer/client_config_formatter_legacy.test.ts index 737257273d9..38a8f098e8e 100644 --- a/packages/client-config/src/client-config-writer/client_config_formatter_legacy.test.ts +++ b/packages/client-config/src/client-config-writer/client_config_formatter_legacy.test.ts @@ -20,7 +20,7 @@ void describe('client config formatter', () => { const sampleUserPoolId = randomUUID(); const clientConfig: ClientConfig = { - version: '1.2', + version: '1.3', auth: { aws_region: sampleRegion, identity_pool_id: sampleIdentityPoolId, diff --git a/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.test.ts b/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.test.ts index dda5c288a99..ce5ce1b1105 100644 --- a/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.test.ts +++ b/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.test.ts @@ -26,7 +26,7 @@ void describe('ClientConfigLegacyConverter', () => { version: '3' as any, }), new AmplifyFault('UnsupportedClientConfigVersionFault', { - message: 'Only version 1.2 of ClientConfig is supported.', + message: 'Only version 1.3 of ClientConfig is supported.', }) ); }); @@ -35,7 +35,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); const v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_2, + version: ClientConfigVersionOption.V1_3, auth: { identity_pool_id: 'testIdentityPoolId', user_pool_id: 'testUserPoolId', @@ -133,7 +133,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); const v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_2, + version: ClientConfigVersionOption.V1_3, data: { aws_region: 'testRegion', url: 'testUrl', @@ -274,7 +274,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); const v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_2, + version: ClientConfigVersionOption.V1_3, storage: { aws_region: 'testRegion', bucket_name: 'testBucket', @@ -296,7 +296,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); const v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_2, + version: ClientConfigVersionOption.V1_3, custom: { customKey: { customNestedKey: { @@ -327,7 +327,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); const v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_2, + version: ClientConfigVersionOption.V1_3, analytics: { amazon_pinpoint: { aws_region: 'testRegion', @@ -356,7 +356,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); const v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_2, + version: ClientConfigVersionOption.V1_3, geo: { aws_region: 'testRegion', maps: { @@ -409,7 +409,7 @@ void describe('ClientConfigLegacyConverter', () => { const converter = new ClientConfigLegacyConverter(); let v1Config: ClientConfig = { - version: ClientConfigVersionOption.V1_2, + version: ClientConfigVersionOption.V1_3, notifications: { amazon_pinpoint_app_id: 'testAppId', aws_region: 'testRegion', @@ -452,7 +452,7 @@ void describe('ClientConfigLegacyConverter', () => { // both APNS and FCM cannot be specified together as they both map to Push. v1Config = { - version: ClientConfigVersionOption.V1_2, + version: ClientConfigVersionOption.V1_3, notifications: { amazon_pinpoint_app_id: 'testAppId', aws_region: 'testRegion', diff --git a/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.ts b/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.ts index 3131b041edb..c3b89dcf6e0 100644 --- a/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.ts +++ b/packages/client-config/src/client-config-writer/client_config_to_legacy_converter.ts @@ -2,7 +2,7 @@ import { AmplifyFault } from '@aws-amplify/platform-core'; import { ClientConfig, ClientConfigLegacy, - clientConfigTypesV1_2, + clientConfigTypesV1_3, } from '../client-config-types/client_config.js'; import { @@ -22,10 +22,10 @@ export class ClientConfigLegacyConverter { * Converts client config to a shape consumable by legacy libraries. */ convertToLegacyConfig = (clientConfig: ClientConfig): ClientConfigLegacy => { - // We can only convert from V1.2 of ClientConfig. For everything else, throw - if (!this.isClientConfigV1_2(clientConfig)) { + // We can only convert from V1.3 of ClientConfig. For everything else, throw + if (!this.isClientConfigV1_3(clientConfig)) { throw new AmplifyFault('UnsupportedClientConfigVersionFault', { - message: 'Only version 1.2 of ClientConfig is supported.', + message: 'Only version 1.3 of ClientConfig is supported.', }); } @@ -274,9 +274,9 @@ export class ClientConfigLegacyConverter { }; // eslint-disable-next-line @typescript-eslint/naming-convention - isClientConfigV1_2 = ( + isClientConfigV1_3 = ( clientConfig: ClientConfig - ): clientConfig is clientConfigTypesV1_2.AWSAmplifyBackendOutputs => { - return clientConfig.version === '1.2'; + ): clientConfig is clientConfigTypesV1_3.AWSAmplifyBackendOutputs => { + return clientConfig.version === '1.3'; }; } diff --git a/packages/client-config/src/client-config-writer/client_config_writer.test.ts b/packages/client-config/src/client-config-writer/client_config_writer.test.ts index 7f3771224de..e181d3deb28 100644 --- a/packages/client-config/src/client-config-writer/client_config_writer.test.ts +++ b/packages/client-config/src/client-config-writer/client_config_writer.test.ts @@ -42,7 +42,7 @@ void describe('client config writer', () => { }); const clientConfig: ClientConfig = { - version: '1.2', + version: '1.3', auth: { aws_region: sampleRegion, identity_pool_id: sampleIdentityPoolId, diff --git a/packages/client-config/src/generate_empty_client_config_to_file.test.ts b/packages/client-config/src/generate_empty_client_config_to_file.test.ts index 34dbbc9f82e..21abe85a034 100644 --- a/packages/client-config/src/generate_empty_client_config_to_file.test.ts +++ b/packages/client-config/src/generate_empty_client_config_to_file.test.ts @@ -30,15 +30,15 @@ void describe('generate empty client config to file', () => { path.join(process.cwd(), 'userOutDir', 'amplifyconfiguration.ts') ); }); - void it('correctly generates an empty file for client config version 1.2', async () => { + void it('correctly generates an empty file for client config version 1.3', async () => { await generateEmptyClientConfigToFile( - ClientConfigVersionOption.V1_2, + ClientConfigVersionOption.V1_3, 'userOutDir' ); assert.equal(writeFileMock.mock.callCount(), 1); assert.deepStrictEqual( writeFileMock.mock.calls[0].arguments[1], - `{\n "version": "1.2"\n}` + `{\n "version": "1.3"\n}` ); assert.deepStrictEqual( writeFileMock.mock.calls[0].arguments[0], diff --git a/packages/client-config/src/generate_empty_client_config_to_file.ts b/packages/client-config/src/generate_empty_client_config_to_file.ts index 260dad61739..b2563330b1a 100644 --- a/packages/client-config/src/generate_empty_client_config_to_file.ts +++ b/packages/client-config/src/generate_empty_client_config_to_file.ts @@ -15,7 +15,7 @@ export const generateEmptyClientConfigToFile = async ( format?: ClientConfigFormat ): Promise => { const clientConfig: ClientConfig = { - version: '1.2', + version: '1.3', }; return writeClientConfigToFile(clientConfig, version, outDir, format); }; diff --git a/packages/client-config/src/unified_client_config_generator.test.ts b/packages/client-config/src/unified_client_config_generator.test.ts index c466311a68f..496d59df9d1 100644 --- a/packages/client-config/src/unified_client_config_generator.test.ts +++ b/packages/client-config/src/unified_client_config_generator.test.ts @@ -26,6 +26,140 @@ const stubClientProvider = { }; void describe('UnifiedClientConfigGenerator', () => { void describe('generateClientConfig', () => { + void it('transforms backend output into client config for V1.3', async () => { + const groups = [ + { + ADMINS: { + precedence: 0, + }, + }, + { + EDITORS: { + precedence: 1, + }, + }, + ]; + const stubOutput: UnifiedBackendOutput = { + [platformOutputKey]: { + version: '1', + payload: { + deploymentType: 'branch', + region: 'us-east-1', + }, + }, + [authOutputKey]: { + version: '1', + payload: { + identityPoolId: 'testIdentityPoolId', + userPoolId: 'testUserPoolId', + webClientId: 'testWebClientId', + authRegion: 'us-east-1', + passwordPolicyMinLength: '8', + passwordPolicyRequirements: + '["REQUIRES_NUMBERS","REQUIRES_LOWERCASE","REQUIRES_UPPERCASE"]', + mfaTypes: '["SMS","TOTP"]', + mfaConfiguration: 'OPTIONAL', + verificationMechanisms: '["email","phone_number"]', + usernameAttributes: '["email"]', + signupAttributes: '["email"]', + allowUnauthenticatedIdentities: 'true', + groups: JSON.stringify(groups), + }, + }, + [graphqlOutputKey]: { + version: '1', + payload: { + awsAppsyncApiEndpoint: 'testApiEndpoint', + awsAppsyncRegion: 'us-east-1', + awsAppsyncAuthenticationType: 'API_KEY', + awsAppsyncAdditionalAuthenticationTypes: 'API_KEY', + awsAppsyncConflictResolutionMode: 'AUTO_MERGE', + awsAppsyncApiKey: 'testApiKey', + awsAppsyncApiId: 'testApiId', + amplifyApiModelSchemaS3Uri: 'testApiSchemaUri', + }, + }, + [customOutputKey]: { + version: '1', + payload: { + customOutputs: JSON.stringify({ + custom: { + output1: 'val1', + output2: 'val2', + }, + }), + }, + }, + }; + const outputRetrieval = mock.fn(async () => stubOutput); + const modelSchemaAdapter = new ModelIntrospectionSchemaAdapter( + stubClientProvider + ); + + mock.method( + modelSchemaAdapter, + 'getModelIntrospectionSchemaFromS3Uri', + () => undefined + ); + const configContributors = new ClientConfigContributorFactory( + modelSchemaAdapter + ).getContributors('1.3'); + const clientConfigGenerator = new UnifiedClientConfigGenerator( + outputRetrieval, + configContributors + ); + const result = await clientConfigGenerator.generateClientConfig(); + const expectedClientConfig: ClientConfig = { + auth: { + user_pool_id: 'testUserPoolId', + aws_region: 'us-east-1', + user_pool_client_id: 'testWebClientId', + identity_pool_id: 'testIdentityPoolId', + mfa_methods: ['SMS', 'TOTP'], + standard_required_attributes: ['email'], + username_attributes: ['email'], + user_verification_types: ['email', 'phone_number'], + mfa_configuration: 'OPTIONAL', + + password_policy: { + min_length: 8, + require_lowercase: true, + require_numbers: true, + require_symbols: false, + require_uppercase: true, + }, + + unauthenticated_identities_enabled: true, + groups: [ + { + ADMINS: { + precedence: 0, + }, + }, + { + EDITORS: { + precedence: 1, + }, + }, + ], + }, + data: { + url: 'testApiEndpoint', + aws_region: 'us-east-1', + api_key: 'testApiKey', + default_authorization_type: 'API_KEY', + authorization_types: ['API_KEY'], + }, + custom: { + output1: 'val1', + output2: 'val2', + }, + version: '1.3', + }; + + assert.deepStrictEqual(result, expectedClientConfig); + }); + void it('transforms backend output into client config for V1.2', async () => { const stubOutput: UnifiedBackendOutput = { [platformOutputKey]: { @@ -406,7 +540,7 @@ void describe('UnifiedClientConfigGenerator', () => { ); const configContributors = new ClientConfigContributorFactory( modelSchemaAdapter - ).getContributors('1.2'); //Generate with new configuration format + ).getContributors('1.3'); //Generate with new configuration format const clientConfigGenerator = new UnifiedClientConfigGenerator( outputRetrieval, configContributors @@ -438,7 +572,7 @@ void describe('UnifiedClientConfigGenerator', () => { output1: 'val1', output2: 'val2', }, - version: '1.2', // The max version prevails + version: '1.3', // The max version prevails }; assert.deepStrictEqual(result, expectedClientConfig); @@ -477,7 +611,7 @@ void describe('UnifiedClientConfigGenerator', () => { ); const configContributors = new ClientConfigContributorFactory( modelSchemaAdapter - ).getContributors('1.2'); + ).getContributors('1.3'); const clientConfigGenerator = new UnifiedClientConfigGenerator( outputRetrieval, @@ -509,7 +643,7 @@ void describe('UnifiedClientConfigGenerator', () => { const configContributors = new ClientConfigContributorFactory( modelSchemaAdapter - ).getContributors('1.2'); + ).getContributors('1.3'); const clientConfigGenerator = new UnifiedClientConfigGenerator( outputRetrieval, @@ -541,7 +675,7 @@ void describe('UnifiedClientConfigGenerator', () => { const configContributors = new ClientConfigContributorFactory( modelSchemaAdapter - ).getContributors('1.2'); + ).getContributors('1.3'); const clientConfigGenerator = new UnifiedClientConfigGenerator( outputRetrieval, @@ -604,7 +738,7 @@ void describe('UnifiedClientConfigGenerator', () => { const configContributors = new ClientConfigContributorFactory( modelSchemaAdapter - ).getContributors('1.2'); + ).getContributors('1.3'); const clientConfigGenerator = new UnifiedClientConfigGenerator( outputRetrieval, @@ -637,7 +771,7 @@ void describe('UnifiedClientConfigGenerator', () => { const configContributors = new ClientConfigContributorFactory( modelSchemaAdapter - ).getContributors('1.2'); + ).getContributors('1.3'); const clientConfigGenerator = new UnifiedClientConfigGenerator( outputRetrieval, diff --git a/packages/integration-tests/src/amplify_auth_credentials_factory.ts b/packages/integration-tests/src/amplify_auth_credentials_factory.ts index 7faffce0666..d21f9f8d86c 100644 --- a/packages/integration-tests/src/amplify_auth_credentials_factory.ts +++ b/packages/integration-tests/src/amplify_auth_credentials_factory.ts @@ -33,7 +33,7 @@ export class AmplifyAuthCredentialsFactory { */ constructor( private readonly cognitoIdentityProviderClient: CognitoIdentityProviderClient, - authConfig: NonNullable['auth']> + authConfig: NonNullable['auth']> ) { if (!authConfig.identity_pool_id) { throw new Error('Client config must have identity pool id.'); diff --git a/packages/integration-tests/src/test-project-setup/access_testing_project.ts b/packages/integration-tests/src/test-project-setup/access_testing_project.ts index e44010f82da..90295a0047c 100644 --- a/packages/integration-tests/src/test-project-setup/access_testing_project.ts +++ b/packages/integration-tests/src/test-project-setup/access_testing_project.ts @@ -147,7 +147,7 @@ class AccessTestingProjectTestProject extends TestProjectBase { backendId: BackendIdentifier ): Promise { await super.assertPostDeployment(backendId); - const clientConfig = await generateClientConfig(backendId, '1.2'); + const clientConfig = await generateClientConfig(backendId, '1.3'); await this.assertDifferentCognitoInstanceCannotAssumeAmplifyRoles( clientConfig ); @@ -160,7 +160,7 @@ class AccessTestingProjectTestProject extends TestProjectBase { * I.e. roles not created by auth construct. */ private assertGenericIamRolesAccessToData = async ( - clientConfig: ClientConfigVersionTemplateType<'1.2'> + clientConfig: ClientConfigVersionTemplateType<'1.3'> ) => { if (!clientConfig.custom) { throw new Error('Client config is missing custom section'); @@ -262,7 +262,7 @@ class AccessTestingProjectTestProject extends TestProjectBase { * This asserts that authenticated and unauthenticated roles have relevant access to data API. */ private assertAmplifyAuthAccessToData = async ( - clientConfig: ClientConfigVersionTemplateType<'1.2'> + clientConfig: ClientConfigVersionTemplateType<'1.3'> ): Promise => { if (!clientConfig.auth) { throw new Error('Client config is missing auth section'); @@ -367,7 +367,7 @@ class AccessTestingProjectTestProject extends TestProjectBase { * unauthorized roles. I.e. it tests trust policy. */ private assertDifferentCognitoInstanceCannotAssumeAmplifyRoles = async ( - clientConfig: ClientConfigVersionTemplateType<'1.2'> + clientConfig: ClientConfigVersionTemplateType<'1.3'> ): Promise => { const simpleAuthUser = await this.createAuthenticatedSimpleAuthCognitoUser( clientConfig @@ -416,7 +416,7 @@ class AccessTestingProjectTestProject extends TestProjectBase { }; private createAuthenticatedSimpleAuthCognitoUser = async ( - clientConfig: ClientConfigVersionTemplateType<'1.2'> + clientConfig: ClientConfigVersionTemplateType<'1.3'> ): Promise => { if (!clientConfig.custom) { throw new Error('Client config is missing custom section'); @@ -496,7 +496,7 @@ class AccessTestingProjectTestProject extends TestProjectBase { }; private createAppSyncClient = ( - clientConfig: ClientConfigVersionTemplateType<'1.2'>, + clientConfig: ClientConfigVersionTemplateType<'1.3'>, credentials: IamCredentials ): ApolloClient => { if (!clientConfig.data?.url) { diff --git a/packages/integration-tests/src/test-project-setup/cdk/auth_cdk_project.ts b/packages/integration-tests/src/test-project-setup/cdk/auth_cdk_project.ts index 7010f5d2957..62282b3fd6f 100644 --- a/packages/integration-tests/src/test-project-setup/cdk/auth_cdk_project.ts +++ b/packages/integration-tests/src/test-project-setup/cdk/auth_cdk_project.ts @@ -78,7 +78,7 @@ class AuthTestCdkProject extends TestCdkProjectBase { { stackName: this.stackName, }, - '1.2', //version of the config + '1.3', //version of the config awsClientProvider ); diff --git a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts index f65f1f48c7f..b5a1658dd03 100644 --- a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts +++ b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts @@ -325,7 +325,7 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { assert.ok(fileContent.includes('newKey: string;')); // Env var added via addEnvironment assert.ok(fileContent.includes('TEST_SECRET: string;')); // Env var added via defineFunction - // assert storage access paths are correct in stack outputs + // assert specific config are correct in the outputs file const outputsObject = JSON.parse( await fs.readFile( path.join(this.projectDirPath, 'amplify_outputs.json'), @@ -349,6 +349,21 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { }, }) ); + + assert.ok( + isMatch(outputsObject.auth.groups, [ + { + Editors: { + precedence: 2, // previously 0 but was overwritten + }, + }, + { + Admins: { + precedence: 1, + }, + }, + ]) + ); } private getUpdateReplacementDefinition = (suffix: string) => ({ diff --git a/packages/integration-tests/src/test-projects/custom-outputs/amplify/backend.ts b/packages/integration-tests/src/test-projects/custom-outputs/amplify/backend.ts index 82b87ac6555..ceed80b636b 100644 --- a/packages/integration-tests/src/test-projects/custom-outputs/amplify/backend.ts +++ b/packages/integration-tests/src/test-projects/custom-outputs/amplify/backend.ts @@ -16,7 +16,7 @@ const sampleIdentityPoolId = 'test_identity_pool_id'; const sampleUserPoolClientId = 'test_user_pool_client_id'; backend.addOutput({ - version: '1.2', + version: '1.3', custom: { // test deploy time values restApiUrl: restApi.url, @@ -26,7 +26,7 @@ backend.addOutput({ }); backend.addOutput({ - version: '1.2', + version: '1.3', custom: { // test synth time values // and composition of config @@ -36,7 +36,7 @@ backend.addOutput({ const fakeCognitoUserPoolId = 'fakeCognitoUserPoolId'; backend.addOutput({ - version: '1.2', + version: '1.3', // test reserved key auth: { aws_region: sampleRegion, diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts index 097a822ddb2..92733f12ec8 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts @@ -24,5 +24,5 @@ export const auth = defineAuth({ triggers: { postConfirmation: defaultNodeFunc, }, - groups: ['Admins'], + groups: ['Editors', 'Admins'], }); diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/backend.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/backend.ts index 4cd85ed1e3e..d9d298e5557 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/backend.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/backend.ts @@ -7,6 +7,9 @@ import { Stack } from 'aws-cdk-lib'; const backend = defineBackend(dataStorageAuthWithTriggers); backend.defaultNodeFunc.addEnvironment('newKey', 'newValue'); +// Change precedence of Editors group so Admins group has the lowest precedence +backend.auth.resources.groups['Editors'].cfnUserGroup.precedence = 2; + const scheduleFunctionLambda = backend.funcWithSchedule.resources.lambda; const scheduleFunctionLambdaRole = scheduleFunctionLambda.role; const queueStack = Stack.of(scheduleFunctionLambda); From 0d6489d369d01bea83bf845be388ed7f6c13f957 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 21 Oct 2024 14:03:27 -0700 Subject: [PATCH 053/199] Use AiModel from data-schema-types as possible input (#2133) --- .changeset/happy-jokes-double.md | 5 +++ .changeset/tough-taxis-tan.md | 5 +++ package-lock.json | 38 +++++++++---------- packages/backend-ai/API.md | 5 +-- packages/backend-ai/package.json | 1 + .../backend-ai/src/conversation/factory.ts | 8 +--- packages/backend-data/package.json | 2 +- 7 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 .changeset/happy-jokes-double.md create mode 100644 .changeset/tough-taxis-tan.md diff --git a/.changeset/happy-jokes-double.md b/.changeset/happy-jokes-double.md new file mode 100644 index 00000000000..54c395a105d --- /dev/null +++ b/.changeset/happy-jokes-double.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-data': patch +--- + +Update data-schema-types diff --git a/.changeset/tough-taxis-tan.md b/.changeset/tough-taxis-tan.md new file mode 100644 index 00000000000..a7d71c529e5 --- /dev/null +++ b/.changeset/tough-taxis-tan.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-ai': patch +--- + +Use AiModel from data-schema-types as possible input diff --git a/package-lock.json b/package-lock.json index dbc1f5fbc8d..4006b75aec4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2796,10 +2796,9 @@ } }, "node_modules/@aws-amplify/data-schema-types": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema-types/-/data-schema-types-1.1.1.tgz", - "integrity": "sha512-WhWEEsztpSSxIY0lJ3Ge5iA4g3PBm66SQmy1fBH1FBq0T+cxUBijifOU8MNwf+tf6lGpArMX0RS54HRVF5fUSA==", - "license": "Apache-2.0", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema-types/-/data-schema-types-1.2.0.tgz", + "integrity": "sha512-1hy2r7jl3hQ5J/CGjhmPhFPcdGSakfme1ZLjlTMJZILfYifZLSlGRKNCelMb3J5N9203hyeT5XDi5yR47JL1TQ==", "dependencies": { "graphql": "15.8.0", "rxjs": "^7.8.1" @@ -31455,7 +31454,7 @@ }, "packages/ai-constructs": { "name": "@aws-amplify/ai-constructs", - "version": "0.4.0", + "version": "0.6.1", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.3.0", @@ -31495,15 +31494,15 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.4.0", + "version": "1.5.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-auth": "^1.2.0", "@aws-amplify/backend-data": "^1.1.4", - "@aws-amplify/backend-function": "^1.6.0", + "@aws-amplify/backend-function": "^1.7.0", "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/backend-secret": "^1.1.2", + "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.1", "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/data-schema": "^1.0.0", @@ -31524,12 +31523,13 @@ }, "packages/backend-ai": { "name": "@aws-amplify/backend-ai", - "version": "0.3.0", + "version": "0.3.2", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.4.0", + "@aws-amplify/ai-constructs": "^0.6.0", "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.0.2", + "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.0.1" }, @@ -31564,7 +31564,7 @@ "@aws-amplify/backend-output-schemas": "^1.1.0", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/data-construct": "^1.10.1", - "@aws-amplify/data-schema-types": "^1.1.1", + "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/plugin-types": "^1.2.2" }, "devDependencies": { @@ -31579,7 +31579,7 @@ }, "packages/backend-deployer": { "name": "@aws-amplify/backend-deployer", - "version": "1.1.4", + "version": "1.1.5", "license": "Apache-2.0", "dependencies": { "@aws-amplify/platform-core": "^1.0.6", @@ -31594,7 +31594,7 @@ }, "packages/backend-function": { "name": "@aws-amplify/backend-function", - "version": "1.6.0", + "version": "1.7.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", @@ -31664,7 +31664,7 @@ }, "packages/backend-secret": { "name": "@aws-amplify/backend-secret", - "version": "1.1.3", + "version": "1.1.4", "license": "Apache-2.0", "dependencies": { "@aws-amplify/platform-core": "^1.0.5", @@ -31986,7 +31986,7 @@ }, "packages/deployed-backend-client": { "name": "@aws-amplify/deployed-backend-client", - "version": "1.4.1", + "version": "1.4.2", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", @@ -32038,11 +32038,11 @@ "license": "Apache-2.0", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.4.0", + "@aws-amplify/ai-constructs": "^0.6.0", "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend": "^1.4.0", - "@aws-amplify/backend-ai": "^0.3.0", - "@aws-amplify/backend-secret": "^1.1.2", + "@aws-amplify/backend": "^1.5.0", + "@aws-amplify/backend-ai": "^0.3.2", + "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/deployed-backend-client": "^1.4.1", diff --git a/packages/backend-ai/API.md b/packages/backend-ai/API.md index b9e044d8206..bd810d42741 100644 --- a/packages/backend-ai/API.md +++ b/packages/backend-ai/API.md @@ -4,6 +4,7 @@ ```ts +import { AiModel } from '@aws-amplify/data-schema-types'; import { ConstructFactory } from '@aws-amplify/plugin-types'; import { ConversationTurnEventVersion } from '@aws-amplify/ai-constructs/conversation'; import { FunctionResources } from '@aws-amplify/plugin-types'; @@ -49,9 +50,7 @@ type DefineConversationHandlerFunctionProps = { name: string; entry?: string; models: Array<{ - modelId: string | { - resourcePath: string; - }; + modelId: string | AiModel; region?: string; }>; }; diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 78b578dfde4..0ecd974bada 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -25,6 +25,7 @@ "@aws-amplify/ai-constructs": "^0.6.0", "@aws-amplify/backend-output-schemas": "^1.3.0", "@aws-amplify/backend-output-storage": "^1.0.2", + "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.0.1" }, diff --git a/packages/backend-ai/src/conversation/factory.ts b/packages/backend-ai/src/conversation/factory.ts index b75b6d49c4f..811f3f7fbdc 100644 --- a/packages/backend-ai/src/conversation/factory.ts +++ b/packages/backend-ai/src/conversation/factory.ts @@ -15,6 +15,7 @@ import { } from '@aws-amplify/ai-constructs/conversation'; import path from 'path'; import { CallerDirectoryExtractor } from '@aws-amplify/platform-core'; +import { AiModel } from '@aws-amplify/data-schema-types'; class ConversationHandlerFunctionGenerator implements ConstructContainerEntryGenerator @@ -115,12 +116,7 @@ export type DefineConversationHandlerFunctionProps = { name: string; entry?: string; models: Array<{ - modelId: - | string - | { - // This is to match return of 'a.ai.model.anthropic.claude3Haiku()' - resourcePath: string; - }; + modelId: string | AiModel; region?: string; }>; }; diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index 3f31e6e5077..e61bdddc0ef 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -32,6 +32,6 @@ "@aws-amplify/backend-output-schemas": "^1.1.0", "@aws-amplify/data-construct": "^1.10.1", "@aws-amplify/plugin-types": "^1.2.2", - "@aws-amplify/data-schema-types": "^1.1.1" + "@aws-amplify/data-schema-types": "^1.2.0" } } From 34b18092294aafe450a262fc17a088640750dc9e Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 21 Oct 2024 14:37:13 -0700 Subject: [PATCH 054/199] Split sandbox and deployment tests (#2129) * refactor e2e * fix that * try this * disable these for now * disable these for now * ahh * try this * try this * try this * that's not needed * try this * flip dimensions order * try this * try this * Revert "try this" This reverts commit 40ff4a45cc371b5024024c765339477d7e1ae8b4. * try this * try this * try this * try this --- .changeset/ninety-cherries-applaud.md | 2 + .eslint_dictionary.json | 3 + .github/workflows/health_checks.yml | 58 ++++-- .../src/find_deployed_resource.ts | 7 +- .../src/setup_test_directory.ts | 7 + .../src/test-e2e/deployment.test.ts | 191 ------------------ .../access_testing_project.deployment.test.ts | 4 + .../auth_cdk_project.deployment.test.ts | 4 + .../cdk.deployment.test.template.ts | 50 +++++ ...rsation_handler_project.deployment.test.ts | 4 + .../custom_outputs.deployment.test.ts | 4 + ...rage_auth_with_triggers.deployment.test.ts | 4 + .../deployment/deployment.test.template.ts | 171 ++++++++++++++++ ..._with_typescript_idioms.deployment.test.ts | 4 + .../src/test-e2e/sandbox.test.ts | 110 ---------- .../access_testing_project.sandbox.test.ts | 4 + ...nversation_handler_project.sandbox.test.ts | 4 + .../sandbox/custom_outputs.sandbox.test.ts | 4 + ...storage_auth_with_triggers.sandbox.test.ts | 4 + ...mal_with_typescript_idioms.sandbox.test.ts | 4 + .../test-e2e/sandbox/sandbox.test.template.ts | 107 ++++++++++ .../access_testing_project.ts | 21 +- .../cdk/auth_cdk_project.ts | 4 +- .../cdk/test_cdk_project_creator.ts | 16 -- .../conversation_handler_project.ts | 29 ++- .../src/test-project-setup/custom_outputs.ts | 9 +- .../data_storage_auth_with_triggers.ts | 34 +++- .../minimal_with_typescript_idioms.ts | 9 +- .../test_project_creator.ts | 72 ------- .../sparse_test_matrix_generator.test.ts | 61 ++++++ .../sparse_test_matrix_generator.ts | 93 +++++++++ .../sparse-generator-test-stubs/test1.test.ts | 1 + .../sparse-generator-test-stubs/test2.test.ts | 1 + .../sparse-generator-test-stubs/test3.test.ts | 1 + scripts/generate_sparse_test_matrix.ts | 31 +++ 35 files changed, 694 insertions(+), 438 deletions(-) create mode 100644 .changeset/ninety-cherries-applaud.md delete mode 100644 packages/integration-tests/src/test-e2e/deployment.test.ts create mode 100644 packages/integration-tests/src/test-e2e/deployment/access_testing_project.deployment.test.ts create mode 100644 packages/integration-tests/src/test-e2e/deployment/auth_cdk_project.deployment.test.ts create mode 100644 packages/integration-tests/src/test-e2e/deployment/cdk.deployment.test.template.ts create mode 100644 packages/integration-tests/src/test-e2e/deployment/conversation_handler_project.deployment.test.ts create mode 100644 packages/integration-tests/src/test-e2e/deployment/custom_outputs.deployment.test.ts create mode 100644 packages/integration-tests/src/test-e2e/deployment/data_storage_auth_with_triggers.deployment.test.ts create mode 100644 packages/integration-tests/src/test-e2e/deployment/deployment.test.template.ts create mode 100644 packages/integration-tests/src/test-e2e/deployment/minimal_with_typescript_idioms.deployment.test.ts delete mode 100644 packages/integration-tests/src/test-e2e/sandbox.test.ts create mode 100644 packages/integration-tests/src/test-e2e/sandbox/access_testing_project.sandbox.test.ts create mode 100644 packages/integration-tests/src/test-e2e/sandbox/conversation_handler_project.sandbox.test.ts create mode 100644 packages/integration-tests/src/test-e2e/sandbox/custom_outputs.sandbox.test.ts create mode 100644 packages/integration-tests/src/test-e2e/sandbox/data_storage_auth_with_triggers.sandbox.test.ts create mode 100644 packages/integration-tests/src/test-e2e/sandbox/minimal_with_typescript_idioms.sandbox.test.ts create mode 100644 packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts create mode 100644 scripts/components/sparse_test_matrix_generator.test.ts create mode 100644 scripts/components/sparse_test_matrix_generator.ts create mode 100644 scripts/components/test-resources/sparse-generator-test-stubs/test1.test.ts create mode 100644 scripts/components/test-resources/sparse-generator-test-stubs/test2.test.ts create mode 100644 scripts/components/test-resources/sparse-generator-test-stubs/test3.test.ts create mode 100644 scripts/generate_sparse_test_matrix.ts diff --git a/.changeset/ninety-cherries-applaud.md b/.changeset/ninety-cherries-applaud.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/ninety-cherries-applaud.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index 2e0a49ae60c..c987d863b63 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -19,6 +19,7 @@ "birthdate", "bundler", "callee", + "cartesian", "cdk", "changelog", "changeset", @@ -170,6 +171,7 @@ "tslint", "typename", "typeof", + "ubuntu", "unauth", "unix", "unlink", @@ -188,6 +190,7 @@ "wildcards", "workspace", "writev", + "xlarge", "yaml", "yargs", "zoneinfo" diff --git a/.github/workflows/health_checks.yml b/.github/workflows/health_checks.yml index e9aafec01aa..8c1bff59935 100644 --- a/.github/workflows/health_checks.yml +++ b/.github/workflows/health_checks.yml @@ -206,25 +206,34 @@ jobs: run: npm run test:dir packages/integration-tests/lib/test-e2e/amplify_outputs_backwards_compatibility.test.js env: BASELINE_DIR: ${{ steps.setup_baseline_version.outputs.baseline_dir }} + e2e_generate_deployment_tests_matrix: + if: needs.do_include_e2e.outputs.run_e2e == 'true' + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.generateMatrix.outputs.matrix }} + timeout-minutes: 5 + needs: + - do_include_e2e + - build + steps: + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 + - uses: ./.github/actions/restore_build_cache + - run: echo "$(npx tsx scripts/generate_sparse_test_matrix.ts 'packages/integration-tests/lib/test-e2e/deployment/*.deployment.test.js')" + - id: generateMatrix + run: echo "matrix=$(npx tsx scripts/generate_sparse_test_matrix.ts 'packages/integration-tests/lib/test-e2e/deployment/*.deployment.test.js')" >> "$GITHUB_OUTPUT" e2e_deployment: if: needs.do_include_e2e.outputs.run_e2e == 'true' strategy: # will finish running other test matrices even if one fails fail-fast: false - matrix: - os: [ubuntu-latest, macos-14-xlarge, windows-latest] - node-version: [18, 20] - # skip multiple node version test on other os - exclude: - - os: macos-14-xlarge - node-version: 20 - - os: windows-latest - node-version: 20 + matrix: ${{ fromJson(needs.e2e_generate_deployment_tests_matrix.outputs.matrix) }} runs-on: ${{ matrix.os }} + name: e2e_deployment ${{ matrix.displayNames }} ${{ matrix.node-version }} ${{ matrix.os }} timeout-minutes: ${{ matrix.os == 'windows-latest' && 35 || 25 }} needs: - do_include_e2e - build + - e2e_generate_deployment_tests_matrix permissions: # these permissions are required for the configure-aws-credentials action to get a JWT from GitHub id-token: write @@ -238,26 +247,35 @@ jobs: node_version: ${{ matrix.node-version }} link_cli: true run: | - npm run test:dir packages/integration-tests/lib/test-e2e/deployment.test.js + npm run test:dir ${{ matrix.testPaths }} + e2e_generate_sandbox_tests_matrix: + if: needs.do_include_e2e.outputs.run_e2e == 'true' + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.generateMatrix.outputs.matrix }} + timeout-minutes: 5 + needs: + - do_include_e2e + - build + steps: + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 + - uses: ./.github/actions/restore_build_cache + - run: echo "$(npx tsx scripts/generate_sparse_test_matrix.ts 'packages/integration-tests/lib/test-e2e/sandbox/*.sandbox.test.js')" + - id: generateMatrix + run: echo "matrix=$(npx tsx scripts/generate_sparse_test_matrix.ts 'packages/integration-tests/lib/test-e2e/sandbox/*.sandbox.test.js')" >> "$GITHUB_OUTPUT" e2e_sandbox: if: needs.do_include_e2e.outputs.run_e2e == 'true' strategy: # will finish running other test matrices even if one fails fail-fast: false - matrix: - os: [ubuntu-latest, macos-14-xlarge, windows-latest] - node-version: [18, 20] - # skip multiple node version test on other os - exclude: - - os: macos-14-xlarge - node-version: 20 - - os: windows-latest - node-version: 20 + matrix: ${{ fromJson(needs.e2e_generate_sandbox_tests_matrix.outputs.matrix) }} runs-on: ${{ matrix.os }} + name: e2e_sandbox ${{ matrix.displayNames }} ${{ matrix.node-version }} ${{ matrix.os }} timeout-minutes: ${{ matrix.os == 'windows-latest' && 35 || 25 }} needs: - do_include_e2e - build + - e2e_generate_sandbox_tests_matrix permissions: # these permissions are required for the configure-aws-credentials action to get a JWT from GitHub id-token: write @@ -270,7 +288,7 @@ jobs: e2e_test_accounts: ${{ vars.E2E_TEST_ACCOUNTS }} node_version: ${{ matrix.node-version }} link_cli: true - run: npm run test:dir packages/integration-tests/lib/test-e2e/sandbox.test.js + run: npm run test:dir ${{ matrix.testPaths }} e2e_backend_output: if: needs.do_include_e2e.outputs.run_e2e == 'true' runs-on: ubuntu-latest diff --git a/packages/integration-tests/src/find_deployed_resource.ts b/packages/integration-tests/src/find_deployed_resource.ts index ef8830e680d..933415ddd30 100644 --- a/packages/integration-tests/src/find_deployed_resource.ts +++ b/packages/integration-tests/src/find_deployed_resource.ts @@ -4,6 +4,7 @@ import { DescribeStackResourcesCommand, } from '@aws-sdk/client-cloudformation'; import { BackendIdentifierConversions } from '@aws-amplify/platform-core'; +import { e2eToolingClientConfig } from './e2e_tooling_client_config.js'; export type StringPredicate = (str: string) => boolean; @@ -14,7 +15,11 @@ export class DeployedResourcesFinder { /** * Construct with a cfnClient */ - constructor(private readonly cfnClient: CloudFormationClient) {} + constructor( + private readonly cfnClient: CloudFormationClient = new CloudFormationClient( + e2eToolingClientConfig + ) + ) {} /** * Find resources of type "resourceType" within the stack defined by "backendId" diff --git a/packages/integration-tests/src/setup_test_directory.ts b/packages/integration-tests/src/setup_test_directory.ts index 0939e5709cd..8cddf8b2cf1 100644 --- a/packages/integration-tests/src/setup_test_directory.ts +++ b/packages/integration-tests/src/setup_test_directory.ts @@ -22,6 +22,13 @@ export const createTestDirectory = async (pathName: string | URL) => { * Delete a test directory. */ export const deleteTestDirectory = async (pathName: string | URL) => { + if (process.env.CI) { + // We don't have to delete test directories in CI. + // The VMs are ephemeral. + // On the other hand we want to keep shared parent directories for test projects + // for tests executing in parallel on the same VM. + return; + } if (existsSync(pathName)) { await fs.rm(pathName, { recursive: true, force: true }); } diff --git a/packages/integration-tests/src/test-e2e/deployment.test.ts b/packages/integration-tests/src/test-e2e/deployment.test.ts deleted file mode 100644 index c9ea7500de4..00000000000 --- a/packages/integration-tests/src/test-e2e/deployment.test.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { after, afterEach, before, beforeEach, describe, it } from 'node:test'; -import { - createTestDirectory, - deleteTestDirectory, - rootTestDir, -} from '../setup_test_directory.js'; -import fs from 'fs/promises'; -import { shortUuid } from '../short_uuid.js'; -import { getTestProjectCreators } from '../test-project-setup/test_project_creator.js'; -import { TestProjectBase } from '../test-project-setup/test_project_base.js'; -import { PredicatedActionBuilder } from '../process-controller/predicated_action_queue_builder.js'; -import { ampxCli } from '../process-controller/process_controller.js'; -import path from 'path'; -import { - interruptSandbox, - rejectCleanupSandbox, -} from '../process-controller/predicated_action_macros.js'; -import assert from 'node:assert'; -import { TestBranch, amplifyAppPool } from '../amplify_app_pool.js'; -import { BackendIdentifier } from '@aws-amplify/plugin-types'; -import { ClientConfigFormat } from '@aws-amplify/client-config'; -import { testConcurrencyLevel } from './test_concurrency.js'; -import { TestCdkProjectBase } from '../test-project-setup/cdk/test_cdk_project_base.js'; -import { getTestCdkProjectCreators } from '../test-project-setup/cdk/test_cdk_project_creator.js'; - -const testProjectCreators = getTestProjectCreators(); -const testCdkProjectCreators = getTestCdkProjectCreators(); -void describe('deployment tests', { concurrency: testConcurrencyLevel }, () => { - before(async () => { - await createTestDirectory(rootTestDir); - }); - after(async () => { - await deleteTestDirectory(rootTestDir); - }); - - void describe('amplify deploys', async () => { - testProjectCreators.forEach((testProjectCreator) => { - void describe(`branch deploys ${testProjectCreator.name}`, () => { - let branchBackendIdentifier: BackendIdentifier; - let testBranch: TestBranch; - let testProject: TestProjectBase; - - beforeEach(async () => { - testProject = await testProjectCreator.createProject(rootTestDir); - testBranch = await amplifyAppPool.createTestBranch(); - branchBackendIdentifier = { - namespace: testBranch.appId, - name: testBranch.branchName, - type: 'branch', - }; - }); - - afterEach(async () => { - await testProject.tearDown(branchBackendIdentifier); - }); - - void it(`[${testProjectCreator.name}] deploys fully`, async () => { - await testProject.deploy(branchBackendIdentifier); - await testProject.assertPostDeployment(branchBackendIdentifier); - const testBranchDetails = await amplifyAppPool.fetchTestBranchDetails( - testBranch - ); - assert.ok( - testBranchDetails.backend?.stackArn, - 'branch should have stack associated' - ); - assert.ok( - testBranchDetails.backend?.stackArn?.includes( - branchBackendIdentifier.namespace - ) - ); - assert.ok( - testBranchDetails.backend?.stackArn?.includes( - branchBackendIdentifier.name - ) - ); - - // test generating all client formats - for (const format of [ - ClientConfigFormat.DART, - ClientConfigFormat.JSON, - ]) { - await ampxCli( - [ - 'generate', - 'outputs', - '--branch', - testBranch.branchName, - '--app-id', - testBranch.appId, - '--format', - format, - ], - testProject.projectDirPath - ).run(); - - await testProject.assertClientConfigExists( - testProject.projectDirPath, - format - ); - } - }); - }); - }); - - void describe('fails on compilation error', async () => { - let testProject: TestProjectBase; - before(async () => { - // any project is fine - testProject = await testProjectCreators[0].createProject(rootTestDir); - await fs.cp( - testProject.sourceProjectAmplifyDirURL, - testProject.projectAmplifyDirPath, - { - recursive: true, - } - ); - - // inject failure - await fs.appendFile( - path.join(testProject.projectAmplifyDirPath, 'backend.ts'), - "this won't compile" - ); - }); - - void describe('in sequence', { concurrency: false }, () => { - void it('in sandbox deploy', async () => { - await ampxCli( - ['sandbox', '--dirToWatch', 'amplify'], - testProject.projectDirPath - ) - .do( - new PredicatedActionBuilder().waitForLineIncludes( - 'TypeScript validation check failed' - ) - ) - .do(interruptSandbox()) - .do(rejectCleanupSandbox()) - .run(); - }); - - void it('in pipeline deploy', async () => { - await assert.rejects(() => - ampxCli( - [ - 'pipeline-deploy', - '--branch', - 'test-branch', - '--app-id', - `test-${shortUuid()}`, - ], - testProject.projectDirPath, - { - env: { CI: 'true' }, - } - ) - .do( - new PredicatedActionBuilder().waitForLineIncludes( - 'TypeScript validation check failed' - ) - ) - .run() - ); - }); - }); - }); - }); - - void describe('cdk deploys', () => { - testCdkProjectCreators.forEach((testCdkProjectCreator) => { - void describe(`${testCdkProjectCreator.name}`, () => { - let testCdkProject: TestCdkProjectBase; - - beforeEach(async () => { - testCdkProject = await testCdkProjectCreator.createProject( - rootTestDir - ); - }); - - afterEach(async () => { - await testCdkProject.tearDown(); - }); - - void it(`deploys`, async () => { - await testCdkProject.deploy(); - await testCdkProject.assertPostDeployment(); - }); - }); - }); - }); -}); diff --git a/packages/integration-tests/src/test-e2e/deployment/access_testing_project.deployment.test.ts b/packages/integration-tests/src/test-e2e/deployment/access_testing_project.deployment.test.ts new file mode 100644 index 00000000000..6730123191a --- /dev/null +++ b/packages/integration-tests/src/test-e2e/deployment/access_testing_project.deployment.test.ts @@ -0,0 +1,4 @@ +import { AccessTestingProjectTestProjectCreator } from '../../test-project-setup/access_testing_project.js'; +import { defineDeploymentTest } from './deployment.test.template.js'; + +defineDeploymentTest(new AccessTestingProjectTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/deployment/auth_cdk_project.deployment.test.ts b/packages/integration-tests/src/test-e2e/deployment/auth_cdk_project.deployment.test.ts new file mode 100644 index 00000000000..2b6f3246246 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/deployment/auth_cdk_project.deployment.test.ts @@ -0,0 +1,4 @@ +import { defineCdkDeploymentTest } from './cdk.deployment.test.template.js'; +import { AuthTestCdkProjectCreator } from '../../test-project-setup/cdk/auth_cdk_project.js'; + +defineCdkDeploymentTest(new AuthTestCdkProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/deployment/cdk.deployment.test.template.ts b/packages/integration-tests/src/test-e2e/deployment/cdk.deployment.test.template.ts new file mode 100644 index 00000000000..1bfa79ce14c --- /dev/null +++ b/packages/integration-tests/src/test-e2e/deployment/cdk.deployment.test.template.ts @@ -0,0 +1,50 @@ +import { after, afterEach, before, beforeEach, describe, it } from 'node:test'; +import { + createTestDirectory, + deleteTestDirectory, + rootTestDir, +} from '../../setup_test_directory.js'; +import { testConcurrencyLevel } from '../test_concurrency.js'; +import { TestCdkProjectBase } from '../../test-project-setup/cdk/test_cdk_project_base.js'; +import { TestCdkProjectCreator } from '../../test-project-setup/cdk/test_cdk_project_creator.js'; + +/** + * Defines cdk deployment test + */ +export const defineCdkDeploymentTest = ( + testCdkProjectCreator: TestCdkProjectCreator +) => { + void describe( + 'cdk deployment tests', + { concurrency: testConcurrencyLevel }, + () => { + before(async () => { + await createTestDirectory(rootTestDir); + }); + after(async () => { + await deleteTestDirectory(rootTestDir); + }); + + void describe('cdk deploys', () => { + void describe(`${testCdkProjectCreator.name}`, () => { + let testCdkProject: TestCdkProjectBase; + + beforeEach(async () => { + testCdkProject = await testCdkProjectCreator.createProject( + rootTestDir + ); + }); + + afterEach(async () => { + await testCdkProject.tearDown(); + }); + + void it(`deploys`, async () => { + await testCdkProject.deploy(); + await testCdkProject.assertPostDeployment(); + }); + }); + }); + } + ); +}; diff --git a/packages/integration-tests/src/test-e2e/deployment/conversation_handler_project.deployment.test.ts b/packages/integration-tests/src/test-e2e/deployment/conversation_handler_project.deployment.test.ts new file mode 100644 index 00000000000..b26f10daeae --- /dev/null +++ b/packages/integration-tests/src/test-e2e/deployment/conversation_handler_project.deployment.test.ts @@ -0,0 +1,4 @@ +import { ConversationHandlerTestProjectCreator } from '../../test-project-setup/conversation_handler_project.js'; +import { defineDeploymentTest } from './deployment.test.template.js'; + +defineDeploymentTest(new ConversationHandlerTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/deployment/custom_outputs.deployment.test.ts b/packages/integration-tests/src/test-e2e/deployment/custom_outputs.deployment.test.ts new file mode 100644 index 00000000000..4654bac4f4e --- /dev/null +++ b/packages/integration-tests/src/test-e2e/deployment/custom_outputs.deployment.test.ts @@ -0,0 +1,4 @@ +import { CustomOutputsTestProjectCreator } from '../../test-project-setup/custom_outputs.js'; +import { defineDeploymentTest } from './deployment.test.template.js'; + +defineDeploymentTest(new CustomOutputsTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/deployment/data_storage_auth_with_triggers.deployment.test.ts b/packages/integration-tests/src/test-e2e/deployment/data_storage_auth_with_triggers.deployment.test.ts new file mode 100644 index 00000000000..0fd2ea5062d --- /dev/null +++ b/packages/integration-tests/src/test-e2e/deployment/data_storage_auth_with_triggers.deployment.test.ts @@ -0,0 +1,4 @@ +import { DataStorageAuthWithTriggerTestProjectCreator } from '../../test-project-setup/data_storage_auth_with_triggers.js'; +import { defineDeploymentTest } from './deployment.test.template.js'; + +defineDeploymentTest(new DataStorageAuthWithTriggerTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/deployment/deployment.test.template.ts b/packages/integration-tests/src/test-e2e/deployment/deployment.test.template.ts new file mode 100644 index 00000000000..5c03df72827 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/deployment/deployment.test.template.ts @@ -0,0 +1,171 @@ +import { after, afterEach, before, beforeEach, describe, it } from 'node:test'; +import { + createTestDirectory, + deleteTestDirectory, + rootTestDir, +} from '../../setup_test_directory.js'; +import fs from 'fs/promises'; +import { shortUuid } from '../../short_uuid.js'; +import { TestProjectCreator } from '../../test-project-setup/test_project_creator.js'; +import { TestProjectBase } from '../../test-project-setup/test_project_base.js'; +import { PredicatedActionBuilder } from '../../process-controller/predicated_action_queue_builder.js'; +import { ampxCli } from '../../process-controller/process_controller.js'; +import path from 'path'; +import { + interruptSandbox, + rejectCleanupSandbox, +} from '../../process-controller/predicated_action_macros.js'; +import assert from 'node:assert'; +import { TestBranch, amplifyAppPool } from '../../amplify_app_pool.js'; +import { BackendIdentifier } from '@aws-amplify/plugin-types'; +import { ClientConfigFormat } from '@aws-amplify/client-config'; +import { testConcurrencyLevel } from '../test_concurrency.js'; + +/** + * Defines deployment test + */ +export const defineDeploymentTest = ( + testProjectCreator: TestProjectCreator +) => { + void describe( + 'deployment tests', + { concurrency: testConcurrencyLevel }, + () => { + before(async () => { + await createTestDirectory(rootTestDir); + }); + after(async () => { + await deleteTestDirectory(rootTestDir); + }); + + void describe(`branch deploys ${testProjectCreator.name}`, () => { + let branchBackendIdentifier: BackendIdentifier; + let testBranch: TestBranch; + let testProject: TestProjectBase; + + beforeEach(async () => { + testProject = await testProjectCreator.createProject(rootTestDir); + testBranch = await amplifyAppPool.createTestBranch(); + branchBackendIdentifier = { + namespace: testBranch.appId, + name: testBranch.branchName, + type: 'branch', + }; + }); + + afterEach(async () => { + await testProject.tearDown(branchBackendIdentifier); + }); + + void it(`[${testProjectCreator.name}] deploys fully`, async () => { + await testProject.deploy(branchBackendIdentifier); + await testProject.assertPostDeployment(branchBackendIdentifier); + const testBranchDetails = await amplifyAppPool.fetchTestBranchDetails( + testBranch + ); + assert.ok( + testBranchDetails.backend?.stackArn, + 'branch should have stack associated' + ); + assert.ok( + testBranchDetails.backend?.stackArn?.includes( + branchBackendIdentifier.namespace + ) + ); + assert.ok( + testBranchDetails.backend?.stackArn?.includes( + branchBackendIdentifier.name + ) + ); + + // test generating all client formats + for (const format of [ + ClientConfigFormat.DART, + ClientConfigFormat.JSON, + ]) { + await ampxCli( + [ + 'generate', + 'outputs', + '--branch', + testBranch.branchName, + '--app-id', + testBranch.appId, + '--format', + format, + ], + testProject.projectDirPath + ).run(); + + await testProject.assertClientConfigExists( + testProject.projectDirPath, + format + ); + } + }); + }); + + void describe('fails on compilation error', async () => { + let testProject: TestProjectBase; + before(async () => { + // any project is fine + testProject = await testProjectCreator.createProject(rootTestDir); + await fs.cp( + testProject.sourceProjectAmplifyDirURL, + testProject.projectAmplifyDirPath, + { + recursive: true, + } + ); + + // inject failure + await fs.appendFile( + path.join(testProject.projectAmplifyDirPath, 'backend.ts'), + "this won't compile" + ); + }); + + void describe('in sequence', { concurrency: false }, () => { + void it('in sandbox deploy', async () => { + await ampxCli( + ['sandbox', '--dirToWatch', 'amplify'], + testProject.projectDirPath + ) + .do( + new PredicatedActionBuilder().waitForLineIncludes( + 'TypeScript validation check failed' + ) + ) + .do(interruptSandbox()) + .do(rejectCleanupSandbox()) + .run(); + }); + + void it('in pipeline deploy', async () => { + await assert.rejects(() => + ampxCli( + [ + 'pipeline-deploy', + '--branch', + 'test-branch', + '--app-id', + `test-${shortUuid()}`, + ], + testProject.projectDirPath, + { + env: { CI: 'true' }, + } + ) + .do( + new PredicatedActionBuilder().waitForLineIncludes( + 'TypeScript validation check failed' + ) + ) + .run() + ); + }); + }); + }); + } + ); +}; diff --git a/packages/integration-tests/src/test-e2e/deployment/minimal_with_typescript_idioms.deployment.test.ts b/packages/integration-tests/src/test-e2e/deployment/minimal_with_typescript_idioms.deployment.test.ts new file mode 100644 index 00000000000..af3e619d9de --- /dev/null +++ b/packages/integration-tests/src/test-e2e/deployment/minimal_with_typescript_idioms.deployment.test.ts @@ -0,0 +1,4 @@ +import { MinimalWithTypescriptIdiomTestProjectCreator } from '../../test-project-setup/minimal_with_typescript_idioms.js'; +import { defineDeploymentTest } from './deployment.test.template.js'; + +defineDeploymentTest(new MinimalWithTypescriptIdiomTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/sandbox.test.ts b/packages/integration-tests/src/test-e2e/sandbox.test.ts deleted file mode 100644 index 394b3c8b3ea..00000000000 --- a/packages/integration-tests/src/test-e2e/sandbox.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { after, before, describe, it } from 'node:test'; -import { - createTestDirectory, - deleteTestDirectory, - rootTestDir, -} from '../setup_test_directory.js'; -import { getTestProjectCreators } from '../test-project-setup/test_project_creator.js'; -import { TestProjectBase } from '../test-project-setup/test_project_base.js'; -import { userInfo } from 'os'; -import { ampxCli } from '../process-controller/process_controller.js'; -import { - ensureDeploymentTimeLessThan, - interruptSandbox, - rejectCleanupSandbox, - replaceFiles, - waitForConfigUpdateAfterDeployment, -} from '../process-controller/predicated_action_macros.js'; -import { BackendIdentifier } from '@aws-amplify/plugin-types'; -import { testConcurrencyLevel } from './test_concurrency.js'; -import { - amplifySharedSecretNameKey, - createAmplifySharedSecretName, -} from '../shared_secret.js'; - -const testProjectCreators = getTestProjectCreators(); -void describe('sandbox tests', { concurrency: testConcurrencyLevel }, () => { - before(async () => { - await createTestDirectory(rootTestDir); - }); - after(async () => { - await deleteTestDirectory(rootTestDir); - }); - - void describe('amplify deploys', async () => { - testProjectCreators.forEach((testProjectCreator) => { - void describe(`sandbox deploys ${testProjectCreator.name}`, () => { - let testProject: TestProjectBase; - let sandboxBackendIdentifier: BackendIdentifier; - - before(async () => { - testProject = await testProjectCreator.createProject(rootTestDir); - sandboxBackendIdentifier = { - type: 'sandbox', - namespace: testProject.name, - name: userInfo().username, - }; - }); - - after(async () => { - await testProject.tearDown(sandboxBackendIdentifier); - }); - - void describe('in sequence', { concurrency: false }, () => { - const sharedSecretsEnv = { - [amplifySharedSecretNameKey]: createAmplifySharedSecretName(), - }; - void it(`[${testProjectCreator.name}] deploys fully`, async () => { - await testProject.deploy( - sandboxBackendIdentifier, - sharedSecretsEnv - ); - await testProject.assertPostDeployment(sandboxBackendIdentifier); - }); - - void it('generates config after sandbox --once deployment', async () => { - const processController = ampxCli( - ['sandbox', '--once'], - testProject.projectDirPath, - { - env: sharedSecretsEnv, - } - ); - await processController - .do(waitForConfigUpdateAfterDeployment()) - .run(); - - await testProject.assertPostDeployment(sandboxBackendIdentifier); - }); - - void it(`[${testProjectCreator.name}] hot-swaps a change`, async () => { - const updates = await testProject.getUpdates(); - if (updates.length > 0) { - const processController = ampxCli( - ['sandbox', '--dirToWatch', 'amplify'], - testProject.projectDirPath, - { - env: sharedSecretsEnv, - } - ); - - for (const update of updates) { - processController - .do(replaceFiles(update.replacements)) - .do(ensureDeploymentTimeLessThan(update.deployThresholdSec)); - } - - // Execute the process. - await processController - .do(interruptSandbox()) - .do(rejectCleanupSandbox()) - .run(); - - await testProject.assertPostDeployment(sandboxBackendIdentifier); - } - }); - }); - }); - }); - }); -}); diff --git a/packages/integration-tests/src/test-e2e/sandbox/access_testing_project.sandbox.test.ts b/packages/integration-tests/src/test-e2e/sandbox/access_testing_project.sandbox.test.ts new file mode 100644 index 00000000000..42fc2460d16 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/sandbox/access_testing_project.sandbox.test.ts @@ -0,0 +1,4 @@ +import { defineSandboxTest } from './sandbox.test.template.js'; +import { AccessTestingProjectTestProjectCreator } from '../../test-project-setup/access_testing_project.js'; + +defineSandboxTest(new AccessTestingProjectTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/sandbox/conversation_handler_project.sandbox.test.ts b/packages/integration-tests/src/test-e2e/sandbox/conversation_handler_project.sandbox.test.ts new file mode 100644 index 00000000000..b4ee374c491 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/sandbox/conversation_handler_project.sandbox.test.ts @@ -0,0 +1,4 @@ +import { defineSandboxTest } from './sandbox.test.template.js'; +import { ConversationHandlerTestProjectCreator } from '../../test-project-setup/conversation_handler_project.js'; + +defineSandboxTest(new ConversationHandlerTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/sandbox/custom_outputs.sandbox.test.ts b/packages/integration-tests/src/test-e2e/sandbox/custom_outputs.sandbox.test.ts new file mode 100644 index 00000000000..17f47a7efb3 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/sandbox/custom_outputs.sandbox.test.ts @@ -0,0 +1,4 @@ +import { defineSandboxTest } from './sandbox.test.template.js'; +import { CustomOutputsTestProjectCreator } from '../../test-project-setup/custom_outputs.js'; + +defineSandboxTest(new CustomOutputsTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/sandbox/data_storage_auth_with_triggers.sandbox.test.ts b/packages/integration-tests/src/test-e2e/sandbox/data_storage_auth_with_triggers.sandbox.test.ts new file mode 100644 index 00000000000..b4132444451 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/sandbox/data_storage_auth_with_triggers.sandbox.test.ts @@ -0,0 +1,4 @@ +import { defineSandboxTest } from './sandbox.test.template.js'; +import { DataStorageAuthWithTriggerTestProjectCreator } from '../../test-project-setup/data_storage_auth_with_triggers.js'; + +defineSandboxTest(new DataStorageAuthWithTriggerTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/sandbox/minimal_with_typescript_idioms.sandbox.test.ts b/packages/integration-tests/src/test-e2e/sandbox/minimal_with_typescript_idioms.sandbox.test.ts new file mode 100644 index 00000000000..3f19b529d56 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/sandbox/minimal_with_typescript_idioms.sandbox.test.ts @@ -0,0 +1,4 @@ +import { defineSandboxTest } from './sandbox.test.template.js'; +import { MinimalWithTypescriptIdiomTestProjectCreator } from '../../test-project-setup/minimal_with_typescript_idioms.js'; + +defineSandboxTest(new MinimalWithTypescriptIdiomTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts b/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts new file mode 100644 index 00000000000..03068bedf19 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts @@ -0,0 +1,107 @@ +import { after, before, describe, it } from 'node:test'; +import { + createTestDirectory, + deleteTestDirectory, + rootTestDir, +} from '../../setup_test_directory.js'; +import { TestProjectCreator } from '../../test-project-setup/test_project_creator.js'; +import { TestProjectBase } from '../../test-project-setup/test_project_base.js'; +import { userInfo } from 'os'; +import { ampxCli } from '../../process-controller/process_controller.js'; +import { + ensureDeploymentTimeLessThan, + interruptSandbox, + rejectCleanupSandbox, + replaceFiles, + waitForConfigUpdateAfterDeployment, +} from '../../process-controller/predicated_action_macros.js'; +import { BackendIdentifier } from '@aws-amplify/plugin-types'; +import { testConcurrencyLevel } from '../test_concurrency.js'; +import { + amplifySharedSecretNameKey, + createAmplifySharedSecretName, +} from '../../shared_secret.js'; + +/** + * Defines sandbox test + */ +export const defineSandboxTest = (testProjectCreator: TestProjectCreator) => { + void describe('sandbox test', { concurrency: testConcurrencyLevel }, () => { + before(async () => { + await createTestDirectory(rootTestDir); + }); + after(async () => { + await deleteTestDirectory(rootTestDir); + }); + + void describe(`sandbox deploys ${testProjectCreator.name}`, () => { + let testProject: TestProjectBase; + let sandboxBackendIdentifier: BackendIdentifier; + + before(async () => { + testProject = await testProjectCreator.createProject(rootTestDir); + sandboxBackendIdentifier = { + type: 'sandbox', + namespace: testProject.name, + name: userInfo().username, + }; + }); + + after(async () => { + await testProject.tearDown(sandboxBackendIdentifier); + }); + + void describe('in sequence', { concurrency: false }, () => { + const sharedSecretsEnv = { + [amplifySharedSecretNameKey]: createAmplifySharedSecretName(), + }; + void it(`[${testProjectCreator.name}] deploys fully`, async () => { + await testProject.deploy(sandboxBackendIdentifier, sharedSecretsEnv); + await testProject.assertPostDeployment(sandboxBackendIdentifier); + }); + + void it('generates config after sandbox --once deployment', async () => { + const processController = ampxCli( + ['sandbox', '--once'], + testProject.projectDirPath, + { + env: sharedSecretsEnv, + } + ); + await processController + .do(waitForConfigUpdateAfterDeployment()) + .run(); + + await testProject.assertPostDeployment(sandboxBackendIdentifier); + }); + + void it(`[${testProjectCreator.name}] hot-swaps a change`, async () => { + const updates = await testProject.getUpdates(); + if (updates.length > 0) { + const processController = ampxCli( + ['sandbox', '--dirToWatch', 'amplify'], + testProject.projectDirPath, + { + env: sharedSecretsEnv, + } + ); + + for (const update of updates) { + processController + .do(replaceFiles(update.replacements)) + .do(ensureDeploymentTimeLessThan(update.deployThresholdSec)); + } + + // Execute the process. + await processController + .do(interruptSandbox()) + .do(rejectCleanupSandbox()) + .run(); + + await testProject.assertPostDeployment(sandboxBackendIdentifier); + } + }); + }); + }); + }); +}; diff --git a/packages/integration-tests/src/test-project-setup/access_testing_project.ts b/packages/integration-tests/src/test-project-setup/access_testing_project.ts index 90295a0047c..4430a49397b 100644 --- a/packages/integration-tests/src/test-project-setup/access_testing_project.ts +++ b/packages/integration-tests/src/test-project-setup/access_testing_project.ts @@ -45,6 +45,7 @@ import { IamCredentials } from '../types.js'; import { AmplifyAuthCredentialsFactory } from '../amplify_auth_credentials_factory.js'; import { SemVer } from 'semver'; import { AmplifyClient } from '@aws-sdk/client-amplify'; +import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; // TODO: this is a work around // it seems like as of amplify v6 , some of the code only runs in the browser ... @@ -69,11 +70,21 @@ export class AccessTestingProjectTestProjectCreator * Creates project creator. */ constructor( - private readonly cfnClient: CloudFormationClient, - private readonly amplifyClient: AmplifyClient, - private readonly cognitoIdentityClient: CognitoIdentityClient, - private readonly cognitoIdentityProviderClient: CognitoIdentityProviderClient, - private readonly stsClient: STSClient + private readonly cfnClient: CloudFormationClient = new CloudFormationClient( + e2eToolingClientConfig + ), + private readonly amplifyClient: AmplifyClient = new AmplifyClient( + e2eToolingClientConfig + ), + private readonly cognitoIdentityClient: CognitoIdentityClient = new CognitoIdentityClient( + e2eToolingClientConfig + ), + private readonly cognitoIdentityProviderClient: CognitoIdentityProviderClient = new CognitoIdentityProviderClient( + e2eToolingClientConfig + ), + private readonly stsClient: STSClient = new STSClient( + e2eToolingClientConfig + ) ) {} createProject = async (e2eProjectDir: string): Promise => { diff --git a/packages/integration-tests/src/test-project-setup/cdk/auth_cdk_project.ts b/packages/integration-tests/src/test-project-setup/cdk/auth_cdk_project.ts index 62282b3fd6f..97a3692efed 100644 --- a/packages/integration-tests/src/test-project-setup/cdk/auth_cdk_project.ts +++ b/packages/integration-tests/src/test-project-setup/cdk/auth_cdk_project.ts @@ -22,7 +22,9 @@ export class AuthTestCdkProjectCreator implements TestCdkProjectCreator { /** * Constructor. */ - constructor(private readonly resourceFinder: DeployedResourcesFinder) {} + constructor( + private readonly resourceFinder: DeployedResourcesFinder = new DeployedResourcesFinder() + ) {} createProject = async ( e2eProjectDir: string diff --git a/packages/integration-tests/src/test-project-setup/cdk/test_cdk_project_creator.ts b/packages/integration-tests/src/test-project-setup/cdk/test_cdk_project_creator.ts index 6f0e94efe2e..c1b6d2ef6e0 100644 --- a/packages/integration-tests/src/test-project-setup/cdk/test_cdk_project_creator.ts +++ b/packages/integration-tests/src/test-project-setup/cdk/test_cdk_project_creator.ts @@ -1,10 +1,6 @@ -import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; -import { e2eToolingClientConfig } from '../../e2e_tooling_client_config.js'; import { TestCdkProjectBase } from './test_cdk_project_base.js'; -import { AuthTestCdkProjectCreator } from './auth_cdk_project.js'; import { fileURLToPath } from 'node:url'; import path from 'path'; -import { DeployedResourcesFinder } from '../../find_deployed_resource.js'; const dirname = path.dirname(fileURLToPath(import.meta.url)); export const testCdkProjectsSourceRoot = path.resolve( @@ -20,15 +16,3 @@ export type TestCdkProjectCreator = { readonly name: string; createProject: (e2eProjectDir: string) => Promise; }; - -/** - * Generates a list of test cdk projects. - */ -export const getTestCdkProjectCreators = (): TestCdkProjectCreator[] => { - const testCdkProjectCreators: TestCdkProjectCreator[] = []; - - const cfnClient = new CloudFormationClient(e2eToolingClientConfig); - const resourceFinder = new DeployedResourcesFinder(cfnClient); - testCdkProjectCreators.push(new AuthTestCdkProjectCreator(resourceFinder)); - return testCdkProjectCreators; -}; diff --git a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts index 8dc05092dfe..223675f9e5d 100644 --- a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts +++ b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts @@ -32,6 +32,7 @@ import { import { resolve } from 'path'; import { fileURLToPath } from 'url'; import * as bedrock from '@aws-sdk/client-bedrock-runtime'; +import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; // TODO: this is a work around // it seems like as of amplify v6 , some of the code only runs in the browser ... @@ -100,11 +101,19 @@ export class ConversationHandlerTestProjectCreator * Creates project creator. */ constructor( - private readonly cfnClient: CloudFormationClient, - private readonly amplifyClient: AmplifyClient, - private readonly lambdaClient: LambdaClient, - private readonly cognitoIdentityProviderClient: CognitoIdentityProviderClient, - private readonly resourceFinder: DeployedResourcesFinder + private readonly cfnClient: CloudFormationClient = new CloudFormationClient( + e2eToolingClientConfig + ), + private readonly amplifyClient: AmplifyClient = new AmplifyClient( + e2eToolingClientConfig + ), + private readonly lambdaClient: LambdaClient = new LambdaClient( + e2eToolingClientConfig + ), + private readonly cognitoIdentityProviderClient: CognitoIdentityProviderClient = new CognitoIdentityProviderClient( + e2eToolingClientConfig + ), + private readonly resourceFinder: DeployedResourcesFinder = new DeployedResourcesFinder() ) {} createProject = async (e2eProjectDir: string): Promise => { @@ -155,9 +164,13 @@ class ConversationHandlerTestProject extends TestProjectBase { projectAmplifyDirPath: string, cfnClient: CloudFormationClient, amplifyClient: AmplifyClient, - private readonly lambdaClient: LambdaClient, - private readonly cognitoIdentityProviderClient: CognitoIdentityProviderClient, - private readonly resourceFinder: DeployedResourcesFinder + private readonly lambdaClient: LambdaClient = new LambdaClient( + e2eToolingClientConfig + ), + private readonly cognitoIdentityProviderClient: CognitoIdentityProviderClient = new CognitoIdentityProviderClient( + e2eToolingClientConfig + ), + private readonly resourceFinder: DeployedResourcesFinder = new DeployedResourcesFinder() ) { super( name, diff --git a/packages/integration-tests/src/test-project-setup/custom_outputs.ts b/packages/integration-tests/src/test-project-setup/custom_outputs.ts index 7e6b367e5a5..0a20230e25a 100644 --- a/packages/integration-tests/src/test-project-setup/custom_outputs.ts +++ b/packages/integration-tests/src/test-project-setup/custom_outputs.ts @@ -12,6 +12,7 @@ import { } from '@aws-amplify/client-config'; import assert from 'node:assert'; import { AmplifyClient } from '@aws-sdk/client-amplify'; +import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; /** * Creates minimal test projects with custom outputs. @@ -23,8 +24,12 @@ export class CustomOutputsTestProjectCreator implements TestProjectCreator { * Creates project creator. */ constructor( - private readonly cfnClient: CloudFormationClient, - private readonly amplifyClient: AmplifyClient + private readonly cfnClient: CloudFormationClient = new CloudFormationClient( + e2eToolingClientConfig + ), + private readonly amplifyClient: AmplifyClient = new AmplifyClient( + e2eToolingClientConfig + ) ) {} createProject = async (e2eProjectDir: string): Promise => { diff --git a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts index b5a1658dd03..414ea28b050 100644 --- a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts +++ b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts @@ -1,5 +1,5 @@ import fs from 'fs/promises'; -import { SecretClient } from '@aws-amplify/backend-secret'; +import { SecretClient, getSecretClient } from '@aws-amplify/backend-secret'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; import { createEmptyAmplifyProject } from './create_empty_amplify_project.js'; import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; @@ -46,15 +46,29 @@ export class DataStorageAuthWithTriggerTestProjectCreator * Creates project creator. */ constructor( - private readonly cfnClient: CloudFormationClient, - private readonly amplifyClient: AmplifyClient, - private readonly secretClient: SecretClient, - private readonly lambdaClient: LambdaClient, - private readonly s3Client: S3Client, - private readonly iamClient: IAMClient, - private readonly sqsClient: SQSClient, - private readonly cloudTrailClient: CloudTrailClient, - private readonly resourceFinder: DeployedResourcesFinder + private readonly cfnClient: CloudFormationClient = new CloudFormationClient( + e2eToolingClientConfig + ), + private readonly amplifyClient: AmplifyClient = new AmplifyClient( + e2eToolingClientConfig + ), + private readonly secretClient: SecretClient = getSecretClient( + e2eToolingClientConfig + ), + private readonly lambdaClient: LambdaClient = new LambdaClient( + e2eToolingClientConfig + ), + private readonly s3Client: S3Client = new S3Client(e2eToolingClientConfig), + private readonly iamClient: IAMClient = new IAMClient( + e2eToolingClientConfig + ), + private readonly sqsClient: SQSClient = new SQSClient( + e2eToolingClientConfig + ), + private readonly cloudTrailClient: CloudTrailClient = new CloudTrailClient( + e2eToolingClientConfig + ), + private readonly resourceFinder: DeployedResourcesFinder = new DeployedResourcesFinder() ) {} createProject = async (e2eProjectDir: string): Promise => { diff --git a/packages/integration-tests/src/test-project-setup/minimal_with_typescript_idioms.ts b/packages/integration-tests/src/test-project-setup/minimal_with_typescript_idioms.ts index 983f11a211d..b7c1886df18 100644 --- a/packages/integration-tests/src/test-project-setup/minimal_with_typescript_idioms.ts +++ b/packages/integration-tests/src/test-project-setup/minimal_with_typescript_idioms.ts @@ -4,6 +4,7 @@ import { createEmptyAmplifyProject } from './create_empty_amplify_project.js'; import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; import { TestProjectCreator } from './test_project_creator.js'; import { AmplifyClient } from '@aws-sdk/client-amplify'; +import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; /** * Creates minimal test projects with typescript idioms. @@ -17,8 +18,12 @@ export class MinimalWithTypescriptIdiomTestProjectCreator * Creates project creator. */ constructor( - private readonly cfnClient: CloudFormationClient, - private readonly amplifyClient: AmplifyClient + private readonly cfnClient: CloudFormationClient = new CloudFormationClient( + e2eToolingClientConfig + ), + private readonly amplifyClient: AmplifyClient = new AmplifyClient( + e2eToolingClientConfig + ) ) {} createProject = async (e2eProjectDir: string): Promise => { diff --git a/packages/integration-tests/src/test-project-setup/test_project_creator.ts b/packages/integration-tests/src/test-project-setup/test_project_creator.ts index 95cf523aa4e..73271567590 100644 --- a/packages/integration-tests/src/test-project-setup/test_project_creator.ts +++ b/packages/integration-tests/src/test-project-setup/test_project_creator.ts @@ -1,78 +1,6 @@ import { TestProjectBase } from './test_project_base.js'; -import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; -import { getSecretClient } from '@aws-amplify/backend-secret'; -import { DataStorageAuthWithTriggerTestProjectCreator } from './data_storage_auth_with_triggers.js'; -import { MinimalWithTypescriptIdiomTestProjectCreator } from './minimal_with_typescript_idioms.js'; -import { ConversationHandlerTestProjectCreator } from './conversation_handler_project.js'; -import { LambdaClient } from '@aws-sdk/client-lambda'; -import { DeployedResourcesFinder } from '../find_deployed_resource.js'; -import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; -import { CustomOutputsTestProjectCreator } from './custom_outputs.js'; -import { S3Client } from '@aws-sdk/client-s3'; -import { IAMClient } from '@aws-sdk/client-iam'; -import { AccessTestingProjectTestProjectCreator } from './access_testing_project.js'; -import { CognitoIdentityProviderClient } from '@aws-sdk/client-cognito-identity-provider'; -import { CognitoIdentityClient } from '@aws-sdk/client-cognito-identity'; -import { STSClient } from '@aws-sdk/client-sts'; -import { AmplifyClient } from '@aws-sdk/client-amplify'; -import { SQSClient } from '@aws-sdk/client-sqs'; -import { CloudTrailClient } from '@aws-sdk/client-cloudtrail'; export type TestProjectCreator = { readonly name: string; createProject: (e2eProjectDir: string) => Promise; }; - -/** - * Generates a list of test projects. - */ -export const getTestProjectCreators = (): TestProjectCreator[] => { - const testProjectCreators: TestProjectCreator[] = []; - - const cfnClient = new CloudFormationClient(e2eToolingClientConfig); - const cloudTrailClient = new CloudTrailClient(e2eToolingClientConfig); - const amplifyClient = new AmplifyClient(e2eToolingClientConfig); - const cognitoIdentityClient = new CognitoIdentityClient( - e2eToolingClientConfig - ); - const cognitoIdentityProviderClient = new CognitoIdentityProviderClient( - e2eToolingClientConfig - ); - const lambdaClient = new LambdaClient(e2eToolingClientConfig); - const s3Client = new S3Client(e2eToolingClientConfig); - const iamClient = new IAMClient(e2eToolingClientConfig); - const sqsClient = new SQSClient(e2eToolingClientConfig); - const resourceFinder = new DeployedResourcesFinder(cfnClient); - const stsClient = new STSClient(e2eToolingClientConfig); - const secretClient = getSecretClient(e2eToolingClientConfig); - testProjectCreators.push( - new DataStorageAuthWithTriggerTestProjectCreator( - cfnClient, - amplifyClient, - secretClient, - lambdaClient, - s3Client, - iamClient, - sqsClient, - cloudTrailClient, - resourceFinder - ), - new MinimalWithTypescriptIdiomTestProjectCreator(cfnClient, amplifyClient), - new CustomOutputsTestProjectCreator(cfnClient, amplifyClient), - new AccessTestingProjectTestProjectCreator( - cfnClient, - amplifyClient, - cognitoIdentityClient, - cognitoIdentityProviderClient, - stsClient - ), - new ConversationHandlerTestProjectCreator( - cfnClient, - amplifyClient, - lambdaClient, - cognitoIdentityProviderClient, - resourceFinder - ) - ); - return testProjectCreators; -}; diff --git a/scripts/components/sparse_test_matrix_generator.test.ts b/scripts/components/sparse_test_matrix_generator.test.ts new file mode 100644 index 00000000000..73a0a5af60e --- /dev/null +++ b/scripts/components/sparse_test_matrix_generator.test.ts @@ -0,0 +1,61 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert'; +import { SparseTestMatrixGenerator } from './sparse_test_matrix_generator.js'; +import { fileURLToPath } from 'url'; + +void describe('Sparse matrix generator', () => { + void it('generates sparse matrix', async () => { + const testDirectory = fileURLToPath( + new URL('./test-resources/sparse-generator-test-stubs', import.meta.url) + ); + const matrix = await new SparseTestMatrixGenerator({ + testGlobPattern: `${testDirectory}/*.test.ts`, + dimensions: { + dimension1: ['dim1val1', 'dim1val2', 'dim1,val3'], + dimension2: ['dim2val1', 'dim2val2'], + }, + maxTestsPerJob: 2, + }).generate(); + + assert.deepStrictEqual(matrix, { + include: [ + { + displayNames: 'test3.test.ts test2.test.ts', + dimension1: 'dim1val1', + dimension2: 'dim2val1', + testPaths: `${testDirectory}/test3.test.ts ${testDirectory}/test2.test.ts`, + }, + { + displayNames: 'test3.test.ts test2.test.ts', + dimension1: 'dim1val2', + dimension2: 'dim2val2', + testPaths: `${testDirectory}/test3.test.ts ${testDirectory}/test2.test.ts`, + }, + { + displayNames: 'test3.test.ts test2.test.ts', + dimension1: 'dim1,val3', + dimension2: 'dim2val1', + testPaths: `${testDirectory}/test3.test.ts ${testDirectory}/test2.test.ts`, + }, + { + displayNames: 'test1.test.ts', + dimension1: 'dim1val1', + dimension2: 'dim2val1', + testPaths: `${testDirectory}/test1.test.ts`, + }, + { + displayNames: 'test1.test.ts', + dimension1: 'dim1val2', + dimension2: 'dim2val2', + testPaths: `${testDirectory}/test1.test.ts`, + }, + { + displayNames: 'test1.test.ts', + dimension1: 'dim1,val3', + dimension2: 'dim2val1', + testPaths: `${testDirectory}/test1.test.ts`, + }, + ], + }); + }); +}); diff --git a/scripts/components/sparse_test_matrix_generator.ts b/scripts/components/sparse_test_matrix_generator.ts new file mode 100644 index 00000000000..4daf7ce0f07 --- /dev/null +++ b/scripts/components/sparse_test_matrix_generator.ts @@ -0,0 +1,93 @@ +import { glob } from 'glob'; +import path from 'path'; + +// See https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow +type JobMatrix = { + include?: Array>; +} & Record; + +export type SparseTestMatrixGeneratorProps = { + testGlobPattern: string; + maxTestsPerJob: number; + dimensions: Record>; +}; + +/** + * Generates a sparse test matrix. + * + * Sparse matrix is created is such a way that: + * 1. Every test is included + * 2. Every dimension's value is included + * 3. Algorithm avoids cartesian product of dimensions, just minimal subset that uses all values. + */ +export class SparseTestMatrixGenerator { + /** + * Creates sparse test matrix generator. + */ + constructor(private readonly props: SparseTestMatrixGeneratorProps) { + if (Object.keys(props.dimensions).length === 0) { + throw new Error('At least one dimension is required'); + } + } + + generate = async (): Promise => { + const testPaths = await glob(this.props.testGlobPattern); + + const matrix: JobMatrix = {}; + matrix.include = []; + + for (const testPathsBatch of this.chunkArray( + testPaths, + this.props.maxTestsPerJob + )) { + const dimensionsIndexes: Record = {}; + const dimensionCoverageComplete: Record = {}; + + Object.keys(this.props.dimensions).forEach((key) => { + dimensionsIndexes[key] = 0; + dimensionCoverageComplete[key] = false; + }); + + let allDimensionsComplete = false; + + do { + const matrixEntry: Record = {}; + matrixEntry.displayNames = testPathsBatch + .map((testPath) => path.basename(testPath)) + .join(' '); + Object.keys(this.props.dimensions).forEach((key) => { + matrixEntry[key] = this.props.dimensions[key][dimensionsIndexes[key]]; + }); + matrixEntry.testPaths = testPathsBatch.join(' '); + matrix.include?.push(matrixEntry); + + Object.keys(this.props.dimensions).forEach((key) => { + dimensionsIndexes[key]++; + if (dimensionsIndexes[key] === this.props.dimensions[key].length) { + // mark dimension as complete and start the cycle from start until all dimensions are used. + dimensionCoverageComplete[key] = true; + dimensionsIndexes[key] = 0; + } + }); + + // check if all dimensions are processed. + allDimensionsComplete = Object.keys(this.props.dimensions).reduce( + (acc, key) => { + return acc && dimensionCoverageComplete[key]; + }, + true + ); + } while (!allDimensionsComplete); + } + + return matrix; + }; + + private chunkArray = (array: Array, chunkSize: number) => { + const result: Array> = []; + for (let i = 0; i < array.length; i += chunkSize) { + result.push(array.slice(i, i + chunkSize)); + } + return result; + }; +} diff --git a/scripts/components/test-resources/sparse-generator-test-stubs/test1.test.ts b/scripts/components/test-resources/sparse-generator-test-stubs/test1.test.ts new file mode 100644 index 00000000000..af6cf15235e --- /dev/null +++ b/scripts/components/test-resources/sparse-generator-test-stubs/test1.test.ts @@ -0,0 +1 @@ +// Empty, content doesn't matter. diff --git a/scripts/components/test-resources/sparse-generator-test-stubs/test2.test.ts b/scripts/components/test-resources/sparse-generator-test-stubs/test2.test.ts new file mode 100644 index 00000000000..af6cf15235e --- /dev/null +++ b/scripts/components/test-resources/sparse-generator-test-stubs/test2.test.ts @@ -0,0 +1 @@ +// Empty, content doesn't matter. diff --git a/scripts/components/test-resources/sparse-generator-test-stubs/test3.test.ts b/scripts/components/test-resources/sparse-generator-test-stubs/test3.test.ts new file mode 100644 index 00000000000..af6cf15235e --- /dev/null +++ b/scripts/components/test-resources/sparse-generator-test-stubs/test3.test.ts @@ -0,0 +1 @@ +// Empty, content doesn't matter. diff --git a/scripts/generate_sparse_test_matrix.ts b/scripts/generate_sparse_test_matrix.ts new file mode 100644 index 00000000000..c5a76eb9654 --- /dev/null +++ b/scripts/generate_sparse_test_matrix.ts @@ -0,0 +1,31 @@ +import { SparseTestMatrixGenerator } from './components/sparse_test_matrix_generator.js'; + +// This script generates a sparse test matrix. +// Every test must run on each type of OS and each version of node. +// However, we don't have to run every combination. + +if (process.argv.length < 3) { + console.log( + "Usage: npx tsx scripts/generate_sparse_test_matrix.ts '' " + ); +} + +const testGlobPattern = process.argv[2]; +const maxTestsPerJob = process.argv[3] ? parseInt(process.argv[3]) : 2; + +if (!Number.isInteger(maxTestsPerJob)) { + throw new Error( + 'Invalid max tests per job. If you are using glob pattern with starts in bash put it in quotes' + ); +} + +const matrix = await new SparseTestMatrixGenerator({ + testGlobPattern, + maxTestsPerJob, + dimensions: { + 'node-version': ['18', '20'], + os: ['ubuntu-latest', 'macos-14-xlarge', 'windows-latest'], + }, +}).generate(); + +console.log(JSON.stringify(matrix)); From 21bb8603df1c9e0588df0a69d146f9e703f505bb Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 21 Oct 2024 21:00:36 -0700 Subject: [PATCH 055/199] Enable debug logging in conversation handler e2e tests --- .changeset/nine-hornets-mate.md | 2 ++ .../conversation_handler_project.ts | 4 +++ .../conversation-handler/amplify/backend.ts | 29 ++++++++++++++----- 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 .changeset/nine-hornets-mate.md diff --git a/.changeset/nine-hornets-mate.md b/.changeset/nine-hornets-mate.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/nine-hornets-mate.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts index 223675f9e5d..d19c984edab 100644 --- a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts +++ b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts @@ -582,6 +582,9 @@ class ConversationHandlerTestProject extends TestProjectBase { functionName: string, apolloClient: ApolloClient ): Promise => { + console.log( + `Sending event conversationId=${event.conversationId} currentMessageId=${event.currentMessageId}` + ); await this.lambdaClient.send( new InvokeCommand({ FunctionName: functionName, @@ -687,6 +690,7 @@ class ConversationHandlerTestProject extends TestProjectBase { conversationId: { eq: $conversationId } associatedUserMessageId: { eq: $associatedUserMessageId } } + limit: 1000 ) { items { conversationId diff --git a/packages/integration-tests/src/test-projects/conversation-handler/amplify/backend.ts b/packages/integration-tests/src/test-projects/conversation-handler/amplify/backend.ts index 3c6fa81c2e8..f6914ace4eb 100644 --- a/packages/integration-tests/src/test-projects/conversation-handler/amplify/backend.ts +++ b/packages/integration-tests/src/test-projects/conversation-handler/amplify/backend.ts @@ -9,11 +9,24 @@ const backend = defineBackend({ auth, data, customConversationHandler }); const stack = backend.createStack('conversationHandlerStack'); -new ConversationHandlerFunction(stack, 'defaultConversationHandlerFunction', { - models: [ - { - modelId: bedrockModelId, - region: stack.region, - }, - ], -}); +const defaultConversationHandler = new ConversationHandlerFunction( + stack, + 'defaultConversationHandlerFunction', + { + models: [ + { + modelId: bedrockModelId, + region: stack.region, + }, + ], + } +); + +defaultConversationHandler.resources.cfnResources.cfnFunction.addPropertyOverride( + 'LoggingConfig.ApplicationLogLevel', + 'DEBUG' +); +backend.customConversationHandler.resources.cfnResources.cfnFunction.addPropertyOverride( + 'LoggingConfig.ApplicationLogLevel', + 'DEBUG' +); From 00412a646290a7dc9c5a600fc0ea6137136f1129 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Tue, 22 Oct 2024 09:28:16 -0700 Subject: [PATCH 056/199] Hydrate npx cache with 'which' (#2136) * add 'which' to dev dependencies * Revert "add 'which' to dev dependencies" This reverts commit 767d9f4fd2d90328cfa1ee67e03ae546c33019ab. * add 'which' to dev dependencies --- .github/actions/setup_node/action.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/actions/setup_node/action.yml b/.github/actions/setup_node/action.yml index 6bda314a870..8f407ee4370 100644 --- a/.github/actions/setup_node/action.yml +++ b/.github/actions/setup_node/action.yml @@ -12,3 +12,10 @@ runs: with: node-version: ${{ inputs.node-version }} cache: 'npm' + - name: Hydrate npx cache + # This step hydrates npx cache with packages that we use in builds and tests upfront. + # Otherwise, concurrent attempt to use these tools with cache miss results in race conditions between + # two installations. That may result in corrupted npx cache. + shell: bash + run: | + npx which npx From 2c0d316d2a6b54b2c09188d9822966e7751b1907 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Tue, 22 Oct 2024 12:23:00 -0700 Subject: [PATCH 057/199] Retry conversation handler transactions in e2e tests (#2141) --- .changeset/serious-trees-invent.md | 2 + .../conversation_handler_project.ts | 182 +++++++++++------- 2 files changed, 116 insertions(+), 68 deletions(-) create mode 100644 .changeset/serious-trees-invent.md diff --git a/.changeset/serious-trees-invent.md b/.changeset/serious-trees-invent.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/serious-trees-invent.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts index d19c984edab..e309ffc71f1 100644 --- a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts +++ b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts @@ -194,6 +194,7 @@ class ConversationHandlerTestProject extends TestProjectBase { throw new Error('Conversation handler project must include auth'); } + const dataUrl = clientConfig.data?.url; const authenticatedUserCredentials = await new AmplifyAuthCredentialsFactory( this.cognitoIdentityProviderClient, @@ -218,94 +219,116 @@ class ConversationHandlerTestProject extends TestProjectBase { cache: new InMemoryCache(), }); - await this.assertDefaultConversationHandlerCanExecuteTurn( - backendId, - authenticatedUserCredentials.accessToken, - clientConfig.data.url, - apolloClient, - false + await this.executeWithRetry(() => + this.assertDefaultConversationHandlerCanExecuteTurn( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + false + ) ); - await this.assertDefaultConversationHandlerCanExecuteTurn( - backendId, - authenticatedUserCredentials.accessToken, - clientConfig.data.url, - apolloClient, - true + await this.executeWithRetry(() => + this.assertDefaultConversationHandlerCanExecuteTurn( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + true + ) ); - await this.assertDefaultConversationHandlerCanExecuteTurn( - backendId, - authenticatedUserCredentials.accessToken, - clientConfig.data.url, - apolloClient, - false, - // Simulate eventual consistency - true + await this.executeWithRetry(() => + this.assertDefaultConversationHandlerCanExecuteTurn( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + false, + // Simulate eventual consistency + true + ) ); - await this.assertCustomConversationHandlerCanExecuteTurn( - backendId, - authenticatedUserCredentials.accessToken, - clientConfig.data.url, - apolloClient, - false + await this.executeWithRetry(() => + this.assertCustomConversationHandlerCanExecuteTurn( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + false + ) ); - await this.assertCustomConversationHandlerCanExecuteTurn( - backendId, - authenticatedUserCredentials.accessToken, - clientConfig.data.url, - apolloClient, - true + await this.executeWithRetry(() => + this.assertCustomConversationHandlerCanExecuteTurn( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + true + ) ); - await this.assertDefaultConversationHandlerCanExecuteTurnWithDataTool( - backendId, - authenticatedUserCredentials.accessToken, - clientConfig.data.url, - apolloClient, - false + await this.executeWithRetry(() => + this.assertDefaultConversationHandlerCanExecuteTurnWithDataTool( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + false + ) ); - await this.assertDefaultConversationHandlerCanExecuteTurnWithDataTool( - backendId, - authenticatedUserCredentials.accessToken, - clientConfig.data.url, - apolloClient, - true + await this.executeWithRetry(() => + this.assertDefaultConversationHandlerCanExecuteTurnWithDataTool( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + true + ) ); - await this.assertDefaultConversationHandlerCanExecuteTurnWithClientTool( - backendId, - authenticatedUserCredentials.accessToken, - clientConfig.data.url, - apolloClient, - false + await this.executeWithRetry(() => + this.assertDefaultConversationHandlerCanExecuteTurnWithClientTool( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + false + ) ); - await this.assertDefaultConversationHandlerCanExecuteTurnWithClientTool( - backendId, - authenticatedUserCredentials.accessToken, - clientConfig.data.url, - apolloClient, - true + await this.executeWithRetry(() => + this.assertDefaultConversationHandlerCanExecuteTurnWithClientTool( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + true + ) ); - await this.assertDefaultConversationHandlerCanExecuteTurnWithImage( - backendId, - authenticatedUserCredentials.accessToken, - clientConfig.data.url, - apolloClient, - false + await this.executeWithRetry(() => + this.assertDefaultConversationHandlerCanExecuteTurnWithImage( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + false + ) ); - await this.assertDefaultConversationHandlerCanExecuteTurnWithImage( - backendId, - authenticatedUserCredentials.accessToken, - clientConfig.data.url, - apolloClient, - true + await this.executeWithRetry(() => + this.assertDefaultConversationHandlerCanExecuteTurnWithImage( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + true + ) ); } @@ -832,4 +855,27 @@ class ConversationHandlerTestProject extends TestProjectBase { }, }; }; + + /** + * Bedrock sometimes produces empty response or half backed response. + * On the other hand we have to run some assertions on those responses. + * Therefore, we wrap transactions in retry loop. + */ + private executeWithRetry = async ( + callable: () => Promise, + maxAttempts = 3 + ) => { + const collectedErrors = []; + for (let i = 1; i <= maxAttempts; i++) { + try { + await callable(); + // if successful return early; + return; + } catch (e) { + collectedErrors.push(e); + } + } + // if we got here there were only errors + throw new AggregateError(collectedErrors); + }; } From 853b8b1d92f83de9f67b8aebe7454ab6bf643b71 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 23 Oct 2024 10:49:14 -0700 Subject: [PATCH 058/199] Add log groups to e2e resource cleanup script (#2142) * cover log groups in e2e test cleanup * tmp change * Revert "tmp change" This reverts commit 0cc56b8a618765b7a53574022ae8bd0da3b730a4. * condition * condition --- package-lock.json | 1 + package.json | 1 + scripts/cleanup_e2e_resources.ts | 67 ++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/package-lock.json b/package-lock.json index 4006b75aec4..411d47ba6ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@actions/github": "^6.0.0", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", + "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-cognito-identity-provider": "^3.624.0", "@aws-sdk/client-dynamodb": "^3.624.0", "@aws-sdk/client-iam": "^3.624.0", diff --git a/package.json b/package.json index 4a0fb2be0f0..fd58cf6c046 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "@actions/github": "^6.0.0", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", + "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-cognito-identity-provider": "^3.624.0", "@aws-sdk/client-dynamodb": "^3.624.0", "@aws-sdk/client-iam": "^3.624.0", diff --git a/scripts/cleanup_e2e_resources.ts b/scripts/cleanup_e2e_resources.ts index 536b7b41d28..97463ce7440 100644 --- a/scripts/cleanup_e2e_resources.ts +++ b/scripts/cleanup_e2e_resources.ts @@ -6,6 +6,13 @@ import { StackStatus, StackSummary, } from '@aws-sdk/client-cloudformation'; +import { + CloudWatchLogsClient, + DeleteLogGroupCommand, + DescribeLogGroupsCommand, + DescribeLogGroupsCommandOutput, + LogGroup, +} from '@aws-sdk/client-cloudwatch-logs'; import { Bucket, DeleteBucketCommand, @@ -70,6 +77,9 @@ const amplifyClient = new AmplifyClient({ const cfnClient = new CloudFormationClient({ maxAttempts: 5, }); +const cloudWatchClient = new CloudWatchLogsClient({ + maxAttempts: 5, +}); const cognitoClient = new CognitoIdentityProviderClient({ maxAttempts: 5, }); @@ -91,6 +101,7 @@ const TEST_CDK_RESOURCE_PREFIX = 'test-cdk'; /** * Stacks are considered stale after 2 hours. + * Log groups are considered stale after 7 days. For troubleshooting purposes. * Other resources are considered stale after 3 hours. * * Stack deletion triggers asynchronous resource deletion while this script is running. @@ -100,6 +111,7 @@ const TEST_CDK_RESOURCE_PREFIX = 'test-cdk'; */ const stackStaleDurationInMilliseconds = 2 * 60 * 60 * 1000; // 2 hours in milliseconds const staleDurationInMilliseconds = 3 * 60 * 60 * 1000; // 3 hours in milliseconds +const logGroupStaleDurationInMilliseconds = 7 * 24 * 60 * 60 * 1000; // 7 days in milliseconds const isStackStale = ( stackSummary: StackSummary | undefined @@ -113,6 +125,17 @@ const isStackStale = ( ); }; +const isLogGroupStale = ( + logGroup: LogGroup | undefined +): boolean | undefined => { + if (!logGroup?.creationTime) { + return; + } + return ( + now.getTime() - logGroup.creationTime > logGroupStaleDurationInMilliseconds + ); +}; + const isStale = (creationDate: Date | undefined): boolean | undefined => { if (!creationDate) { return; @@ -546,3 +569,47 @@ for (const staleDynamoDBTable of allStaleDynamoDBTables) { ); } } + +const listAllStaleTestLogGroups = async (): Promise> => { + let nextToken: string | undefined = undefined; + const logGroups: Array = []; + do { + const listLogGroupsResponse: DescribeLogGroupsCommandOutput = + await cloudWatchClient.send( + new DescribeLogGroupsCommand({ + nextToken, + }) + ); + nextToken = listLogGroupsResponse.nextToken; + listLogGroupsResponse.logGroups + ?.filter( + (logGroup) => + (logGroup.logGroupName?.startsWith(TEST_AMPLIFY_RESOURCE_PREFIX) || + logGroup.logGroupName?.startsWith( + `/aws/lambda/${TEST_AMPLIFY_RESOURCE_PREFIX}` + )) && + isLogGroupStale(logGroup) + ) + .forEach((item) => { + logGroups.push(item); + }); + } while (nextToken); + return logGroups; +}; + +const allStaleLogGroups = await listAllStaleTestLogGroups(); +for (const logGroup of allStaleLogGroups) { + try { + await cloudWatchClient.send( + new DeleteLogGroupCommand({ + logGroupName: logGroup.logGroupName, + }) + ); + console.log(`Successfully deleted ${logGroup.logGroupName} log group`); + } catch (e) { + const errorMessage = e instanceof Error ? e.message : ''; + console.log( + `Failed to delete ${logGroup.logGroupName} log group. ${errorMessage}` + ); + } +} From bd4ff4d3920261308c8a0f854f07d552540f6311 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 23 Oct 2024 13:39:30 -0700 Subject: [PATCH 059/199] Add memory setting to conversation handler (#2143) * Add memory setting to conversation handler * pr feedback --- .changeset/red-candles-fly.md | 5 ++ .changeset/tiny-ears-fly.md | 6 ++ packages/ai-constructs/API.md | 1 + .../conversation_handler_construct.test.ts | 62 +++++++++++++++++++ .../conversation_handler_construct.ts | 30 +++++++++ packages/backend-ai/API.md | 1 + .../src/conversation/factory.test.ts | 15 +++++ .../backend-ai/src/conversation/factory.ts | 7 +++ packages/backend-function/src/factory.ts | 2 +- 9 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 .changeset/red-candles-fly.md create mode 100644 .changeset/tiny-ears-fly.md diff --git a/.changeset/red-candles-fly.md b/.changeset/red-candles-fly.md new file mode 100644 index 00000000000..d8802f845ac --- /dev/null +++ b/.changeset/red-candles-fly.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-function': patch +--- + +Fix jsdocs that incorrectly state default memory settings diff --git a/.changeset/tiny-ears-fly.md b/.changeset/tiny-ears-fly.md new file mode 100644 index 00000000000..49fc36312ff --- /dev/null +++ b/.changeset/tiny-ears-fly.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/ai-constructs': patch +'@aws-amplify/backend-ai': patch +--- + +Add memory setting to conversation handler diff --git a/packages/ai-constructs/API.md b/packages/ai-constructs/API.md index 26a63ca9def..a5621d37df5 100644 --- a/packages/ai-constructs/API.md +++ b/packages/ai-constructs/API.md @@ -54,6 +54,7 @@ type ConversationHandlerFunctionProps = { modelId: string; region?: string; }>; + memoryMB?: number; outputStorageStrategy?: BackendOutputStorageStrategy; }; diff --git a/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts b/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts index bdc8f343500..b0130e711f9 100644 --- a/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts +++ b/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts @@ -222,4 +222,66 @@ void describe('Conversation Handler Function construct', () => { Handler: 'index.handler', }); }); + + void describe('memory property', () => { + void it('sets valid memory', () => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [], + memoryMB: 234, + }); + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + MemorySize: 234, + }); + }); + + void it('sets default memory', () => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [], + }); + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + MemorySize: 512, + }); + }); + + void it('throws on memory below 128 MB', () => { + assert.throws(() => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [], + memoryMB: 127, + }); + }, new Error('memoryMB must be a whole number between 128 and 10240 inclusive')); + }); + + void it('throws on memory above 10240 MB', () => { + assert.throws(() => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [], + memoryMB: 10241, + }); + }, new Error('memoryMB must be a whole number between 128 and 10240 inclusive')); + }); + + void it('throws on fractional memory', () => { + assert.throws(() => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [], + memoryMB: 256.2, + }); + }, new Error('memoryMB must be a whole number between 128 and 10240 inclusive')); + }); + }); }); diff --git a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts index f4972529a0d..995b92fed6c 100644 --- a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts +++ b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts @@ -34,6 +34,12 @@ export type ConversationHandlerFunctionProps = { modelId: string; region?: string; }>; + /** + * An amount of memory (RAM) to allocate to the function between 128 and 10240 MB. + * Must be a whole number. + * Default is 512MB. + */ + memoryMB?: number; /** * @internal */ @@ -86,6 +92,7 @@ export class ConversationHandlerFunction timeout: Duration.seconds(60), entry: this.props.entry ?? defaultHandlerFilePath, handler: 'handler', + memorySize: this.resolveMemory(), bundling: { // Do not bundle SDK if conversation handler is using our default implementation which is // compatible with Lambda provided SDK. @@ -153,4 +160,27 @@ export class ConversationHandlerFunction }, }); }; + + private resolveMemory = () => { + const memoryMin = 128; + const memoryMax = 10240; + const memoryDefault = 512; + if (this.props.memoryMB === undefined) { + return memoryDefault; + } + if ( + !isWholeNumberBetweenInclusive(this.props.memoryMB, memoryMin, memoryMax) + ) { + throw new Error( + `memoryMB must be a whole number between ${memoryMin} and ${memoryMax} inclusive` + ); + } + return this.props.memoryMB; + }; } + +const isWholeNumberBetweenInclusive = ( + test: number, + min: number, + max: number +) => min <= test && test <= max && test % 1 === 0; diff --git a/packages/backend-ai/API.md b/packages/backend-ai/API.md index bd810d42741..2b75a01c579 100644 --- a/packages/backend-ai/API.md +++ b/packages/backend-ai/API.md @@ -53,6 +53,7 @@ type DefineConversationHandlerFunctionProps = { modelId: string | AiModel; region?: string; }>; + memoryMB?: number; }; // @public (undocumented) diff --git a/packages/backend-ai/src/conversation/factory.test.ts b/packages/backend-ai/src/conversation/factory.test.ts index 26ba1759be9..9802e4944bc 100644 --- a/packages/backend-ai/src/conversation/factory.test.ts +++ b/packages/backend-ai/src/conversation/factory.test.ts @@ -188,4 +188,19 @@ void describe('ConversationHandlerFactory', () => { }); }); }); + + void it('passes memory setting to construct', () => { + const factory = defineConversationHandlerFunction({ + entry: './test-assets/with-default-entry/handler.ts', + name: 'testHandlerName', + models: [], + memoryMB: 271, + }); + const lambda = factory.getInstance(getInstanceProps); + const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('AWS::Lambda::Function', { + MemorySize: 271, + }); + }); }); diff --git a/packages/backend-ai/src/conversation/factory.ts b/packages/backend-ai/src/conversation/factory.ts index 811f3f7fbdc..6e04bbbcf54 100644 --- a/packages/backend-ai/src/conversation/factory.ts +++ b/packages/backend-ai/src/conversation/factory.ts @@ -43,6 +43,7 @@ class ConversationHandlerFunctionGenerator }; }), outputStorageStrategy: this.outputStorageStrategy, + memoryMB: this.props.memoryMB, }; const conversationHandlerFunction = new ConversationHandlerFunction( scope, @@ -119,6 +120,12 @@ export type DefineConversationHandlerFunctionProps = { modelId: string | AiModel; region?: string; }>; + /** + * An amount of memory (RAM) to allocate to the function between 128 and 10240 MB. + * Must be a whole number. + * Default is 512MB. + */ + memoryMB?: number; }; /** diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 7c09ea97d9b..5d5a6938e26 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -104,7 +104,7 @@ export type FunctionProps = { /** * An amount of memory (RAM) to allocate to the function between 128 and 10240 MB. * Must be a whole number. - * Default is 128MB. + * Default is 512MB. */ memoryMB?: number; From b2057f9e0bbd0df5038c89f8e936201cd75c0e31 Mon Sep 17 00:00:00 2001 From: Kethan sai Date: Wed, 23 Oct 2024 16:47:59 -0400 Subject: [PATCH 060/199] adds version and help short hand arguments (#2144) --- .changeset/honest-ducks-act.md | 5 +++++ packages/cli/src/main_parser_factory.test.ts | 13 ++++++++++++- packages/cli/src/main_parser_factory.ts | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 .changeset/honest-ducks-act.md diff --git a/.changeset/honest-ducks-act.md b/.changeset/honest-ducks-act.md new file mode 100644 index 00000000000..90904974186 --- /dev/null +++ b/.changeset/honest-ducks-act.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-cli': minor +--- + +adds shorthand argument for version and help diff --git a/packages/cli/src/main_parser_factory.test.ts b/packages/cli/src/main_parser_factory.test.ts index 5a54ffa4a52..e957baa9515 100644 --- a/packages/cli/src/main_parser_factory.test.ts +++ b/packages/cli/src/main_parser_factory.test.ts @@ -17,11 +17,22 @@ void describe('main parser', { concurrency: false }, () => { assert.match(output, /generate\s+Generates post deployment artifacts/); }); - void it('shows version', async () => { + void it('includes generate command in shorthand help output', async () => { + const output = await commandRunner.runCommand('-h'); + assert.match(output, /Commands:/); + assert.match(output, /generate\s+Generates post deployment artifacts/); + }); + + void it('shows version for long version option', async () => { const output = await commandRunner.runCommand('--version'); assert.equal(output, `${version}\n`); }); + void it('shows version for shorthand version option', async () => { + const output = await commandRunner.runCommand('-v'); + assert.equal(output, `${version}\n`); + }); + void it('prints help if command is not provided', async () => { await assert.rejects( () => commandRunner.runCommand(''), diff --git a/packages/cli/src/main_parser_factory.ts b/packages/cli/src/main_parser_factory.ts index 5189c7de3f2..78fd56a5501 100644 --- a/packages/cli/src/main_parser_factory.ts +++ b/packages/cli/src/main_parser_factory.ts @@ -29,6 +29,8 @@ export const createMainParser = (libraryVersion: string): Argv => { .command(createConfigureCommand()) .command(createInfoCommand()) .help() + .alias('h', 'help') + .alias('v', 'version') .demandCommand() .strictCommands() .recommendCommands() From 10ef35d2fe00025fefd12603bfb2e3dce696be9a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:05:19 -0700 Subject: [PATCH 061/199] Version Packages (#2127) Co-authored-by: github-actions[bot] --- .changeset/happy-jokes-double.md | 5 ----- .changeset/honest-ducks-act.md | 5 ----- .changeset/nine-hornets-mate.md | 2 -- .changeset/ninety-cherries-applaud.md | 2 -- .changeset/red-candles-fly.md | 5 ----- .changeset/serious-trees-invent.md | 2 -- .changeset/strange-eggs-lie.md | 9 --------- .changeset/tiny-ears-fly.md | 6 ------ .changeset/tough-taxis-tan.md | 5 ----- packages/ai-constructs/CHANGELOG.md | 8 ++++++++ packages/ai-constructs/package.json | 4 ++-- packages/auth-construct/CHANGELOG.md | 8 ++++++++ packages/auth-construct/package.json | 4 ++-- packages/backend-ai/CHANGELOG.md | 11 +++++++++++ packages/backend-ai/package.json | 6 +++--- packages/backend-data/CHANGELOG.md | 8 ++++++++ packages/backend-data/package.json | 4 ++-- packages/backend-function/CHANGELOG.md | 8 ++++++++ packages/backend-function/package.json | 4 ++-- packages/backend-output-schemas/CHANGELOG.md | 6 ++++++ packages/backend-output-schemas/package.json | 2 +- packages/backend/CHANGELOG.md | 13 +++++++++++++ packages/backend/package.json | 10 +++++----- packages/cli/CHANGELOG.md | 13 +++++++++++++ packages/cli/package.json | 6 +++--- packages/client-config/CHANGELOG.md | 11 +++++++++++ packages/client-config/package.json | 4 ++-- 27 files changed, 108 insertions(+), 63 deletions(-) delete mode 100644 .changeset/happy-jokes-double.md delete mode 100644 .changeset/honest-ducks-act.md delete mode 100644 .changeset/nine-hornets-mate.md delete mode 100644 .changeset/ninety-cherries-applaud.md delete mode 100644 .changeset/red-candles-fly.md delete mode 100644 .changeset/serious-trees-invent.md delete mode 100644 .changeset/strange-eggs-lie.md delete mode 100644 .changeset/tiny-ears-fly.md delete mode 100644 .changeset/tough-taxis-tan.md diff --git a/.changeset/happy-jokes-double.md b/.changeset/happy-jokes-double.md deleted file mode 100644 index 54c395a105d..00000000000 --- a/.changeset/happy-jokes-double.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-data': patch ---- - -Update data-schema-types diff --git a/.changeset/honest-ducks-act.md b/.changeset/honest-ducks-act.md deleted file mode 100644 index 90904974186..00000000000 --- a/.changeset/honest-ducks-act.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-cli': minor ---- - -adds shorthand argument for version and help diff --git a/.changeset/nine-hornets-mate.md b/.changeset/nine-hornets-mate.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/nine-hornets-mate.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/ninety-cherries-applaud.md b/.changeset/ninety-cherries-applaud.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/ninety-cherries-applaud.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/red-candles-fly.md b/.changeset/red-candles-fly.md deleted file mode 100644 index d8802f845ac..00000000000 --- a/.changeset/red-candles-fly.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-function': patch ---- - -Fix jsdocs that incorrectly state default memory settings diff --git a/.changeset/serious-trees-invent.md b/.changeset/serious-trees-invent.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/serious-trees-invent.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/strange-eggs-lie.md b/.changeset/strange-eggs-lie.md deleted file mode 100644 index b4f16ffd00e..00000000000 --- a/.changeset/strange-eggs-lie.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@aws-amplify/backend-output-schemas': minor -'@aws-amplify/client-config': minor -'@aws-amplify/auth-construct': patch -'@aws-amplify/backend': patch -'@aws-amplify/backend-cli': patch ---- - -add user groups to outputs diff --git a/.changeset/tiny-ears-fly.md b/.changeset/tiny-ears-fly.md deleted file mode 100644 index 49fc36312ff..00000000000 --- a/.changeset/tiny-ears-fly.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/ai-constructs': patch -'@aws-amplify/backend-ai': patch ---- - -Add memory setting to conversation handler diff --git a/.changeset/tough-taxis-tan.md b/.changeset/tough-taxis-tan.md deleted file mode 100644 index a7d71c529e5..00000000000 --- a/.changeset/tough-taxis-tan.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-ai': patch ---- - -Use AiModel from data-schema-types as possible input diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index 42ca57f0a1c..f60205a020f 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/ai-constructs +## 0.6.2 + +### Patch Changes + +- bd4ff4d: Add memory setting to conversation handler +- Updated dependencies [5f46d8d] + - @aws-amplify/backend-output-schemas@1.4.0 + ## 0.6.1 ### Patch Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index e1d1c9575a7..dfda3c6cc1a 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "0.6.1", + "version": "0.6.2", "type": "commonjs", "publishConfig": { "access": "public" @@ -26,7 +26,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.3.0", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.0.1", "@aws-sdk/client-bedrock-runtime": "^3.622.0", diff --git a/packages/auth-construct/CHANGELOG.md b/packages/auth-construct/CHANGELOG.md index 3daa63b0973..0131feac702 100644 --- a/packages/auth-construct/CHANGELOG.md +++ b/packages/auth-construct/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/auth-construct +## 1.3.2 + +### Patch Changes + +- 5f46d8d: add user groups to outputs +- Updated dependencies [5f46d8d] + - @aws-amplify/backend-output-schemas@1.4.0 + ## 1.3.1 ### Patch Changes diff --git a/packages/auth-construct/package.json b/packages/auth-construct/package.json index f69909104b9..292804c060f 100644 --- a/packages/auth-construct/package.json +++ b/packages/auth-construct/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/auth-construct", - "version": "1.3.1", + "version": "1.3.2", "type": "commonjs", "publishConfig": { "access": "public" @@ -19,7 +19,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.1.0", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/plugin-types": "^1.2.2", "@aws-sdk/util-arn-parser": "^3.568.0" diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index 8fe5a022e55..156b1a5ed38 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend-ai +## 0.3.3 + +### Patch Changes + +- bd4ff4d: Add memory setting to conversation handler +- 0d6489d: Use AiModel from data-schema-types as possible input +- Updated dependencies [5f46d8d] +- Updated dependencies [bd4ff4d] + - @aws-amplify/backend-output-schemas@1.4.0 + - @aws-amplify/ai-constructs@0.6.2 + ## 0.3.2 ### Patch Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 0ecd974bada..bf8c19bf8a2 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "0.3.2", + "version": "0.3.3", "type": "module", "publishConfig": { "access": "public" @@ -22,8 +22,8 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.6.0", - "@aws-amplify/backend-output-schemas": "^1.3.0", + "@aws-amplify/ai-constructs": "^0.6.2", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.0.2", "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/platform-core": "^1.1.0", diff --git a/packages/backend-data/CHANGELOG.md b/packages/backend-data/CHANGELOG.md index 84349755363..9696801d2e8 100644 --- a/packages/backend-data/CHANGELOG.md +++ b/packages/backend-data/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-data +## 1.1.5 + +### Patch Changes + +- 0d6489d: Update data-schema-types +- Updated dependencies [5f46d8d] + - @aws-amplify/backend-output-schemas@1.4.0 + ## 1.1.4 ### Patch Changes diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index e61bdddc0ef..834a7bcd956 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-data", - "version": "1.1.4", + "version": "1.1.5", "type": "module", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ }, "dependencies": { "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/backend-output-schemas": "^1.1.0", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/data-construct": "^1.10.1", "@aws-amplify/plugin-types": "^1.2.2", "@aws-amplify/data-schema-types": "^1.2.0" diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index 56b83b92b67..31cb83a9a2b 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-function +## 1.7.1 + +### Patch Changes + +- bd4ff4d: Fix jsdocs that incorrectly state default memory settings +- Updated dependencies [5f46d8d] + - @aws-amplify/backend-output-schemas@1.4.0 + ## 1.7.0 ### Minor Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index c6266509831..e44cf19acab 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.7.0", + "version": "1.7.1", "type": "module", "publishConfig": { "access": "public" @@ -19,7 +19,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.1.0", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/plugin-types": "^1.3.0", "execa": "^8.0.1" diff --git a/packages/backend-output-schemas/CHANGELOG.md b/packages/backend-output-schemas/CHANGELOG.md index a50379c41e0..0212c02ffd6 100644 --- a/packages/backend-output-schemas/CHANGELOG.md +++ b/packages/backend-output-schemas/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/backend-output-schemas +## 1.4.0 + +### Minor Changes + +- 5f46d8d: add user groups to outputs + ## 1.3.0 ### Minor Changes diff --git a/packages/backend-output-schemas/package.json b/packages/backend-output-schemas/package.json index 1f1a70be4bc..93e0106dcbc 100644 --- a/packages/backend-output-schemas/package.json +++ b/packages/backend-output-schemas/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-output-schemas", - "version": "1.3.0", + "version": "1.4.0", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 41bd9901fd5..3dbfc94a1e2 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/backend +## 1.5.1 + +### Patch Changes + +- 5f46d8d: add user groups to outputs +- Updated dependencies [0d6489d] +- Updated dependencies [bd4ff4d] +- Updated dependencies [5f46d8d] + - @aws-amplify/backend-data@1.1.5 + - @aws-amplify/backend-function@1.7.1 + - @aws-amplify/backend-output-schemas@1.4.0 + - @aws-amplify/client-config@1.5.0 + ## 1.5.0 ### Minor Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 979906fcce1..fa2bb4cff0e 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.5.0", + "version": "1.5.1", "type": "module", "publishConfig": { "access": "public" @@ -27,13 +27,13 @@ "dependencies": { "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/backend-auth": "^1.2.0", - "@aws-amplify/backend-function": "^1.7.0", - "@aws-amplify/backend-data": "^1.1.4", - "@aws-amplify/backend-output-schemas": "^1.3.0", + "@aws-amplify/backend-function": "^1.7.1", + "@aws-amplify/backend-data": "^1.1.5", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.1", - "@aws-amplify/client-config": "^1.4.0", + "@aws-amplify/client-config": "^1.5.0", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.3.0", "@aws-sdk/client-amplify": "^3.624.0", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 6a1e1bfa24f..33b90df4827 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/backend-cli +## 1.3.0 + +### Minor Changes + +- b2057f9: adds shorthand argument for version and help + +### Patch Changes + +- 5f46d8d: add user groups to outputs +- Updated dependencies [5f46d8d] + - @aws-amplify/backend-output-schemas@1.4.0 + - @aws-amplify/client-config@1.5.0 + ## 1.2.9 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index a0559a1dc41..e95347875a0 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-cli", - "version": "1.2.9", + "version": "1.3.0", "description": "Command line interface for various Amplify tools", "bin": { "ampx": "lib/ampx.js", @@ -32,10 +32,10 @@ "homepage": "https://github.com/aws-amplify/amplify-backend#readme", "dependencies": { "@aws-amplify/backend-deployer": "^1.1.3", - "@aws-amplify/backend-output-schemas": "^1.2.1", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.1.3", - "@aws-amplify/client-config": "^1.4.0", + "@aws-amplify/client-config": "^1.5.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/form-generator": "^1.0.3", "@aws-amplify/model-generator": "^1.0.8", diff --git a/packages/client-config/CHANGELOG.md b/packages/client-config/CHANGELOG.md index e459d5953bf..120924601a3 100644 --- a/packages/client-config/CHANGELOG.md +++ b/packages/client-config/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/client-config +## 1.5.0 + +### Minor Changes + +- 5f46d8d: add user groups to outputs + +### Patch Changes + +- Updated dependencies [5f46d8d] + - @aws-amplify/backend-output-schemas@1.4.0 + ## 1.4.0 ### Minor Changes diff --git a/packages/client-config/package.json b/packages/client-config/package.json index d5d44aba63b..95e97b8e919 100644 --- a/packages/client-config/package.json +++ b/packages/client-config/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/client-config", - "version": "1.4.0", + "version": "1.5.0", "type": "module", "publishConfig": { "access": "public" @@ -24,7 +24,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.2.1", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/model-generator": "^1.0.7", "@aws-amplify/platform-core": "^1.0.7", From 63fb254127895afe018712c11040ba2fef627497 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Fri, 25 Oct 2024 12:16:55 -0700 Subject: [PATCH 062/199] Include accumulated turn content in chunk mutation (#2149) --- .changeset/sour-seahorses-walk.md | 5 + package-lock.json | 44 ++++---- .../runtime/bedrock_converse_adapter.test.ts | 49 ++++++++ .../runtime/bedrock_converse_adapter.ts | 13 +++ .../conversation_turn_executor.test.ts | 27 +---- .../runtime/conversation_turn_executor.ts | 6 +- .../conversation_turn_response_sender.test.ts | 105 +++++++++++++++++- .../conversation_turn_response_sender.ts | 57 ++++++++-- ...ion_turn_streaming_response_sender.test.ts | 72 ------------ ...ersation_turn_streaming_response_sender.ts | 48 -------- .../src/conversation/runtime/types.ts | 1 + .../amplify/data/resource.ts | 1 + 12 files changed, 249 insertions(+), 179 deletions(-) create mode 100644 .changeset/sour-seahorses-walk.md delete mode 100644 packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.test.ts delete mode 100644 packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.ts diff --git a/.changeset/sour-seahorses-walk.md b/.changeset/sour-seahorses-walk.md new file mode 100644 index 00000000000..919eaae261d --- /dev/null +++ b/.changeset/sour-seahorses-walk.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': minor +--- + +Include accumulated turn content in chunk mutation diff --git a/package-lock.json b/package-lock.json index 411d47ba6ed..0156b885e10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31455,10 +31455,10 @@ }, "packages/ai-constructs": { "name": "@aws-amplify/ai-constructs", - "version": "0.6.1", + "version": "0.6.2", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.3.0", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.0.1", "@aws-sdk/client-bedrock-runtime": "^3.622.0", @@ -31480,10 +31480,10 @@ }, "packages/auth-construct": { "name": "@aws-amplify/auth-construct", - "version": "1.3.1", + "version": "1.3.2", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.1.0", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/plugin-types": "^1.2.2", "@aws-sdk/util-arn-parser": "^3.568.0" @@ -31495,17 +31495,17 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.5.0", + "version": "1.5.1", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-auth": "^1.2.0", - "@aws-amplify/backend-data": "^1.1.4", - "@aws-amplify/backend-function": "^1.7.0", - "@aws-amplify/backend-output-schemas": "^1.3.0", + "@aws-amplify/backend-data": "^1.1.5", + "@aws-amplify/backend-function": "^1.7.1", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.1", - "@aws-amplify/client-config": "^1.4.0", + "@aws-amplify/client-config": "^1.5.0", "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.3.0", @@ -31524,11 +31524,11 @@ }, "packages/backend-ai": { "name": "@aws-amplify/backend-ai", - "version": "0.3.2", + "version": "0.3.3", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.6.0", - "@aws-amplify/backend-output-schemas": "^1.3.0", + "@aws-amplify/ai-constructs": "^0.6.2", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.0.2", "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/platform-core": "^1.1.0", @@ -31559,10 +31559,10 @@ }, "packages/backend-data": { "name": "@aws-amplify/backend-data", - "version": "1.1.4", + "version": "1.1.5", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.1.0", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/data-construct": "^1.10.1", "@aws-amplify/data-schema-types": "^1.2.0", @@ -31595,10 +31595,10 @@ }, "packages/backend-function": { "name": "@aws-amplify/backend-function", - "version": "1.7.0", + "version": "1.7.1", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.1.0", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/plugin-types": "^1.3.0", "execa": "^8.0.1" @@ -31631,7 +31631,7 @@ }, "packages/backend-output-schemas": { "name": "@aws-amplify/backend-output-schemas", - "version": "1.3.0", + "version": "1.4.0", "license": "Apache-2.0", "devDependencies": { "@aws-amplify/plugin-types": "^1.2.0" @@ -31696,14 +31696,14 @@ }, "packages/cli": { "name": "@aws-amplify/backend-cli", - "version": "1.2.9", + "version": "1.3.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-deployer": "^1.1.3", - "@aws-amplify/backend-output-schemas": "^1.2.1", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.1.3", - "@aws-amplify/client-config": "^1.4.0", + "@aws-amplify/client-config": "^1.5.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/form-generator": "^1.0.3", "@aws-amplify/model-generator": "^1.0.8", @@ -31850,10 +31850,10 @@ }, "packages/client-config": { "name": "@aws-amplify/client-config", - "version": "1.4.0", + "version": "1.5.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.2.1", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/model-generator": "^1.0.7", "@aws-amplify/platform-core": "^1.0.7", diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts index e75c672d5d8..ae424828c70 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts @@ -105,6 +105,11 @@ void describe('Bedrock converse adapter', () => { // See mockConverseStreamCommandOutput below of how split chunks are mocked. assert.deepStrictEqual(chunks, [ { + accumulatedTurnContent: [ + { + text: 'b', + }, + ], conversationId: event.conversationId, associatedUserMessageId: event.currentMessageId, contentBlockText: 'b', @@ -112,6 +117,11 @@ void describe('Bedrock converse adapter', () => { contentBlockDeltaIndex: 0, }, { + accumulatedTurnContent: [ + { + text: 'block1', + }, + ], conversationId: event.conversationId, associatedUserMessageId: event.currentMessageId, contentBlockText: 'lock1', @@ -119,12 +129,25 @@ void describe('Bedrock converse adapter', () => { contentBlockDeltaIndex: 1, }, { + accumulatedTurnContent: [ + { + text: 'block1', + }, + ], conversationId: event.conversationId, associatedUserMessageId: event.currentMessageId, contentBlockIndex: 0, contentBlockDoneAtIndex: 1, }, { + accumulatedTurnContent: [ + { + text: 'block1', + }, + { + text: 'b', + }, + ], conversationId: event.conversationId, associatedUserMessageId: event.currentMessageId, contentBlockText: 'b', @@ -132,6 +155,14 @@ void describe('Bedrock converse adapter', () => { contentBlockDeltaIndex: 0, }, { + accumulatedTurnContent: [ + { + text: 'block1', + }, + { + text: 'block2', + }, + ], conversationId: event.conversationId, associatedUserMessageId: event.currentMessageId, contentBlockText: 'lock2', @@ -139,12 +170,28 @@ void describe('Bedrock converse adapter', () => { contentBlockDeltaIndex: 1, }, { + accumulatedTurnContent: [ + { + text: 'block1', + }, + { + text: 'block2', + }, + ], conversationId: event.conversationId, associatedUserMessageId: event.currentMessageId, contentBlockIndex: 1, contentBlockDoneAtIndex: 1, }, { + accumulatedTurnContent: [ + { + text: 'block1', + }, + { + text: 'block2', + }, + ], conversationId: event.conversationId, associatedUserMessageId: event.currentMessageId, contentBlockIndex: 1, @@ -648,12 +695,14 @@ void describe('Bedrock converse adapter', () => { await askBedrockWithStreaming(adapter); assert.deepStrictEqual(chunks, [ { + accumulatedTurnContent: [{ toolUse: clientToolUse }], conversationId: event.conversationId, associatedUserMessageId: event.currentMessageId, contentBlockIndex: 0, contentBlockToolUse: JSON.stringify({ toolUse: clientToolUse }), }, { + accumulatedTurnContent: [{ toolUse: clientToolUse }], conversationId: event.conversationId, associatedUserMessageId: event.currentMessageId, contentBlockIndex: 0, diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts index d4da37d4b8f..510925492a9 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts @@ -20,6 +20,7 @@ import { } from './types.js'; import { ConversationTurnEventToolsProvider } from './event-tools-provider'; import { ConversationMessageHistoryRetriever } from './conversation_message_history_retriever'; +import * as bedrock from '@aws-sdk/client-bedrock-runtime'; /** * This class is responsible for interacting with Bedrock Converse API @@ -173,6 +174,9 @@ export class BedrockConverseAdapter { let blockIndex = 0; let lastBlockIndex = 0; let stopReason = ''; + // Accumulates client facing content per turn. + // So that upstream can persist full message at the end of the streaming. + const accumulatedTurnContent: Array = []; do { const toolConfig = this.createToolConfiguration(); const converseCommandInput: ConverseStreamCommandInput = { @@ -202,10 +206,12 @@ export class BedrockConverseAdapter { let toolUseInput: string = ''; let blockDeltaIndex = 0; let lastBlockDeltaIndex = 0; + // Accumulate current message for the tool use loop purpose. const accumulatedAssistantMessage: Message = { role: undefined, content: [], }; + for await (const chunk of bedrockResponse.stream) { this.logger.debug('Bedrock Converse Stream response chunk:', chunk); if (chunk.messageStart) { @@ -230,6 +236,7 @@ export class BedrockConverseAdapter { } else if (chunk.contentBlockDelta.delta?.text) { text += chunk.contentBlockDelta.delta.text; yield { + accumulatedTurnContent: [...accumulatedTurnContent, { text }], conversationId: this.event.conversationId, associatedUserMessageId: this.event.currentMessageId, contentBlockText: chunk.contentBlockDelta.delta.text, @@ -248,7 +255,9 @@ export class BedrockConverseAdapter { this.clientToolByName.has(toolUseBlock.toolUse.name) ) { clientToolsRequested = true; + accumulatedTurnContent.push(toolUseBlock); yield { + accumulatedTurnContent: [...accumulatedTurnContent], conversationId: this.event.conversationId, associatedUserMessageId: this.event.currentMessageId, contentBlockIndex: blockIndex, @@ -263,7 +272,9 @@ export class BedrockConverseAdapter { accumulatedAssistantMessage.content?.push({ text, }); + accumulatedTurnContent.push({ text }); yield { + accumulatedTurnContent: [...accumulatedTurnContent], conversationId: this.event.conversationId, associatedUserMessageId: this.event.currentMessageId, contentBlockIndex: blockIndex, @@ -285,6 +296,7 @@ export class BedrockConverseAdapter { // For now if any of client tools is used we ignore executable tools // and propagate result back to client. yield { + accumulatedTurnContent: [...accumulatedTurnContent], conversationId: this.event.conversationId, associatedUserMessageId: this.event.currentMessageId, contentBlockIndex: lastBlockIndex, @@ -313,6 +325,7 @@ export class BedrockConverseAdapter { } while (stopReason === 'tool_use'); yield { + accumulatedTurnContent: [...accumulatedTurnContent], conversationId: this.event.conversationId, associatedUserMessageId: this.event.currentMessageId, contentBlockIndex: lastBlockIndex, diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts index acd3a3e923e..be96f22fa38 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts @@ -5,7 +5,6 @@ import { ConversationTurnEvent, StreamingResponseChunk } from './types'; import { BedrockConverseAdapter } from './bedrock_converse_adapter'; import { ContentBlock } from '@aws-sdk/client-bedrock-runtime'; import { ConversationTurnResponseSender } from './conversation_turn_response_sender'; -import { ConversationTurnStreamingResponseSender } from './conversation_turn_streaming_response_sender'; void describe('Conversation turn executor', () => { const event: ConversationTurnEvent = { @@ -45,11 +44,8 @@ void describe('Conversation turn executor', () => { () => Promise.resolve() ); - const streamResponseSender = new ConversationTurnStreamingResponseSender( - event - ); const streamResponseSenderSendResponseMock = mock.method( - streamResponseSender, + responseSender, 'sendResponseChunk', () => Promise.resolve() ); @@ -68,7 +64,6 @@ void describe('Conversation turn executor', () => { [], bedrockConverseAdapter, responseSender, - streamResponseSender, consoleMock ).execute(); @@ -115,6 +110,7 @@ void describe('Conversation turn executor', () => { contentBlockDeltaIndex: 1, conversationId: 'testConversationId', associatedUserMessageId: 'testCurrentMessageId', + accumulatedTurnContent: [{ text: 'chunk1' }], }, { contentBlockText: 'chunk2', @@ -122,6 +118,7 @@ void describe('Conversation turn executor', () => { contentBlockDeltaIndex: 1, conversationId: 'testConversationId', associatedUserMessageId: 'testCurrentMessageId', + accumulatedTurnContent: [{ text: 'chunk1chunk2' }], }, ]; const bedrockConverseAdapterAskBedrockMock = mock.method( @@ -141,11 +138,8 @@ void describe('Conversation turn executor', () => { () => Promise.resolve() ); - const streamResponseSender = new ConversationTurnStreamingResponseSender( - event - ); const streamResponseSenderSendResponseMock = mock.method( - streamResponseSender, + responseSender, 'sendResponseChunk', () => Promise.resolve() ); @@ -164,7 +158,6 @@ void describe('Conversation turn executor', () => { [], bedrockConverseAdapter, responseSender, - streamResponseSender, consoleMock ).execute(); @@ -215,11 +208,8 @@ void describe('Conversation turn executor', () => { () => Promise.resolve() ); - const streamResponseSender = new ConversationTurnStreamingResponseSender( - event - ); const streamResponseSenderSendResponseMock = mock.method( - streamResponseSender, + responseSender, 'sendResponseChunk', () => Promise.resolve() ); @@ -240,7 +230,6 @@ void describe('Conversation turn executor', () => { [], bedrockConverseAdapter, responseSender, - streamResponseSender, consoleMock ).execute(), (error: Error) => { @@ -295,11 +284,8 @@ void describe('Conversation turn executor', () => { () => Promise.reject(responseSenderError) ); - const streamResponseSender = new ConversationTurnStreamingResponseSender( - event - ); const streamResponseSenderSendResponseMock = mock.method( - streamResponseSender, + responseSender, 'sendResponseChunk', () => Promise.resolve() ); @@ -320,7 +306,6 @@ void describe('Conversation turn executor', () => { [], bedrockConverseAdapter, responseSender, - streamResponseSender, consoleMock ).execute(), (error: Error) => { diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts index 01c19d857e4..04a73842079 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts @@ -1,7 +1,6 @@ import { ConversationTurnResponseSender } from './conversation_turn_response_sender.js'; import { ConversationTurnEvent, ExecutableTool, JSONSchema } from './types.js'; import { BedrockConverseAdapter } from './bedrock_converse_adapter.js'; -import { ConversationTurnStreamingResponseSender } from './conversation_turn_streaming_response_sender'; /** * This class is responsible for orchestrating conversation turn execution. @@ -22,9 +21,6 @@ export class ConversationTurnExecutor { additionalTools ), private readonly responseSender = new ConversationTurnResponseSender(event), - private readonly streamingResponseSender = new ConversationTurnStreamingResponseSender( - event - ), private readonly logger = console ) {} @@ -38,7 +34,7 @@ export class ConversationTurnExecutor { if (this.event.streamResponse) { const chunks = this.bedrockConverseAdapter.askBedrockStreaming(); for await (const chunk of chunks) { - await this.streamingResponseSender.sendResponseChunk(chunk); + await this.responseSender.sendResponseChunk(chunk); } } else { const assistantResponse = diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts index 1cb79fe365f..b38ccbdff90 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts @@ -3,8 +3,9 @@ import assert from 'node:assert'; import { ConversationTurnResponseSender, MutationResponseInput, + MutationStreamingResponseInput, } from './conversation_turn_response_sender'; -import { ConversationTurnEvent } from './types'; +import { ConversationTurnEvent, StreamingResponseChunk } from './types'; import { ContentBlock } from '@aws-sdk/client-bedrock-runtime'; import { GraphqlRequest, @@ -133,4 +134,106 @@ void describe('Conversation turn response sender', () => { }, }); }); + + void it('sends streaming response chunk back to appsync', async () => { + const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); + const executeGraphqlMock = mock.method( + graphqlRequestExecutor, + 'executeGraphql', + () => + // Mock successful Appsync response + Promise.resolve() + ); + const sender = new ConversationTurnResponseSender( + event, + graphqlRequestExecutor + ); + const chunk: StreamingResponseChunk = { + accumulatedTurnContent: [{ text: 'testAccumulatedMessageContent' }], + associatedUserMessageId: 'testAssociatedUserMessageId', + contentBlockIndex: 1, + contentBlockDeltaIndex: 2, + conversationId: 'testConversationId', + contentBlockText: 'testBlockText', + }; + await sender.sendResponseChunk(chunk); + + assert.strictEqual(executeGraphqlMock.mock.calls.length, 1); + const request = executeGraphqlMock.mock.calls[0] + .arguments[0] as GraphqlRequest; + assert.deepStrictEqual(request, { + query: + '\n' + + ' mutation PublishModelResponse($input: testResponseMutationInputTypeName!) {\n' + + ' testResponseMutationName(input: $input) {\n' + + ' testSelectionSet\n' + + ' }\n' + + ' }\n' + + ' ', + variables: { + input: chunk, + }, + }); + }); + + void it('serializes tool use input to JSON when streaming', async () => { + const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); + const executeGraphqlMock = mock.method( + graphqlRequestExecutor, + 'executeGraphql', + () => + // Mock successful Appsync response + Promise.resolve() + ); + const sender = new ConversationTurnResponseSender( + event, + graphqlRequestExecutor + ); + const toolUseBlock: ContentBlock.ToolUseMember = { + toolUse: { + name: 'testTool', + toolUseId: 'testToolUseId', + input: { + testPropertyKey: 'testPropertyValue', + }, + }, + }; + const chunk: StreamingResponseChunk = { + accumulatedTurnContent: [toolUseBlock], + associatedUserMessageId: 'testAssociatedUserMessageId', + contentBlockIndex: 1, + contentBlockDeltaIndex: 2, + conversationId: 'testConversationId', + contentBlockText: 'testBlockText', + }; + await sender.sendResponseChunk(chunk); + + assert.strictEqual(executeGraphqlMock.mock.calls.length, 1); + const request = executeGraphqlMock.mock.calls[0] + .arguments[0] as GraphqlRequest; + assert.deepStrictEqual(request, { + query: + '\n' + + ' mutation PublishModelResponse($input: testResponseMutationInputTypeName!) {\n' + + ' testResponseMutationName(input: $input) {\n' + + ' testSelectionSet\n' + + ' }\n' + + ' }\n' + + ' ', + variables: { + input: { + ...chunk, + accumulatedTurnContent: [ + { + toolUse: { + input: JSON.stringify(toolUseBlock.toolUse.input), + name: toolUseBlock.toolUse.name, + toolUseId: toolUseBlock.toolUse.toolUseId, + }, + }, + ], + }, + }, + }); + }); }); diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts index 9ca441fd6bc..7590f5edcfe 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts @@ -1,4 +1,4 @@ -import { ConversationTurnEvent } from './types.js'; +import { ConversationTurnEvent, StreamingResponseChunk } from './types.js'; import type { ContentBlock } from '@aws-sdk/client-bedrock-runtime'; import { GraphqlRequestExecutor } from './graphql_request_executor'; @@ -10,6 +10,10 @@ export type MutationResponseInput = { }; }; +export type MutationStreamingResponseInput = { + input: StreamingResponseChunk; +}; + /** * This class is responsible for sending a response produced by Bedrock back to AppSync * in a form of mutation. @@ -37,6 +41,15 @@ export class ConversationTurnResponseSender { >(responseMutationRequest); }; + sendResponseChunk = async (chunk: StreamingResponseChunk) => { + const responseMutationRequest = this.createStreamingMutationRequest(chunk); + this.logger.debug('Sending response mutation:', responseMutationRequest); + await this.graphqlRequestExecutor.executeGraphql< + MutationStreamingResponseInput, + void + >(responseMutationRequest); + }; + private createMutationRequest = (content: ContentBlock[]) => { const query = ` mutation PublishModelResponse($input: ${this.event.responseMutation.inputTypeName}!) { @@ -45,7 +58,39 @@ export class ConversationTurnResponseSender { } } `; - content = content.map((block) => { + content = this.serializeContent(content); + const variables: MutationResponseInput = { + input: { + conversationId: this.event.conversationId, + content, + associatedUserMessageId: this.event.currentMessageId, + }, + }; + return { query, variables }; + }; + + private createStreamingMutationRequest = (chunk: StreamingResponseChunk) => { + const query = ` + mutation PublishModelResponse($input: ${this.event.responseMutation.inputTypeName}!) { + ${this.event.responseMutation.name}(input: $input) { + ${this.event.responseMutation.selectionSet} + } + } + `; + chunk = { + ...chunk, + accumulatedTurnContent: this.serializeContent( + chunk.accumulatedTurnContent + ), + }; + const variables: MutationStreamingResponseInput = { + input: chunk, + }; + return { query, variables }; + }; + + private serializeContent = (content: ContentBlock[]) => { + return content.map((block) => { if (block.toolUse) { // The `input` field is typed as `AWS JSON` in the GraphQL API because it can represent // arbitrary JSON values. @@ -55,13 +100,5 @@ export class ConversationTurnResponseSender { } return block; }); - const variables: MutationResponseInput = { - input: { - conversationId: this.event.conversationId, - content, - associatedUserMessageId: this.event.currentMessageId, - }, - }; - return { query, variables }; }; } diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.test.ts deleted file mode 100644 index c94b8b5ff0a..00000000000 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { describe, it, mock } from 'node:test'; -import assert from 'node:assert'; -import { ConversationTurnEvent, StreamingResponseChunk } from './types'; -import { - GraphqlRequest, - GraphqlRequestExecutor, -} from './graphql_request_executor'; -import { - ConversationTurnStreamingResponseSender, - MutationStreamingResponseInput, -} from './conversation_turn_streaming_response_sender'; - -void describe('Conversation turn streaming response sender', () => { - const event: ConversationTurnEvent = { - conversationId: 'testConversationId', - currentMessageId: 'testCurrentMessageId', - graphqlApiEndpoint: 'http://fake.endpoint/', - messageHistoryQuery: { - getQueryName: '', - getQueryInputTypeName: '', - listQueryName: '', - listQueryInputTypeName: '', - }, - modelConfiguration: { modelId: '', systemPrompt: '' }, - request: { headers: { authorization: 'testToken' } }, - responseMutation: { - name: 'testResponseMutationName', - inputTypeName: 'testResponseMutationInputTypeName', - selectionSet: 'testSelectionSet', - }, - }; - - void it('sends streaming response chunk back to appsync', async () => { - const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); - const executeGraphqlMock = mock.method( - graphqlRequestExecutor, - 'executeGraphql', - () => - // Mock successful Appsync response - Promise.resolve() - ); - const sender = new ConversationTurnStreamingResponseSender( - event, - graphqlRequestExecutor - ); - const chunk: StreamingResponseChunk = { - associatedUserMessageId: 'testAssociatedUserMessageId', - contentBlockIndex: 1, - contentBlockDeltaIndex: 2, - conversationId: 'testConversationId', - contentBlockText: 'testBlockText', - }; - await sender.sendResponseChunk(chunk); - - assert.strictEqual(executeGraphqlMock.mock.calls.length, 1); - const request = executeGraphqlMock.mock.calls[0] - .arguments[0] as GraphqlRequest; - assert.deepStrictEqual(request, { - query: - '\n' + - ' mutation PublishModelResponse($input: testResponseMutationInputTypeName!) {\n' + - ' testResponseMutationName(input: $input) {\n' + - ' testSelectionSet\n' + - ' }\n' + - ' }\n' + - ' ', - variables: { - input: chunk, - }, - }); - }); -}); diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.ts deleted file mode 100644 index f5ccefac088..00000000000 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_streaming_response_sender.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { ConversationTurnEvent, StreamingResponseChunk } from './types.js'; -import { GraphqlRequestExecutor } from './graphql_request_executor'; - -export type MutationStreamingResponseInput = { - input: StreamingResponseChunk; -}; - -/** - * This class is responsible for sending response chunks produced by Bedrock back to AppSync - * in a form of mutation. - */ -export class ConversationTurnStreamingResponseSender { - /** - * Creates conversation turn response sender. - */ - constructor( - private readonly event: ConversationTurnEvent, - private readonly graphqlRequestExecutor = new GraphqlRequestExecutor( - event.graphqlApiEndpoint, - event.request.headers.authorization, - event.request.headers['x-amz-user-agent'] - ), - private readonly logger = console - ) {} - - sendResponseChunk = async (chunk: StreamingResponseChunk) => { - const responseMutationRequest = this.createMutationRequest(chunk); - this.logger.debug('Sending response mutation:', responseMutationRequest); - await this.graphqlRequestExecutor.executeGraphql< - MutationStreamingResponseInput, - void - >(responseMutationRequest); - }; - - private createMutationRequest = (chunk: StreamingResponseChunk) => { - const query = ` - mutation PublishModelResponse($input: ${this.event.responseMutation.inputTypeName}!) { - ${this.event.responseMutation.name}(input: $input) { - ${this.event.responseMutation.selectionSet} - } - } - `; - const variables: MutationStreamingResponseInput = { - input: chunk, - }; - return { query, variables }; - }; -} diff --git a/packages/ai-constructs/src/conversation/runtime/types.ts b/packages/ai-constructs/src/conversation/runtime/types.ts index 01d10a0975d..330a4ae4c1a 100644 --- a/packages/ai-constructs/src/conversation/runtime/types.ts +++ b/packages/ai-constructs/src/conversation/runtime/types.ts @@ -100,6 +100,7 @@ export type StreamingResponseChunk = { conversationId: string; associatedUserMessageId: string; contentBlockIndex: number; + accumulatedTurnContent: Array; } & ( | { // text chunk diff --git a/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts b/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts index 938b71980e6..0b8f9dc75fe 100644 --- a/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts +++ b/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts @@ -100,6 +100,7 @@ const schema = a.schema({ conversationId: a.id().required(), associatedUserMessageId: a.id().required(), contentBlockIndex: a.integer().required(), + accumulatedTurnContent: a.ref('MockContentBlock').array(), // these describe chunks or end of block contentBlockText: a.string(), From e4cdb59a1d2550b421c3923df340c731b280f7a6 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Fri, 25 Oct 2024 12:17:05 -0700 Subject: [PATCH 063/199] Handle process not found error in e2e tests (#2154) --- .changeset/polite-bats-wait.md | 2 ++ .../src/process-controller/execa_process_killer.ts | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 .changeset/polite-bats-wait.md diff --git a/.changeset/polite-bats-wait.md b/.changeset/polite-bats-wait.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/polite-bats-wait.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/process-controller/execa_process_killer.ts b/packages/integration-tests/src/process-controller/execa_process_killer.ts index 0f55e04c6fc..2054f3ee80a 100644 --- a/packages/integration-tests/src/process-controller/execa_process_killer.ts +++ b/packages/integration-tests/src/process-controller/execa_process_killer.ts @@ -14,8 +14,18 @@ export const killExecaProcess = async (processInstance: ExecaChildProcess) => { // turns out killing child process on Windows is a huge PITA // https://stackoverflow.com/questions/23706055/why-can-i-not-kill-my-child-process-in-nodejs-on-windows // https://github.com/sindresorhus/execa#killsignal-options - // eslint-disable-next-line spellcheck/spell-checker - await execa('taskkill', ['/pid', `${processInstance.pid}`, '/f', '/t']); + try { + // eslint-disable-next-line spellcheck/spell-checker + await execa('taskkill', ['/pid', `${processInstance.pid}`, '/f', '/t']); + } catch (e) { + // if process doesn't exist it means that it managed to exit gracefully by now. + // so don't fail in that case. + const isProcessNotFoundError = + e instanceof Error && e.message.includes('not found'); + if (!isProcessNotFoundError) { + throw e; + } + } } else { processInstance.kill('SIGINT'); } From 8a7f22e6ae3624bb8c281b11b23000e0dcb843e9 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Fri, 25 Oct 2024 13:06:27 -0700 Subject: [PATCH 064/199] Add flag to retain test project deployment. (#2151) --- .changeset/chilled-clouds-judge.md | 2 ++ packages/integration-tests/README.md | 21 ++++++++++++++----- .../test-e2e/sandbox/sandbox.test.template.ts | 7 ++++++- .../create_empty_amplify_project.ts | 7 ++++++- 4 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 .changeset/chilled-clouds-judge.md diff --git a/.changeset/chilled-clouds-judge.md b/.changeset/chilled-clouds-judge.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/chilled-clouds-judge.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/README.md b/packages/integration-tests/README.md index 780c9a1929d..f5e3f4b709e 100644 --- a/packages/integration-tests/README.md +++ b/packages/integration-tests/README.md @@ -17,14 +17,25 @@ or `npm run test:dir packages/integration-tests/lib/test-in-memory` (to run them The create-amplify e2e suite tests the first-time installation and setup of a new amplify backend project. To run this suite, run `npm run test:dir packages/integration-tests/lib/test-e2e/create_amplify.test.js` -## deployment tests +## deployment and sandbox tests -To run end-to-end deployment tests, credentials to an AWS account must be available on the machine. Any credentials that will be picked up by the +To run end-to-end deployment or sandbox tests, credentials to an AWS account must be available on the machine. Any credentials that will be picked up by the [default node credential provider](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-credentials-node.html) should work. -This include setting environment variables for a default profile. +This includes setting environment variables for a default profile. -To run this suite, run -`npm run test:dir packages/integration-tests/lib/test-e2e/deployment.test.js` +To run deployment suite, run +`npm run test:dir packages/integration-tests/lib/test-e2e/deployment/*.deployment.test.js` + +To run sandbox suite, run +`npm run test:dir packages/integration-tests/lib/test-e2e/sandbox/*.sandbox.test.js` + +To run deployment or sandbox test for specific project, specify exact test file, for example +`npm run test:dir packages/integration-tests/lib/test-e2e/sandbox/data_storage_auth_with_triggers.sandbox.test.js` + +When working locally with sandbox tests, it is sometimes useful to retain deployment of test project to avoid full re-deployments while working +on single test project incrementally. To retain deployment set `AMPLIFY_BACKEND_TESTS_RETAIN_TEST_PROJECT_DEPLOYMENT` environment +variable to `true`. This flag disables project name randomization and deployment cleanup, so that subsequent runs of same test +target the same CFN stacks. This option is not available for deployment tests (hotswap is not going to work there anyway). ## backend-output tests diff --git a/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts b/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts index 03068bedf19..cac7decd9ac 100644 --- a/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts +++ b/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts @@ -48,7 +48,12 @@ export const defineSandboxTest = (testProjectCreator: TestProjectCreator) => { }); after(async () => { - await testProject.tearDown(sandboxBackendIdentifier); + if ( + process.env.AMPLIFY_BACKEND_TESTS_RETAIN_TEST_PROJECT_DEPLOYMENT !== + 'true' + ) { + await testProject.tearDown(sandboxBackendIdentifier); + } }); void describe('in sequence', { concurrency: false }, () => { diff --git a/packages/integration-tests/src/test-project-setup/create_empty_amplify_project.ts b/packages/integration-tests/src/test-project-setup/create_empty_amplify_project.ts index 97afcf198b8..b86f280f32f 100644 --- a/packages/integration-tests/src/test-project-setup/create_empty_amplify_project.ts +++ b/packages/integration-tests/src/test-project-setup/create_empty_amplify_project.ts @@ -19,7 +19,12 @@ export const createEmptyAmplifyProject = async ( projectDotAmplifyDir: string; }> => { const projectRoot = await fs.mkdtemp(path.join(parentDir, projectDirName)); - const projectName = `${TEST_PROJECT_PREFIX}-${projectDirName}-${shortUuid()}`; + let projectName = `${TEST_PROJECT_PREFIX}-${projectDirName}`; + if ( + process.env.AMPLIFY_BACKEND_TESTS_RETAIN_TEST_PROJECT_DEPLOYMENT !== 'true' + ) { + projectName += `-${shortUuid()}`; + } await fs.writeFile( path.join(projectRoot, 'package.json'), JSON.stringify({ name: projectName, type: 'module' }, null, 2) From 7d74e8eacbead1805e22ae7aed95dfbddb73d191 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 25 Oct 2024 13:13:29 -0700 Subject: [PATCH 065/199] add assertion at end of testing schedule invokes function (#2152) * add assertion at end of testing schedule invokes function * change name of variable --- .changeset/eleven-chairs-camp.md | 2 ++ .../data_storage_auth_with_triggers.ts | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 .changeset/eleven-chairs-camp.md diff --git a/.changeset/eleven-chairs-camp.md b/.changeset/eleven-chairs-camp.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/eleven-chairs-camp.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts index 414ea28b050..893c3bb8ffa 100644 --- a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts +++ b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts @@ -603,7 +603,7 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { ) => { const TIMEOUT_MS = 1000 * 60 * 2; // 2 minutes const startTime = Date.now(); - let messageCount = 0; + let receivedMessageCount = 0; const queue = await this.resourceFinder.findByBackendIdentifier( backendId, @@ -612,16 +612,17 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { ); // wait for schedule to invoke the function one time for it to send a message - while (Date.now() - startTime < TIMEOUT_MS && messageCount < 1) { + while (Date.now() - startTime < TIMEOUT_MS) { const response = await this.sqsClient.send( new ReceiveMessageCommand({ QueueUrl: queue[0], WaitTimeSeconds: 20, + MaxNumberOfMessages: 10, }) ); if (response.Messages) { - messageCount += response.Messages.length; + receivedMessageCount += response.Messages.length; // delete messages afterwards for (const message of response.Messages) { @@ -634,5 +635,11 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { } } } + + if (receivedMessageCount === 0) { + assert.fail( + `The scheduled function failed to invoke and send a message to the queue.` + ); + } }; } From 601a2c1a0bb969fcf71180a5432a4ab2e71270c5 Mon Sep 17 00:00:00 2001 From: Kethan sai Date: Fri, 25 Oct 2024 16:50:39 -0400 Subject: [PATCH 066/199] dedupe environment variables in amplify env type generator (#2153) --- .changeset/silver-rocks-return.md | 6 ++++ .../src/function_env_type_generator.test.ts | 32 +++++++++++++++++++ .../src/function_env_type_generator.ts | 6 +++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 .changeset/silver-rocks-return.md diff --git a/.changeset/silver-rocks-return.md b/.changeset/silver-rocks-return.md new file mode 100644 index 00000000000..14f217d3723 --- /dev/null +++ b/.changeset/silver-rocks-return.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/backend-function': patch +'@aws-amplify/backend': patch +--- + +dedupe environment variables in amplify env type generator diff --git a/packages/backend-function/src/function_env_type_generator.test.ts b/packages/backend-function/src/function_env_type_generator.test.ts index d988e06fec4..15a913479c3 100644 --- a/packages/backend-function/src/function_env_type_generator.test.ts +++ b/packages/backend-function/src/function_env_type_generator.test.ts @@ -69,4 +69,36 @@ void describe('FunctionEnvironmentTypeGenerator', () => { await fsp.rm(targetDirectory, { recursive: true, force: true }); }); + + void it('does not generate duplicate environment variables', () => { + const fsOpenSyncMock = mock.method(fs, 'openSync'); + const fsWriteFileSyncMock = mock.method(fs, 'writeFileSync', () => null); + fsOpenSyncMock.mock.mockImplementation(() => 0); + const functionEnvironmentTypeGenerator = + new FunctionEnvironmentTypeGenerator('testFunction'); + + functionEnvironmentTypeGenerator.generateTypedProcessEnvShim([ + 'TEST_ENV', + 'TEST_ENV', + 'ANOTHER_ENV', + ]); + + const generatedContent = + fsWriteFileSyncMock.mock.calls[0].arguments[1]?.toString() ?? ''; + + // Check TEST_ENV appears only once + assert.equal( + (generatedContent.match(/TEST_ENV: string;/g) || []).length, + 1, + 'TEST_ENV should appear only once' + ); + + // Check ANOTHER_ENV also appears + assert.ok( + generatedContent.includes('ANOTHER_ENV: string;'), + 'ANOTHER_ENV should be included' + ); + + mock.restoreAll(); + }); }); diff --git a/packages/backend-function/src/function_env_type_generator.ts b/packages/backend-function/src/function_env_type_generator.ts index ea650e26c9b..d01a701a190 100644 --- a/packages/backend-function/src/function_env_type_generator.ts +++ b/packages/backend-function/src/function_env_type_generator.ts @@ -57,7 +57,11 @@ export class FunctionEnvironmentTypeGenerator { `/** Amplify backend environment variables available at runtime, this includes environment variables defined in \`defineFunction\` and by cross resource mechanisms */` ); declarations.push(`type ${amplifyBackendEnvVarTypeName} = {`); - amplifyBackendEnvVars.forEach((envName) => { + + // Use a Set to remove duplicates + const uniqueEnvVars = new Set(amplifyBackendEnvVars); + + uniqueEnvVars.forEach((envName) => { const declaration = `${this.indentation}${envName}: string;`; declarations.push(declaration); From 9a5d8e3f985e7b4ecf46babe709ca482d1d03b4f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:58:10 +0000 Subject: [PATCH 067/199] Version Packages (#2155) Co-authored-by: github-actions[bot] --- .changeset/chilled-clouds-judge.md | 2 -- .changeset/eleven-chairs-camp.md | 2 -- .changeset/polite-bats-wait.md | 2 -- .changeset/silver-rocks-return.md | 6 ------ .changeset/sour-seahorses-walk.md | 5 ----- packages/ai-constructs/CHANGELOG.md | 6 ++++++ packages/ai-constructs/package.json | 2 +- packages/backend-ai/CHANGELOG.md | 7 +++++++ packages/backend-ai/package.json | 4 ++-- packages/backend-function/CHANGELOG.md | 6 ++++++ packages/backend-function/package.json | 2 +- packages/backend/CHANGELOG.md | 8 ++++++++ packages/backend/package.json | 4 ++-- packages/integration-tests/package.json | 6 +++--- 14 files changed, 36 insertions(+), 26 deletions(-) delete mode 100644 .changeset/chilled-clouds-judge.md delete mode 100644 .changeset/eleven-chairs-camp.md delete mode 100644 .changeset/polite-bats-wait.md delete mode 100644 .changeset/silver-rocks-return.md delete mode 100644 .changeset/sour-seahorses-walk.md diff --git a/.changeset/chilled-clouds-judge.md b/.changeset/chilled-clouds-judge.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/chilled-clouds-judge.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/eleven-chairs-camp.md b/.changeset/eleven-chairs-camp.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/eleven-chairs-camp.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/polite-bats-wait.md b/.changeset/polite-bats-wait.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/polite-bats-wait.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/silver-rocks-return.md b/.changeset/silver-rocks-return.md deleted file mode 100644 index 14f217d3723..00000000000 --- a/.changeset/silver-rocks-return.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/backend-function': patch -'@aws-amplify/backend': patch ---- - -dedupe environment variables in amplify env type generator diff --git a/.changeset/sour-seahorses-walk.md b/.changeset/sour-seahorses-walk.md deleted file mode 100644 index 919eaae261d..00000000000 --- a/.changeset/sour-seahorses-walk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': minor ---- - -Include accumulated turn content in chunk mutation diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index f60205a020f..ad27b99ebb4 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/ai-constructs +## 0.7.0 + +### Minor Changes + +- 63fb254: Include accumulated turn content in chunk mutation + ## 0.6.2 ### Patch Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index dfda3c6cc1a..f329ef6e038 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "0.6.2", + "version": "0.7.0", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index 156b1a5ed38..e1763c6149a 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,12 @@ # @aws-amplify/backend-ai +## 0.3.4 + +### Patch Changes + +- Updated dependencies [63fb254] + - @aws-amplify/ai-constructs@0.7.0 + ## 0.3.3 ### Patch Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index bf8c19bf8a2..12196fac932 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "0.3.3", + "version": "0.3.4", "type": "module", "publishConfig": { "access": "public" @@ -22,7 +22,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.6.2", + "@aws-amplify/ai-constructs": "^0.7.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.0.2", "@aws-amplify/data-schema-types": "^1.2.0", diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index 31cb83a9a2b..9eb523919d4 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/backend-function +## 1.7.2 + +### Patch Changes + +- 601a2c1: dedupe environment variables in amplify env type generator + ## 1.7.1 ### Patch Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index e44cf19acab..1286d277d34 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.7.1", + "version": "1.7.2", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 3dbfc94a1e2..271e7ff53a4 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend +## 1.5.2 + +### Patch Changes + +- 601a2c1: dedupe environment variables in amplify env type generator +- Updated dependencies [601a2c1] + - @aws-amplify/backend-function@1.7.2 + ## 1.5.1 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index fa2bb4cff0e..4d05c7b7a71 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.5.1", + "version": "1.5.2", "type": "module", "publishConfig": { "access": "public" @@ -27,7 +27,7 @@ "dependencies": { "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/backend-auth": "^1.2.0", - "@aws-amplify/backend-function": "^1.7.1", + "@aws-amplify/backend-function": "^1.7.2", "@aws-amplify/backend-data": "^1.1.5", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.2", diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 692dd9a25c3..b49ec966f44 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -5,10 +5,10 @@ "type": "module", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.6.0", + "@aws-amplify/ai-constructs": "^0.7.0", "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend": "^1.5.0", - "@aws-amplify/backend-ai": "^0.3.2", + "@aws-amplify/backend": "^1.5.2", + "@aws-amplify/backend-ai": "^0.3.4", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/data-schema": "^1.0.0", From c3c3057eae9a7b008ad5585996a2a9e17d1e5dac Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Tue, 29 Oct 2024 17:01:10 -0700 Subject: [PATCH 068/199] update ctrl+c behavior to always print guidance and not prompt (#2160) * update ctrl+c behavior to always print guidance and not prompt * pr feedback * Update .changeset/bright-jokes-draw.md Co-authored-by: Kamil Sobol --------- Co-authored-by: Kamil Sobol --- .changeset/bright-jokes-draw.md | 6 + .../package_manager_controller_base.ts | 1 + .../pnpm_package_manager_controller.ts | 6 - ...yarn_classic_package_manager_controller.ts | 6 - .../sandbox_delete_command.test.ts | 10 +- .../commands/sandbox/sandbox_command.test.ts | 123 +----------------- .../src/commands/sandbox/sandbox_command.ts | 26 +--- .../sandbox/sandbox_command_factory.ts | 7 +- 8 files changed, 18 insertions(+), 167 deletions(-) create mode 100644 .changeset/bright-jokes-draw.md diff --git a/.changeset/bright-jokes-draw.md b/.changeset/bright-jokes-draw.md new file mode 100644 index 00000000000..9a3649dfde2 --- /dev/null +++ b/.changeset/bright-jokes-draw.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/cli-core': minor +'@aws-amplify/backend-cli': minor +--- + +update ctrl+c behavior to always print guidance to delete and exit with no prompt diff --git a/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts b/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts index d722ef84f57..4d9786b3910 100644 --- a/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts +++ b/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts @@ -137,6 +137,7 @@ export abstract class PackageManagerControllerBase /** * allowsSignalPropagation - Determines if the package manager allows the process * signals such as SIGINT to be propagated to the underlying node process. + * @deprecated */ allowsSignalPropagation = () => true; diff --git a/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.ts b/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.ts index 321ad9cd26c..5eb13ea7de3 100644 --- a/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.ts +++ b/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.ts @@ -32,10 +32,4 @@ export class PnpmPackageManagerController extends PackageManagerControllerBase { existsSync ); } - - /** - * Pnpm doesn't handle the node process gracefully during the SIGINT life cycle. - * See: https://github.com/pnpm/pnpm/issues/7374 - */ - allowsSignalPropagation = () => false; } diff --git a/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.ts b/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.ts index 2f7a837e769..48a4330ed96 100644 --- a/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.ts +++ b/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.ts @@ -37,12 +37,6 @@ export class YarnClassicPackageManagerController extends PackageManagerControlle await this.addTypescript(targetDir); await super.initializeTsConfig(targetDir); }; - /** - * - * Yarn doesn't respect the SIGINT life cycle and exits immediately leaving - * the node process hanging. See: https://github.com/yarnpkg/yarn/issues/8895 - */ - allowsSignalPropagation = () => false; private addTypescript = async (targetDir: string) => { await this.executeWithDebugLogger( diff --git a/packages/cli/src/commands/sandbox/sandbox-delete/sandbox_delete_command.test.ts b/packages/cli/src/commands/sandbox/sandbox-delete/sandbox_delete_command.test.ts index 62e80f946c1..c919cd61413 100644 --- a/packages/cli/src/commands/sandbox/sandbox-delete/sandbox_delete_command.test.ts +++ b/packages/cli/src/commands/sandbox/sandbox-delete/sandbox_delete_command.test.ts @@ -1,10 +1,5 @@ import { beforeEach, describe, it, mock } from 'node:test'; -import { - AmplifyPrompter, - PackageManagerControllerFactory, - format, - printer, -} from '@aws-amplify/cli-core'; +import { AmplifyPrompter, format, printer } from '@aws-amplify/cli-core'; import yargs, { CommandModule } from 'yargs'; import { TestCommandRunner } from '../../../test-utils/command_runner.js'; import assert from 'node:assert'; @@ -50,8 +45,7 @@ void describe('sandbox delete command', () => { sandboxFactory, [sandboxDeleteCommand, createSandboxSecretCommand()], clientConfigGeneratorAdapterMock, - commandMiddleware, - new PackageManagerControllerFactory().getPackageManagerController() + commandMiddleware ); const parser = yargs().command(sandboxCommand as unknown as CommandModule); commandRunner = new TestCommandRunner(parser); diff --git a/packages/cli/src/commands/sandbox/sandbox_command.test.ts b/packages/cli/src/commands/sandbox/sandbox_command.test.ts index ce5e2ca9575..6d7df80f9b1 100644 --- a/packages/cli/src/commands/sandbox/sandbox_command.test.ts +++ b/packages/cli/src/commands/sandbox/sandbox_command.test.ts @@ -8,7 +8,7 @@ import { TestCommandError, TestCommandRunner, } from '../../test-utils/command_runner.js'; -import { AmplifyPrompter, format, printer } from '@aws-amplify/cli-core'; +import { format, printer } from '@aws-amplify/cli-core'; import { EventHandler, SandboxCommand } from './sandbox_command.js'; import { createSandboxCommand } from './sandbox_command_factory.js'; import { SandboxDeleteCommand } from './sandbox-delete/sandbox_delete_command.js'; @@ -20,7 +20,6 @@ import { import { createSandboxSecretCommand } from './sandbox-secret/sandbox_secret_command_factory.js'; import { ClientConfigGeneratorAdapter } from '../../client-config/client_config_generator_adapter.js'; import { CommandMiddleware } from '../../command_middleware.js'; -import { PackageManagerController } from '@aws-amplify/plugin-types'; import { AmplifyError } from '@aws-amplify/platform-core'; mock.method(fsp, 'mkdir', () => Promise.resolve()); @@ -54,11 +53,6 @@ void describe('sandbox command', () => { ); const sandboxProfile = 'test-sandbox'; - const allowsSignalPropagationMock = mock.fn(() => true); - const packageManagerControllerMock = { - allowsSignalPropagation: allowsSignalPropagationMock, - } as unknown as PackageManagerController; - beforeEach(async () => { const sandboxFactory = new SandboxSingletonFactory( () => @@ -80,7 +74,6 @@ void describe('sandbox command', () => { [sandboxDeleteCommand, createSandboxSecretCommand()], clientConfigGeneratorAdapterMock, commandMiddleware, - packageManagerControllerMock, () => ({ successfulDeployment: [clientConfigGenerationMock], successfulDeletion: [clientConfigDeletionMock], @@ -189,118 +182,7 @@ void describe('sandbox command', () => { ); }); - void it('asks to delete the sandbox environment when users send ctrl-C and say yes to delete', async (contextual) => { - // Mock process and extract the sigint handler after calling the sandbox command - const processSignal = contextual.mock.method(process, 'on', () => { - /* no op */ - }); - const sandboxStartMock = contextual.mock.method( - sandbox, - 'start', - async () => Promise.resolve() - ); - - const sandboxDeleteMock = contextual.mock.method(sandbox, 'delete', () => - Promise.resolve() - ); - - // User said yes to delete - contextual.mock.method(AmplifyPrompter, 'yesOrNo', () => - Promise.resolve(true) - ); - - await commandRunner.runCommand('sandbox'); - - // Similar to the later 0ms timeout. Without this tests in github action are failing - // but working locally - await new Promise((resolve) => setTimeout(resolve, 0)); - const sigIntHandlerFn = processSignal.mock.calls[0].arguments[1]; - if (sigIntHandlerFn) sigIntHandlerFn(); - - // I can't find any open node:test or yargs issues that would explain why this is necessary - // but for some reason the mock call count does not update without this 0ms wait - await new Promise((resolve) => setTimeout(resolve, 0)); - assert.equal(sandboxStartMock.mock.callCount(), 1); - assert.equal(sandboxDeleteMock.mock.callCount(), 1); - }); - - void it('asks to delete the sandbox environment when users send ctrl-C and say yes to delete with profile', async (contextual) => { - // Mock process and extract the sigint handler after calling the sandbox command - const processSignal = contextual.mock.method(process, 'on', () => { - /* no op */ - }); - const sandboxStartMock = contextual.mock.method( - sandbox, - 'start', - async () => Promise.resolve() - ); - - const sandboxDeleteMock = contextual.mock.method(sandbox, 'delete', () => - Promise.resolve() - ); - - // User said yes to delete - contextual.mock.method(AmplifyPrompter, 'yesOrNo', () => - Promise.resolve(true) - ); - - const profile = 'test_profile'; - await commandRunner.runCommand(`sandbox --profile ${profile}`); - - // Similar to the later 0ms timeout. Without this tests in github action are failing - // but working locally - await new Promise((resolve) => setTimeout(resolve, 0)); - const sigIntHandlerFn = processSignal.mock.calls[0].arguments[1]; - if (sigIntHandlerFn) sigIntHandlerFn(); - - // I can't find any open node:test or yargs issues that would explain why this is necessary - // but for some reason the mock call count does not update without this 0ms wait - await new Promise((resolve) => setTimeout(resolve, 0)); - assert.equal(sandboxStartMock.mock.callCount(), 1); - assert.equal(sandboxDeleteMock.mock.callCount(), 1); - assert.deepStrictEqual(sandboxDeleteMock.mock.calls[0].arguments[0], { - identifier: undefined, - profile, - }); - }); - - void it('asks to delete the sandbox environment when users send ctrl-C and say no to delete', async (contextual) => { - // Mock process and extract the sigint handler after calling the sandbox command - const processSignal = contextual.mock.method(process, 'on', () => { - /* no op */ - }); - const sandboxStartMock = contextual.mock.method( - sandbox, - 'start', - async () => Promise.resolve() - ); - - const sandboxDeleteMock = contextual.mock.method( - sandbox, - 'delete', - async () => Promise.resolve() - ); - - // User said no to delete - contextual.mock.method(AmplifyPrompter, 'yesOrNo', () => - Promise.resolve(false) - ); - - await commandRunner.runCommand('sandbox'); - - // Similar to the previous test's 0ms timeout. Without this tests in github action are failing - // but working locally - await new Promise((resolve) => setTimeout(resolve, 0)); - const sigIntHandlerFn = processSignal.mock.calls[0].arguments[1]; - if (sigIntHandlerFn) sigIntHandlerFn(); - - assert.equal(sandboxStartMock.mock.callCount(), 1); - assert.equal(sandboxDeleteMock.mock.callCount(), 0); - }); - - void it('Does not prompt for deleting the sandbox if package manager does not allow signal propagation', async (contextual) => { - allowsSignalPropagationMock.mock.mockImplementationOnce(() => false); - + void it('Prints stopping sandbox and instructions to delete sandbox when users send ctrl+c', async (contextual) => { // Mock process and extract the sigint handler after calling the sandbox command const processSignal = contextual.mock.method(process, 'on', () => { /* no op */ @@ -371,7 +253,6 @@ void describe('sandbox command', () => { [], clientConfigGeneratorAdapterMock, commandMiddleware, - packageManagerControllerMock, undefined ); const parser = yargs().command(sandboxCommand as unknown as CommandModule); diff --git a/packages/cli/src/commands/sandbox/sandbox_command.ts b/packages/cli/src/commands/sandbox/sandbox_command.ts index 356d0a8eb66..37c069286b6 100644 --- a/packages/cli/src/commands/sandbox/sandbox_command.ts +++ b/packages/cli/src/commands/sandbox/sandbox_command.ts @@ -1,7 +1,7 @@ import { ArgumentsCamelCase, Argv, CommandModule } from 'yargs'; import fs from 'fs'; import fsp from 'fs/promises'; -import { AmplifyPrompter, format, printer } from '@aws-amplify/cli-core'; +import { format, printer } from '@aws-amplify/cli-core'; import { SandboxFunctionStreamingOptions, SandboxSingletonFactory, @@ -20,7 +20,6 @@ import { ClientConfigGeneratorAdapter } from '../../client-config/client_config_ import { CommandMiddleware } from '../../command_middleware.js'; import { SandboxCommandGlobalOptions } from './option_types.js'; import { ArgumentsKebabCase } from '../../kebab_case.js'; -import { PackageManagerController } from '@aws-amplify/plugin-types'; import { AmplifyUserError } from '@aws-amplify/platform-core'; export type SandboxCommandOptionsKebabCase = ArgumentsKebabCase< @@ -81,7 +80,6 @@ export class SandboxCommand private readonly sandboxSubCommands: CommandModule[], private clientConfigGeneratorAdapter: ClientConfigGeneratorAdapter, private commandMiddleware: CommandMiddleware, - private readonly packageManagerController: PackageManagerController, private readonly sandboxEventHandlerCreator?: SandboxEventHandlerCreator ) { this.command = 'sandbox'; @@ -276,23 +274,11 @@ export class SandboxCommand }; sigIntHandler = async () => { - if (!this.packageManagerController.allowsSignalPropagation()) { - printer.print( - `Stopping the sandbox process. To delete the sandbox, run ${format.normalizeAmpxCommand( - 'sandbox delete' - )}` - ); - return; - } - const answer = await AmplifyPrompter.yesOrNo({ - message: - 'Would you like to delete all the resources in your sandbox environment (This cannot be undone)?', - defaultValue: false, - }); - if (answer) - await ( - await this.sandboxFactory.getInstance() - ).delete({ identifier: this.sandboxIdentifier, profile: this.profile }); + printer.print( + `Stopping the sandbox process. To delete the sandbox, run ${format.normalizeAmpxCommand( + 'sandbox delete' + )}` + ); }; private validateDirectory = async (option: string, dir: string) => { diff --git a/packages/cli/src/commands/sandbox/sandbox_command_factory.ts b/packages/cli/src/commands/sandbox/sandbox_command_factory.ts index 07afc979d06..ef5454a9319 100644 --- a/packages/cli/src/commands/sandbox/sandbox_command_factory.ts +++ b/packages/cli/src/commands/sandbox/sandbox_command_factory.ts @@ -16,11 +16,7 @@ import { } from '@aws-amplify/platform-core'; import { SandboxEventHandlerFactory } from './sandbox_event_handler_factory.js'; import { CommandMiddleware } from '../../command_middleware.js'; -import { - PackageManagerControllerFactory, - format, - printer, -} from '@aws-amplify/cli-core'; +import { format, printer } from '@aws-amplify/cli-core'; import { S3Client } from '@aws-sdk/client-s3'; import { AmplifyClient } from '@aws-sdk/client-amplify'; import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; @@ -70,7 +66,6 @@ export const createSandboxCommand = (): CommandModule< [new SandboxDeleteCommand(sandboxFactory), createSandboxSecretCommand()], clientConfigGeneratorAdapter, commandMiddleWare, - new PackageManagerControllerFactory().getPackageManagerController(), eventHandlerFactory.getSandboxEventHandlers ); }; From fbdba22b4571691d34bc20333ebc5d91069c9c37 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Wed, 30 Oct 2024 13:26:36 -0700 Subject: [PATCH 069/199] feed pr base sha and ref into envs before scripts (#2168) * feed pr base sha and ref into envs before scripts * removing empty file --- .changeset/spicy-rules-speak.md | 2 ++ .github/workflows/health_checks.yml | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 .changeset/spicy-rules-speak.md diff --git a/.changeset/spicy-rules-speak.md b/.changeset/spicy-rules-speak.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/spicy-rules-speak.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/workflows/health_checks.yml b/.github/workflows/health_checks.yml index 8c1bff59935..fef7c581b8d 100644 --- a/.github/workflows/health_checks.yml +++ b/.github/workflows/health_checks.yml @@ -429,7 +429,9 @@ jobs: - uses: ./.github/actions/setup_node - uses: ./.github/actions/restore_install_cache - run: git fetch origin - - run: npm run diff:check ${{ github.event.pull_request.base.sha }} + - run: npm run diff:check "$BASE_SHA" + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} check_pr_changesets: if: github.event_name == 'pull_request' && github.event.pull_request.user.login != 'github-actions[bot]' runs-on: ubuntu-latest @@ -443,9 +445,13 @@ jobs: - uses: ./.github/actions/setup_node - uses: ./.github/actions/restore_install_cache - name: Validate that PR has changeset - run: npx changeset status --since origin/${{ github.event.pull_request.base.ref }} + run: npx changeset status --since origin/"$BASE_REF" + env: + BASE_REF: ${{ github.event.pull_request.base.ref }} - name: Validate changeset is not missing packages - run: npx tsx scripts/check_changeset_completeness.ts ${{ github.event.pull_request.base.sha }} + run: npx tsx scripts/check_changeset_completeness.ts "$BASE_SHA" + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} - name: Validate that changeset has necessary dependency updates run: | npx changeset version From 17b694daf5b0fdbdb6029c60604fc1ab62fe0724 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 30 Oct 2024 14:26:37 -0700 Subject: [PATCH 070/199] Handle no running instance of task in e2e tests on Windows (#2171) --- .changeset/strong-camels-march.md | 2 ++ .../src/process-controller/execa_process_killer.ts | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .changeset/strong-camels-march.md diff --git a/.changeset/strong-camels-march.md b/.changeset/strong-camels-march.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/strong-camels-march.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/process-controller/execa_process_killer.ts b/packages/integration-tests/src/process-controller/execa_process_killer.ts index 2054f3ee80a..945b3ba38dd 100644 --- a/packages/integration-tests/src/process-controller/execa_process_killer.ts +++ b/packages/integration-tests/src/process-controller/execa_process_killer.ts @@ -21,7 +21,9 @@ export const killExecaProcess = async (processInstance: ExecaChildProcess) => { // if process doesn't exist it means that it managed to exit gracefully by now. // so don't fail in that case. const isProcessNotFoundError = - e instanceof Error && e.message.includes('not found'); + e instanceof Error && + (e.message.includes('not found') || + e.message.includes('There is no running instance of the task')); if (!isProcessNotFoundError) { throw e; } From b56d344e27f48ba235644fb695e6de465028bc65 Mon Sep 17 00:00:00 2001 From: "Peter V." <98245483+p5quared@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:35:07 -0700 Subject: [PATCH 071/199] chore: update schema generation (#2164) --- .changeset/lovely-fishes-cheer.md | 20 +++ .changeset/orange-nails-cross.md | 5 + .changeset/thirty-cheetahs-clap.md | 2 + package-lock.json | 115 ++++++++++++------ packages/ai-constructs/package.json | 2 +- packages/auth-construct/package.json | 2 +- packages/backend-ai/package.json | 2 +- packages/backend-auth/package.json | 2 +- packages/backend-data/package.json | 2 +- packages/backend-deployer/package.json | 2 +- packages/backend-function/package.json | 2 +- packages/backend-output-storage/package.json | 2 +- .../backend-platform-test-stubs/package.json | 2 +- packages/backend-storage/package.json | 2 +- packages/backend/package.json | 2 +- packages/integration-tests/package.json | 2 +- packages/plugin-types/package.json | 2 +- packages/sandbox/package.json | 2 +- packages/schema-generator/package.json | 2 +- templates/construct/package.json | 2 +- 20 files changed, 120 insertions(+), 54 deletions(-) create mode 100644 .changeset/lovely-fishes-cheer.md create mode 100644 .changeset/orange-nails-cross.md create mode 100644 .changeset/thirty-cheetahs-clap.md diff --git a/.changeset/lovely-fishes-cheer.md b/.changeset/lovely-fishes-cheer.md new file mode 100644 index 00000000000..34797dbcdba --- /dev/null +++ b/.changeset/lovely-fishes-cheer.md @@ -0,0 +1,20 @@ +--- +'@aws-amplify/backend-platform-test-stubs': patch +'@aws-amplify/backend-output-storage': patch +'@aws-amplify/integration-tests': patch +'@aws-amplify/backend-deployer': patch +'@aws-amplify/backend-function': patch +'@aws-amplify/schema-generator': patch +'@aws-amplify/backend-storage': patch +'@aws-amplify/auth-construct': patch +'@aws-amplify/ai-constructs': patch +'@aws-amplify/client-config': patch +'@aws-amplify/backend-auth': patch +'@aws-amplify/backend-data': patch +'@aws-amplify/plugin-types': patch +'@aws-amplify/backend-ai': patch +'@aws-amplify/backend': patch +'@aws-amplify/sandbox': patch +--- + +update aws-cdk lib to ^2.158.0 diff --git a/.changeset/orange-nails-cross.md b/.changeset/orange-nails-cross.md new file mode 100644 index 00000000000..abae0c0d0bf --- /dev/null +++ b/.changeset/orange-nails-cross.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/schema-generator': patch +--- + +Upgrade @aws-amplify/graphql-schema-generator to v0.11.0 diff --git a/.changeset/thirty-cheetahs-clap.md b/.changeset/thirty-cheetahs-clap.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/thirty-cheetahs-clap.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/package-lock.json b/package-lock.json index 0156b885e10..5db391f0d2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5070,13 +5070,13 @@ "license": "0BSD" }, "node_modules/@aws-amplify/graphql-schema-generator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@aws-amplify/graphql-schema-generator/-/graphql-schema-generator-0.9.4.tgz", - "integrity": "sha512-GXoPOes5Sj93p7RWunJlMdxPQyoh+dBaJq3qpQUOSYQU1UxUqAstnD+gqAWEG58opiupHby7jTIi1ljK1e9CrQ==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@aws-amplify/graphql-schema-generator/-/graphql-schema-generator-0.11.0.tgz", + "integrity": "sha512-c5pDuoh8UWD0qQ2N4HjR3ZC/JO6ai8DrsK40oQKwQhG2V/VkxUGdqsg0B9nYiKepxiTw0gXabLq8JfwW4o8uBg==", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-transformer-core": "2.9.3", - "@aws-amplify/graphql-transformer-interfaces": "3.10.1", + "@aws-amplify/graphql-transformer-core": "3.2.2", + "@aws-amplify/graphql-transformer-interfaces": "4.1.2", "@aws-sdk/client-ec2": "3.624.0", "@aws-sdk/client-iam": "3.624.0", "@aws-sdk/client-lambda": "3.624.0", @@ -5084,7 +5084,7 @@ "csv-parse": "^5.5.2", "fs-extra": "11.1.1", "graphql": "^15.5.0", - "graphql-transformer-common": "4.31.1", + "graphql-transformer-common": "5.1.0", "knex": "~2.4.0", "mysql2": "~3.9.7", "ora": "^4.0.3", @@ -5712,6 +5712,24 @@ "node": ">=14.14" } }, + "node_modules/@aws-amplify/graphql-schema-generator/node_modules/graphql-mapping-template": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/graphql-mapping-template/-/graphql-mapping-template-5.0.1.tgz", + "integrity": "sha512-hgFkXUS6Q35zE/uyPGIZYof2kutwTZmVqwJfnQofiCYWRRQS0zjzUdyqmOcCBkbJB4Zi7G7mXcl3fSIs5I5vgA==", + "license": "Apache-2.0" + }, + "node_modules/@aws-amplify/graphql-schema-generator/node_modules/graphql-transformer-common": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/graphql-transformer-common/-/graphql-transformer-common-5.1.0.tgz", + "integrity": "sha512-i1Ja0bjlsrSNT5TzjGOrPyxYGJPTutDOLTJENcGC47+KYzMfQS80KpVpUZlIVlcCbDYeSZbv8HaMtJlJpmjbmw==", + "license": "Apache-2.0", + "dependencies": { + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "md5": "^2.2.1", + "pluralize": "8.0.0" + } + }, "node_modules/@aws-amplify/graphql-schema-generator/node_modules/typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", @@ -5726,17 +5744,17 @@ } }, "node_modules/@aws-amplify/graphql-transformer-core": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@aws-amplify/graphql-transformer-core/-/graphql-transformer-core-2.9.3.tgz", - "integrity": "sha512-gz9PbNTqsyQQn6W5d4HPN/pafvFH7spwd6R/hImisEBFD+80liJc/21nBC8UgUMPu2eXVZrsiWBfWnO8Rbqomg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@aws-amplify/graphql-transformer-core/-/graphql-transformer-core-3.2.2.tgz", + "integrity": "sha512-nHocW0Uy/pHrrt5iMFMzz+9IsJKnaPk9BcWZHcQSJ/9F0Kn0s/vIFT5/Ee2nJFN/h0VK3fTkT9QKOuiQ4UH3Jg==", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "1.1.0", - "@aws-amplify/graphql-transformer-interfaces": "3.10.1", + "@aws-amplify/graphql-directives": "2.4.0", + "@aws-amplify/graphql-transformer-interfaces": "4.1.2", "fs-extra": "^8.1.0", "graphql": "^15.5.0", - "graphql-mapping-template": "4.20.16", - "graphql-transformer-common": "4.31.1", + "graphql-mapping-template": "5.0.1", + "graphql-transformer-common": "5.1.0", "hjson": "^3.2.2", "lodash": "^4.17.21", "md5": "^2.3.0", @@ -5744,10 +5762,16 @@ "ts-dedent": "^2.0.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.129.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.3.0" } }, + "node_modules/@aws-amplify/graphql-transformer-core/node_modules/@aws-amplify/graphql-directives": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@aws-amplify/graphql-directives/-/graphql-directives-2.4.0.tgz", + "integrity": "sha512-+oO9Lb22eIuS8rvLOR+x4F79J5aCF1GIkqYS0paRUTw78NjLTOq1LWjtGMYAfLpbHgoYtrkC2zwpw7sHbmNnzQ==", + "license": "Apache-2.0" + }, "node_modules/@aws-amplify/graphql-transformer-core/node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -5762,6 +5786,24 @@ "node": ">=6 <7 || >=8" } }, + "node_modules/@aws-amplify/graphql-transformer-core/node_modules/graphql-mapping-template": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/graphql-mapping-template/-/graphql-mapping-template-5.0.1.tgz", + "integrity": "sha512-hgFkXUS6Q35zE/uyPGIZYof2kutwTZmVqwJfnQofiCYWRRQS0zjzUdyqmOcCBkbJB4Zi7G7mXcl3fSIs5I5vgA==", + "license": "Apache-2.0" + }, + "node_modules/@aws-amplify/graphql-transformer-core/node_modules/graphql-transformer-common": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/graphql-transformer-common/-/graphql-transformer-common-5.1.0.tgz", + "integrity": "sha512-i1Ja0bjlsrSNT5TzjGOrPyxYGJPTutDOLTJENcGC47+KYzMfQS80KpVpUZlIVlcCbDYeSZbv8HaMtJlJpmjbmw==", + "license": "Apache-2.0", + "dependencies": { + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.1", + "md5": "^2.2.1", + "pluralize": "8.0.0" + } + }, "node_modules/@aws-amplify/graphql-transformer-core/node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -5790,15 +5832,15 @@ } }, "node_modules/@aws-amplify/graphql-transformer-interfaces": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/graphql-transformer-interfaces/-/graphql-transformer-interfaces-3.10.1.tgz", - "integrity": "sha512-daf+cpOSw3lKiS+Tpc5Oo5H+FCkHi/8z+0mAR/greQGPJWzcHv9j2u1Jiy36UvI01ypOhHme58pAs/fKWLWDBQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@aws-amplify/graphql-transformer-interfaces/-/graphql-transformer-interfaces-4.1.2.tgz", + "integrity": "sha512-fW4BIo2stFYOc6LDrSDKW0NTKmBp/c+UJUG5YjDef5fUUTbE8RZMzUGgSjgzDgwXpAT8CyYuncqMLchVkQSFFQ==", "license": "Apache-2.0", "dependencies": { "graphql": "^15.5.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.129.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.3.0" } }, @@ -6078,9 +6120,9 @@ "license": "Apache-2.0" }, "node_modules/@aws-cdk/cloud-assembly-schema": { - "version": "36.0.25", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-36.0.25.tgz", - "integrity": "sha512-AK86v4IMV4zcWfp392e3wlaVJPT72/dk39Lo2SDDFxQR+sikMOyY2IGrULyhK1TwQmPiyxM7QB/0MkTbMDAPrw==", + "version": "38.0.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-38.0.1.tgz", + "integrity": "sha512-KvPe+NMWAulfNVwY7jenFhzhuLhLqJ/OPy5jx7wUstbjnYnjRVLpUHPU3yCjXFE0J8cuJVdx95BJ4rOs66Pi9w==", "bundleDependencies": [ "jsonschema", "semver" @@ -6089,9 +6131,6 @@ "dependencies": { "jsonschema": "^1.4.1", "semver": "^7.6.3" - }, - "engines": { - "node": ">= 18.18.0" } }, "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": { @@ -19460,9 +19499,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.158.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.158.0.tgz", - "integrity": "sha512-Pl9CCLM+XRTy6nyyRJM1INEMtwIlZOib0FWyq9i9E388vurw7sNVJ6tAsfLpGIOLHsFQCbF4f6OZ0KSVxmMaiA==", + "version": "2.164.1", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.164.1.tgz", + "integrity": "sha512-jNvVmfZJbZoAYU94b5dzTlF2z6JXJ204NgcYY5haOa6mq3m2bzdYPXnPtB5kpAX3oBi++yoRdmLhqgckdEhUZA==", "bundleDependencies": [ "@balena/dockerignore", "case", @@ -19481,7 +19520,7 @@ "@aws-cdk/asset-awscli-v1": "^2.2.202", "@aws-cdk/asset-kubectl-v20": "^2.1.2", "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", - "@aws-cdk/cloud-assembly-schema": "^36.0.24", + "@aws-cdk/cloud-assembly-schema": "^38.0.0", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", "fs-extra": "^11.2.0", @@ -31455,7 +31494,7 @@ }, "packages/ai-constructs": { "name": "@aws-amplify/ai-constructs", - "version": "0.6.2", + "version": "0.7.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", @@ -31495,12 +31534,12 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.5.1", + "version": "1.5.2", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-auth": "^1.2.0", "@aws-amplify/backend-data": "^1.1.5", - "@aws-amplify/backend-function": "^1.7.1", + "@aws-amplify/backend-function": "^1.7.2", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.2", "@aws-amplify/backend-secret": "^1.1.4", @@ -31524,10 +31563,10 @@ }, "packages/backend-ai": { "name": "@aws-amplify/backend-ai", - "version": "0.3.3", + "version": "0.3.4", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.6.2", + "@aws-amplify/ai-constructs": "^0.7.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.0.2", "@aws-amplify/data-schema-types": "^1.2.0", @@ -31595,7 +31634,7 @@ }, "packages/backend-function": { "name": "@aws-amplify/backend-function", - "version": "1.7.1", + "version": "1.7.2", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", @@ -32039,10 +32078,10 @@ "license": "Apache-2.0", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.6.0", + "@aws-amplify/ai-constructs": "^0.7.0", "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend": "^1.5.0", - "@aws-amplify/backend-ai": "^0.3.2", + "@aws-amplify/backend": "^1.5.2", + "@aws-amplify/backend-ai": "^0.3.4", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/client-config": "^1.4.0", "@aws-amplify/data-schema": "^1.0.0", @@ -32313,7 +32352,7 @@ "version": "1.2.4", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-schema-generator": "^0.9.4", + "@aws-amplify/graphql-schema-generator": "^0.11.0", "@aws-amplify/platform-core": "^1.0.5" } } diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index f329ef6e038..3f33f4089a4 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -38,7 +38,7 @@ "typescript": "^5.0.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } } diff --git a/packages/auth-construct/package.json b/packages/auth-construct/package.json index 292804c060f..d3e121ee4d3 100644 --- a/packages/auth-construct/package.json +++ b/packages/auth-construct/package.json @@ -25,7 +25,7 @@ "@aws-sdk/util-arn-parser": "^3.568.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } } diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 12196fac932..1a254af2974 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -30,7 +30,7 @@ "@aws-amplify/plugin-types": "^1.0.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } } diff --git a/packages/backend-auth/package.json b/packages/backend-auth/package.json index c9bddfe7c90..89d71470380 100644 --- a/packages/backend-auth/package.json +++ b/packages/backend-auth/package.json @@ -28,7 +28,7 @@ "@aws-amplify/platform-core": "^1.0.6" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } } diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index 834a7bcd956..b9c14d8db9b 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -24,7 +24,7 @@ "@aws-amplify/platform-core": "^1.0.7" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" }, "dependencies": { diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index d78a6b7d7ae..07573125589 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -25,7 +25,7 @@ "tsx": "^4.6.1" }, "peerDependencies": { - "aws-cdk": "^2.152.0", + "aws-cdk": "^2.158.0", "typescript": "^5.0.0" } } diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index 1286d277d34..eee31585122 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -32,7 +32,7 @@ "uuid": "^9.0.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } } diff --git a/packages/backend-output-storage/package.json b/packages/backend-output-storage/package.json index 228a0f85b11..f593321058f 100644 --- a/packages/backend-output-storage/package.json +++ b/packages/backend-output-storage/package.json @@ -24,6 +24,6 @@ "@aws-amplify/plugin-types": "^1.2.2" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0" + "aws-cdk-lib": "^2.158.0" } } diff --git a/packages/backend-platform-test-stubs/package.json b/packages/backend-platform-test-stubs/package.json index 953c053355f..a395b969db2 100644 --- a/packages/backend-platform-test-stubs/package.json +++ b/packages/backend-platform-test-stubs/package.json @@ -17,7 +17,7 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/plugin-types": "^1.2.2", - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } } diff --git a/packages/backend-storage/package.json b/packages/backend-storage/package.json index ac19d604725..334997595b6 100644 --- a/packages/backend-storage/package.json +++ b/packages/backend-storage/package.json @@ -28,7 +28,7 @@ "@aws-amplify/platform-core": "^1.0.6" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } } diff --git a/packages/backend/package.json b/packages/backend/package.json index 4d05c7b7a71..c3bd1a6d13b 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -40,7 +40,7 @@ "lodash.snakecase": "^4.1.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" }, "devDependencies": { diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index b49ec966f44..aaabea803ea 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -33,7 +33,7 @@ "@zip.js/zip.js": "^2.7.52", "aws-amplify": "^6.0.16", "aws-appsync-auth-link": "^3.0.7", - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0", "execa": "^8.0.1", "fs-extra": "^11.1.1", diff --git a/packages/plugin-types/package.json b/packages/plugin-types/package.json index 70711832d6a..8474a4b93fb 100644 --- a/packages/plugin-types/package.json +++ b/packages/plugin-types/package.json @@ -11,7 +11,7 @@ }, "license": "Apache-2.0", "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0", "@aws-sdk/types": "^3.609.0" }, diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index b5c04aa0c8c..f334a6f66c4 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -42,6 +42,6 @@ "@types/parse-gitignore": "^1.0.0" }, "peerDependencies": { - "aws-cdk": "^2.152.0" + "aws-cdk": "^2.158.0" } } diff --git a/packages/schema-generator/package.json b/packages/schema-generator/package.json index 7ee91355ad0..a6b924d9a83 100644 --- a/packages/schema-generator/package.json +++ b/packages/schema-generator/package.json @@ -18,7 +18,7 @@ "update:api": "api-extractor run --local" }, "dependencies": { - "@aws-amplify/graphql-schema-generator": "^0.9.4", + "@aws-amplify/graphql-schema-generator": "^0.11.0", "@aws-amplify/platform-core": "^1.0.5" }, "license": "Apache-2.0" diff --git a/templates/construct/package.json b/templates/construct/package.json index 2a879e37685..3494b52996f 100644 --- a/templates/construct/package.json +++ b/templates/construct/package.json @@ -18,7 +18,7 @@ }, "license": "Apache-2.0", "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } } From ce44b1a4066f88c8fefd9178f04e4d6e37a8eeae Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 30 Oct 2024 15:22:35 -0700 Subject: [PATCH 072/199] Upgrade CDK CLI to match CDK lib (#2172) --- package-lock.json | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5db391f0d2e..866fae1fc69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19483,9 +19483,9 @@ "license": "0BSD" }, "node_modules/aws-cdk": { - "version": "2.158.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.158.0.tgz", - "integrity": "sha512-UcrxBG02RACrnTvfuyZiTuOz8gqOpnqjCMTdVmdpExv5qk9hddhtRAubNaC4xleHuNJnvskYqqVW+Y3Abh6zGQ==", + "version": "2.164.1", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.164.1.tgz", + "integrity": "sha512-dWRViQgHLe7GHkPIQGA+8EQSm8TBcxemyCC3HHW3wbLMWUDbspio9Dktmw5EmWxlFjjWh86Dk1JWf1zKQo8C5g==", "license": "Apache-2.0", "peer": true, "bin": { @@ -31509,7 +31509,7 @@ "typescript": "^5.0.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } }, @@ -31528,7 +31528,7 @@ "@aws-sdk/util-arn-parser": "^3.568.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } }, @@ -31557,7 +31557,7 @@ "aws-lambda": "^1.0.7" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } }, @@ -31574,7 +31574,7 @@ "@aws-amplify/plugin-types": "^1.0.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } }, @@ -31592,7 +31592,7 @@ "@aws-amplify/platform-core": "^1.0.6" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } }, @@ -31613,7 +31613,7 @@ "@aws-amplify/platform-core": "^1.0.7" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } }, @@ -31628,7 +31628,7 @@ "tsx": "^4.6.1" }, "peerDependencies": { - "aws-cdk": "^2.152.0", + "aws-cdk": "^2.158.0", "typescript": "^5.0.0" } }, @@ -31650,7 +31650,7 @@ "uuid": "^9.0.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } }, @@ -31689,7 +31689,7 @@ "@aws-amplify/plugin-types": "^1.2.2" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0" + "aws-cdk-lib": "^2.158.0" } }, "packages/backend-platform-test-stubs": { @@ -31698,7 +31698,7 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/plugin-types": "^1.2.2", - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } }, @@ -31729,7 +31729,7 @@ "@aws-amplify/platform-core": "^1.0.6" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } }, @@ -32106,7 +32106,7 @@ "@zip.js/zip.js": "^2.7.52", "aws-amplify": "^6.0.16", "aws-appsync-auth-link": "^3.0.7", - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0", "execa": "^8.0.1", "fs-extra": "^11.1.1", @@ -32196,7 +32196,7 @@ }, "peerDependencies": { "@aws-sdk/types": "^3.609.0", - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } }, @@ -32344,7 +32344,7 @@ "@types/parse-gitignore": "^1.0.0" }, "peerDependencies": { - "aws-cdk": "^2.152.0" + "aws-cdk": "^2.158.0" } }, "packages/schema-generator": { From 37dd87c82123ac99bc1933622eaf6cf928d99404 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 31 Oct 2024 10:00:09 -0700 Subject: [PATCH 073/199] Propagate errors to AppSync (#2162) * Propagate errors to AppSync * this works * more tests * validation error * more tests --- .changeset/fresh-candles-leave.md | 5 + .../runtime/bedrock_converse_adapter.ts | 3 +- .../conversation_turn_executor.test.ts | 218 +++++++++++++++++- .../runtime/conversation_turn_executor.ts | 37 ++- .../conversation_turn_response_sender.test.ts | 62 ++++- .../conversation_turn_response_sender.ts | 44 +++- .../src/conversation/runtime/errors.ts | 12 + .../src/conversation/runtime/lazy.ts | 17 ++ .../src/conversation/runtime/types.ts | 5 + .../conversation_handler_project.ts | 142 ++++++++++-- .../amplify/data/resource.ts | 11 +- 11 files changed, 519 insertions(+), 37 deletions(-) create mode 100644 .changeset/fresh-candles-leave.md create mode 100644 packages/ai-constructs/src/conversation/runtime/errors.ts create mode 100644 packages/ai-constructs/src/conversation/runtime/lazy.ts diff --git a/.changeset/fresh-candles-leave.md b/.changeset/fresh-candles-leave.md new file mode 100644 index 00000000000..67c5f741d60 --- /dev/null +++ b/.changeset/fresh-candles-leave.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': minor +--- + +Propagate errors to AppSync diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts index 510925492a9..c215efc0b48 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts @@ -21,6 +21,7 @@ import { import { ConversationTurnEventToolsProvider } from './event-tools-provider'; import { ConversationMessageHistoryRetriever } from './conversation_message_history_retriever'; import * as bedrock from '@aws-sdk/client-bedrock-runtime'; +import { ValidationError } from './errors'; /** * This class is responsible for interacting with Bedrock Converse API @@ -87,7 +88,7 @@ export class BedrockConverseAdapter { this.clientToolByName.set(t.name, t); }); if (duplicateTools.size > 0) { - throw new Error( + throw new ValidationError( `Tools must have unique names. Duplicate tools: ${[ ...duplicateTools, ].join(', ')}.` diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts index be96f22fa38..8c42431b632 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.test.ts @@ -5,6 +5,7 @@ import { ConversationTurnEvent, StreamingResponseChunk } from './types'; import { BedrockConverseAdapter } from './bedrock_converse_adapter'; import { ContentBlock } from '@aws-sdk/client-bedrock-runtime'; import { ConversationTurnResponseSender } from './conversation_turn_response_sender'; +import { Lazy } from './lazy'; void describe('Conversation turn executor', () => { const event: ConversationTurnEvent = { @@ -62,8 +63,8 @@ void describe('Conversation turn executor', () => { await new ConversationTurnExecutor( event, [], - bedrockConverseAdapter, - responseSender, + new Lazy(() => responseSender), + new Lazy(() => bedrockConverseAdapter), consoleMock ).execute(); @@ -156,8 +157,8 @@ void describe('Conversation turn executor', () => { await new ConversationTurnExecutor( streamingEvent, [], - bedrockConverseAdapter, - responseSender, + new Lazy(() => responseSender), + new Lazy(() => bedrockConverseAdapter), consoleMock ).execute(); @@ -214,13 +215,21 @@ void describe('Conversation turn executor', () => { () => Promise.resolve() ); + const responseSenderSendErrorsMock = mock.method( + responseSender, + 'sendErrors', + () => Promise.resolve() + ); + const consoleErrorMock = mock.fn(); const consoleLogMock = mock.fn(); const consoleDebugMock = mock.fn(); + const consoleWarnMock = mock.fn(); const consoleMock = { error: consoleErrorMock, log: consoleLogMock, debug: consoleDebugMock, + warn: consoleWarnMock, } as unknown as Console; await assert.rejects( @@ -228,8 +237,8 @@ void describe('Conversation turn executor', () => { new ConversationTurnExecutor( event, [], - bedrockConverseAdapter, - responseSender, + new Lazy(() => responseSender), + new Lazy(() => bedrockConverseAdapter), consoleMock ).execute(), (error: Error) => { @@ -263,6 +272,16 @@ void describe('Conversation turn executor', () => { consoleErrorMock.mock.calls[0].arguments[1], bedrockError ); + assert.strictEqual(responseSenderSendErrorsMock.mock.calls.length, 1); + assert.deepStrictEqual( + responseSenderSendErrorsMock.mock.calls[0].arguments[0], + [ + { + errorType: 'Error', + message: 'Bedrock failed', + }, + ] + ); }); void it('logs and propagates error if response sender throws', async () => { @@ -290,13 +309,21 @@ void describe('Conversation turn executor', () => { () => Promise.resolve() ); + const responseSenderSendErrorsMock = mock.method( + responseSender, + 'sendErrors', + () => Promise.resolve() + ); + const consoleErrorMock = mock.fn(); const consoleLogMock = mock.fn(); const consoleDebugMock = mock.fn(); + const consoleWarnMock = mock.fn(); const consoleMock = { error: consoleErrorMock, log: consoleLogMock, debug: consoleDebugMock, + warn: consoleWarnMock, } as unknown as Console; await assert.rejects( @@ -304,8 +331,8 @@ void describe('Conversation turn executor', () => { new ConversationTurnExecutor( event, [], - bedrockConverseAdapter, - responseSender, + new Lazy(() => responseSender), + new Lazy(() => bedrockConverseAdapter), consoleMock ).execute(), (error: Error) => { @@ -339,5 +366,180 @@ void describe('Conversation turn executor', () => { consoleErrorMock.mock.calls[0].arguments[1], responseSenderError ); + assert.strictEqual(responseSenderSendErrorsMock.mock.calls.length, 1); + assert.deepStrictEqual( + responseSenderSendErrorsMock.mock.calls[0].arguments[0], + [ + { + errorType: 'Error', + message: 'Failed to send response', + }, + ] + ); + }); + + void it('throws original exception if error sender fails', async () => { + const bedrockConverseAdapter = new BedrockConverseAdapter(event, []); + const originalError = new Error('original error'); + mock.method(bedrockConverseAdapter, 'askBedrock', () => + Promise.reject(originalError) + ); + const responseSender = new ConversationTurnResponseSender(event); + mock.method(responseSender, 'sendResponse', () => Promise.resolve()); + + mock.method(responseSender, 'sendResponseChunk', () => Promise.resolve()); + + const responseSenderSendErrorsMock = mock.method( + responseSender, + 'sendErrors', + () => Promise.reject(new Error('sender error')) + ); + + const consoleErrorMock = mock.fn(); + const consoleLogMock = mock.fn(); + const consoleDebugMock = mock.fn(); + const consoleWarnMock = mock.fn(); + const consoleMock = { + error: consoleErrorMock, + log: consoleLogMock, + debug: consoleDebugMock, + warn: consoleWarnMock, + } as unknown as Console; + + await assert.rejects( + () => + new ConversationTurnExecutor( + event, + [], + new Lazy(() => responseSender), + new Lazy(() => bedrockConverseAdapter), + consoleMock + ).execute(), + (error: Error) => { + assert.strictEqual(error, originalError); + return true; + } + ); + + assert.strictEqual(responseSenderSendErrorsMock.mock.calls.length, 1); + assert.deepStrictEqual( + responseSenderSendErrorsMock.mock.calls[0].arguments[0], + [ + { + errorType: 'Error', + message: 'original error', + }, + ] + ); + }); + + void it('serializes unknown errors', async () => { + const bedrockConverseAdapter = new BedrockConverseAdapter(event, []); + const unknownError = { some: 'shape' }; + mock.method(bedrockConverseAdapter, 'askBedrock', () => + Promise.reject(unknownError) + ); + const responseSender = new ConversationTurnResponseSender(event); + mock.method(responseSender, 'sendResponse', () => Promise.resolve()); + + mock.method(responseSender, 'sendResponseChunk', () => Promise.resolve()); + + const responseSenderSendErrorsMock = mock.method( + responseSender, + 'sendErrors', + () => Promise.resolve() + ); + + const consoleErrorMock = mock.fn(); + const consoleLogMock = mock.fn(); + const consoleDebugMock = mock.fn(); + const consoleWarnMock = mock.fn(); + const consoleMock = { + error: consoleErrorMock, + log: consoleLogMock, + debug: consoleDebugMock, + warn: consoleWarnMock, + } as unknown as Console; + + await assert.rejects( + () => + new ConversationTurnExecutor( + event, + [], + new Lazy(() => responseSender), + new Lazy(() => bedrockConverseAdapter), + consoleMock + ).execute(), + (error: Error) => { + assert.strictEqual(error, unknownError); + return true; + } + ); + + assert.strictEqual(responseSenderSendErrorsMock.mock.calls.length, 1); + assert.deepStrictEqual( + responseSenderSendErrorsMock.mock.calls[0].arguments[0], + [ + { + errorType: 'UnknownError', + message: '{"some":"shape"}', + }, + ] + ); + }); + + void it('reports initialization errors', async () => { + const bedrockConverseAdapter = new BedrockConverseAdapter(event, []); + mock.method(bedrockConverseAdapter, 'askBedrock', () => Promise.resolve()); + const responseSender = new ConversationTurnResponseSender(event); + mock.method(responseSender, 'sendResponse', () => Promise.resolve()); + + mock.method(responseSender, 'sendResponseChunk', () => Promise.resolve()); + + const responseSenderSendErrorsMock = mock.method( + responseSender, + 'sendErrors', + () => Promise.resolve() + ); + + const consoleErrorMock = mock.fn(); + const consoleLogMock = mock.fn(); + const consoleDebugMock = mock.fn(); + const consoleWarnMock = mock.fn(); + const consoleMock = { + error: consoleErrorMock, + log: consoleLogMock, + debug: consoleDebugMock, + warn: consoleWarnMock, + } as unknown as Console; + + const initializationError = new Error('initialization error'); + await assert.rejects( + () => + new ConversationTurnExecutor( + event, + [], + new Lazy(() => responseSender), + new Lazy(() => { + throw initializationError; + }), + consoleMock + ).execute(), + (error: Error) => { + assert.strictEqual(error, initializationError); + return true; + } + ); + + assert.strictEqual(responseSenderSendErrorsMock.mock.calls.length, 1); + assert.deepStrictEqual( + responseSenderSendErrorsMock.mock.calls[0].arguments[0], + [ + { + errorType: 'Error', + message: 'initialization error', + }, + ] + ); }); }); diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts index 04a73842079..9c5389f6109 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_executor.ts @@ -1,6 +1,7 @@ import { ConversationTurnResponseSender } from './conversation_turn_response_sender.js'; import { ConversationTurnEvent, ExecutableTool, JSONSchema } from './types.js'; import { BedrockConverseAdapter } from './bedrock_converse_adapter.js'; +import { Lazy } from './lazy'; /** * This class is responsible for orchestrating conversation turn execution. @@ -16,11 +17,13 @@ export class ConversationTurnExecutor { constructor( private readonly event: ConversationTurnEvent, additionalTools: Array, - private readonly bedrockConverseAdapter = new BedrockConverseAdapter( - event, - additionalTools + // We're deferring dependency initialization here so that we can capture all validation errors. + private readonly responseSender = new Lazy( + () => new ConversationTurnResponseSender(event) + ), + private readonly bedrockConverseAdapter = new Lazy( + () => new BedrockConverseAdapter(event, additionalTools) ), - private readonly responseSender = new ConversationTurnResponseSender(event), private readonly logger = console ) {} @@ -32,14 +35,14 @@ export class ConversationTurnExecutor { this.logger.debug('Event received:', this.event); if (this.event.streamResponse) { - const chunks = this.bedrockConverseAdapter.askBedrockStreaming(); + const chunks = this.bedrockConverseAdapter.value.askBedrockStreaming(); for await (const chunk of chunks) { - await this.responseSender.sendResponseChunk(chunk); + await this.responseSender.value.sendResponseChunk(chunk); } } else { const assistantResponse = - await this.bedrockConverseAdapter.askBedrock(); - await this.responseSender.sendResponse(assistantResponse); + await this.bedrockConverseAdapter.value.askBedrock(); + await this.responseSender.value.sendResponse(assistantResponse); } this.logger.log( @@ -50,10 +53,28 @@ export class ConversationTurnExecutor { `Failed to handle conversation turn event, currentMessageId=${this.event.currentMessageId}, conversationId=${this.event.conversationId}`, e ); + await this.tryForwardError(e); // Propagate error to mark lambda execution as failed in metrics. throw e; } }; + + private tryForwardError = async (e: unknown) => { + try { + let errorType = 'UnknownError'; + let message: string; + if (e instanceof Error) { + errorType = e.name; + message = e.message; + } else { + message = JSON.stringify(e); + } + await this.responseSender.value.sendErrors([{ errorType, message }]); + } catch (e) { + // Best effort, only log the fact that we tried to send error back to AppSync. + this.logger.warn('Failed to send error mutation', e); + } + }; } /** diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts index b38ccbdff90..0e7e1fe71a4 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts @@ -5,7 +5,11 @@ import { MutationResponseInput, MutationStreamingResponseInput, } from './conversation_turn_response_sender'; -import { ConversationTurnEvent, StreamingResponseChunk } from './types'; +import { + ConversationTurnError, + ConversationTurnEvent, + StreamingResponseChunk, +} from './types'; import { ContentBlock } from '@aws-sdk/client-bedrock-runtime'; import { GraphqlRequest, @@ -236,4 +240,60 @@ void describe('Conversation turn response sender', () => { }, }); }); + + void it('sends errors response back to appsync', async () => { + const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); + const executeGraphqlMock = mock.method( + graphqlRequestExecutor, + 'executeGraphql', + () => + // Mock successful Appsync response + Promise.resolve() + ); + const sender = new ConversationTurnResponseSender( + event, + graphqlRequestExecutor + ); + const errors: Array = [ + { + errorType: 'errorType1', + message: 'errorMessage1', + }, + { + errorType: 'errorType2', + message: 'errorMessage2', + }, + ]; + await sender.sendErrors(errors); + + assert.strictEqual(executeGraphqlMock.mock.calls.length, 1); + const request = executeGraphqlMock.mock.calls[0] + .arguments[0] as GraphqlRequest; + assert.deepStrictEqual(request, { + query: + '\n' + + ' mutation PublishModelResponse($input: testResponseMutationInputTypeName!) {\n' + + ' testResponseMutationName(input: $input) {\n' + + ' testSelectionSet\n' + + ' }\n' + + ' }\n' + + ' ', + variables: { + input: { + conversationId: event.conversationId, + errors: [ + { + errorType: 'errorType1', + message: 'errorMessage1', + }, + { + errorType: 'errorType2', + message: 'errorMessage2', + }, + ], + associatedUserMessageId: event.currentMessageId, + }, + }, + }); + }); }); diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts index 7590f5edcfe..723b7879599 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts @@ -1,4 +1,8 @@ -import { ConversationTurnEvent, StreamingResponseChunk } from './types.js'; +import { + ConversationTurnError, + ConversationTurnEvent, + StreamingResponseChunk, +} from './types.js'; import type { ContentBlock } from '@aws-sdk/client-bedrock-runtime'; import { GraphqlRequestExecutor } from './graphql_request_executor'; @@ -14,6 +18,14 @@ export type MutationStreamingResponseInput = { input: StreamingResponseChunk; }; +export type MutationErrorsResponseInput = { + input: { + conversationId: string; + errors: ConversationTurnError[]; + associatedUserMessageId: string; + }; +}; + /** * This class is responsible for sending a response produced by Bedrock back to AppSync * in a form of mutation. @@ -50,6 +62,36 @@ export class ConversationTurnResponseSender { >(responseMutationRequest); }; + sendErrors = async (errors: ConversationTurnError[]) => { + const responseMutationRequest = this.createMutationErrorsRequest(errors); + this.logger.debug( + 'Sending errors response mutation:', + responseMutationRequest + ); + await this.graphqlRequestExecutor.executeGraphql< + MutationErrorsResponseInput, + void + >(responseMutationRequest); + }; + + private createMutationErrorsRequest = (errors: ConversationTurnError[]) => { + const query = ` + mutation PublishModelResponse($input: ${this.event.responseMutation.inputTypeName}!) { + ${this.event.responseMutation.name}(input: $input) { + ${this.event.responseMutation.selectionSet} + } + } + `; + const variables: MutationErrorsResponseInput = { + input: { + conversationId: this.event.conversationId, + errors, + associatedUserMessageId: this.event.currentMessageId, + }, + }; + return { query, variables }; + }; + private createMutationRequest = (content: ContentBlock[]) => { const query = ` mutation PublishModelResponse($input: ${this.event.responseMutation.inputTypeName}!) { diff --git a/packages/ai-constructs/src/conversation/runtime/errors.ts b/packages/ai-constructs/src/conversation/runtime/errors.ts new file mode 100644 index 00000000000..1d3063dd49b --- /dev/null +++ b/packages/ai-constructs/src/conversation/runtime/errors.ts @@ -0,0 +1,12 @@ +/** + * Represents validation errors. + */ +export class ValidationError extends Error { + /** + * Creates validation error instance. + */ + constructor(message: string) { + super(message); + this.name = 'ValidationError'; + } +} diff --git a/packages/ai-constructs/src/conversation/runtime/lazy.ts b/packages/ai-constructs/src/conversation/runtime/lazy.ts new file mode 100644 index 00000000000..7f5b2032ca1 --- /dev/null +++ b/packages/ai-constructs/src/conversation/runtime/lazy.ts @@ -0,0 +1,17 @@ +/** + * A class that initializes lazily upon usage. + */ +export class Lazy { + #value?: T; + + /** + * Creates lazy instance. + */ + constructor(private readonly valueFactory: () => T) {} + /** + * Gets a value. Value is create at first access. + */ + public get value(): T { + return (this.#value ??= this.valueFactory()); + } +} diff --git a/packages/ai-constructs/src/conversation/runtime/types.ts b/packages/ai-constructs/src/conversation/runtime/types.ts index 330a4ae4c1a..3d95030cabb 100644 --- a/packages/ai-constructs/src/conversation/runtime/types.ts +++ b/packages/ai-constructs/src/conversation/runtime/types.ts @@ -95,6 +95,11 @@ export type ExecutableTool< execute: (input: TToolInput) => Promise; }; +export type ConversationTurnError = { + errorType: string; + message: string; +}; + export type StreamingResponseChunk = { // always required conversationId: string; diff --git a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts index e309ffc71f1..53a9ea44a93 100644 --- a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts +++ b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts @@ -48,6 +48,7 @@ if (process.versions.node) { type ConversationTurnAppSyncResponse = { associatedUserMessageId: string; content: string; + errors?: Array; }; type ConversationMessage = { @@ -78,6 +79,11 @@ type CreateConversationMessageChatInput = ConversationMessage & { associatedUserMessageId?: string; }; +type ConversationTurnError = { + errorType: string; + message: string; +}; + type ConversationTurnAppSyncResponseChunk = { conversationId: string; associatedUserMessageId: string; @@ -87,6 +93,7 @@ type ConversationTurnAppSyncResponseChunk = { contentBlockDoneAtIndex?: number; contentBlockToolUse?: string; stopReason?: string; + errors?: Array; }; /** @@ -330,6 +337,26 @@ class ConversationHandlerTestProject extends TestProjectBase { true ) ); + + await this.executeWithRetry(() => + this.assertDefaultConversationHandlerCanPropagateError( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + true + ) + ); + + await this.executeWithRetry(() => + this.assertDefaultConversationHandlerCanPropagateError( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + false + ) + ); } private assertDefaultConversationHandlerCanExecuteTurn = async ( @@ -378,12 +405,12 @@ class ConversationHandlerTestProject extends TestProjectBase { } await this.insertMessage(apolloClient, message); - const responseContent = await this.executeConversationTurn( + const response = await this.executeConversationTurn( event, defaultConversationHandlerFunction, apolloClient ); - assert.match(responseContent, /3\.14/); + assert.match(response.content, /3\.14/); }; private assertDefaultConversationHandlerCanExecuteTurnWithImage = async ( @@ -443,14 +470,14 @@ class ConversationHandlerTestProject extends TestProjectBase { ...this.getCommonEventProperties(streamResponse), }; await this.insertMessage(apolloClient, message); - const responseContent = await this.executeConversationTurn( + const response = await this.executeConversationTurn( event, defaultConversationHandlerFunction, apolloClient ); // The image contains a logo of AWS. Responses may vary, but they should always contain statements below. - assert.match(responseContent, /logo/); - assert.match(responseContent, /(aws)|(AWS)|(Amazon Web Services)/); + assert.match(response.content, /logo/); + assert.match(response.content, /(aws)|(AWS)|(Amazon Web Services)/); }; private assertDefaultConversationHandlerCanExecuteTurnWithDataTool = async ( @@ -517,14 +544,14 @@ class ConversationHandlerTestProject extends TestProjectBase { }, ...this.getCommonEventProperties(streamResponse), }; - const responseContent = await this.executeConversationTurn( + const response = await this.executeConversationTurn( event, defaultConversationHandlerFunction, apolloClient ); // Assert that tool was used. I.e. that LLM used value returned by the tool. assert.match( - responseContent, + response.content, new RegExp(expectedTemperatureInDataToolScenario.toString()) ); }; @@ -586,7 +613,7 @@ class ConversationHandlerTestProject extends TestProjectBase { }, ...this.getCommonEventProperties(streamResponse), }; - const responseContent = await this.executeConversationTurn( + const response = await this.executeConversationTurn( event, defaultConversationHandlerFunction, apolloClient @@ -594,17 +621,20 @@ class ConversationHandlerTestProject extends TestProjectBase { // Assert that tool use content blocks are emitted in case LLM selects client tool. // The content blocks are string serialized, but not as a proper JSON, // hence string matching is employed below to detect some signals that tool use blocks kinds were emitted. - assert.match(responseContent, /toolUse/); - assert.match(responseContent, /toolUseId/); + assert.match(response.content, /toolUse/); + assert.match(response.content, /toolUseId/); // Assert that LLM attempts to pass parameter when asking for tool use. - assert.match(responseContent, /"city":"Seattle"/); + assert.match(response.content, /"city":"Seattle"/); }; private executeConversationTurn = async ( event: ConversationTurnEvent, functionName: string, apolloClient: ApolloClient - ): Promise => { + ): Promise<{ + content: string; + errors?: Array; + }> => { console.log( `Sending event conversationId=${event.conversationId} currentMessageId=${event.currentMessageId}` ); @@ -649,6 +679,10 @@ class ConversationHandlerTestProject extends TestProjectBase { contentBlockToolUse conversationId createdAt + errors { + errorType + message + } id owner stopReason @@ -676,6 +710,13 @@ class ConversationHandlerTestProject extends TestProjectBase { assert.ok(chunks); + if (chunks.length === 1 && chunks[0].errors) { + return { + content: '', + errors: chunks[0].errors, + }; + } + chunks.sort((a, b) => { // This is very simplified sort by message,block and delta indexes; let aValue = 1000 * 1000 * a.contentBlockIndex; @@ -699,7 +740,7 @@ class ConversationHandlerTestProject extends TestProjectBase { return accumulated; }, ''); - return content; + return { content }; } const queryResult = await apolloClient.query<{ listConversationMessageAssistantResponses: { @@ -721,6 +762,10 @@ class ConversationHandlerTestProject extends TestProjectBase { updatedAt createdAt content + errors { + errorType + message + } associatedUserMessageId } nextToken @@ -739,8 +784,16 @@ class ConversationHandlerTestProject extends TestProjectBase { ); const response = queryResult.data.listConversationMessageAssistantResponses.items[0]; + + if (response.errors) { + return { + content: '', + errors: response.errors, + }; + } + assert.ok(response.content); - return response.content; + return { content: response.content }; }; private assertCustomConversationHandlerCanExecuteTurn = async ( @@ -780,26 +833,81 @@ class ConversationHandlerTestProject extends TestProjectBase { }, ...this.getCommonEventProperties(streamResponse), }; - const responseContent = await this.executeConversationTurn( + const response = await this.executeConversationTurn( event, customConversationHandlerFunction, apolloClient ); // Assert that tool was used. I.e. LLM used value provided by the tool. assert.match( - responseContent, + response.content, new RegExp( expectedTemperaturesInProgrammaticToolScenario.Seattle.toString() ) ); assert.match( - responseContent, + response.content, new RegExp( expectedTemperaturesInProgrammaticToolScenario.Boston.toString() ) ); }; + private assertDefaultConversationHandlerCanPropagateError = async ( + backendId: BackendIdentifier, + accessToken: string, + graphqlApiEndpoint: string, + apolloClient: ApolloClient, + streamResponse: boolean + ): Promise => { + const defaultConversationHandlerFunction = ( + await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::Lambda::Function', + (name) => name.includes('default') + ) + )[0]; + + const message: CreateConversationMessageChatInput = { + id: randomUUID().toString(), + conversationId: randomUUID().toString(), + role: 'user', + content: [ + { + text: 'What is the value of PI?', + }, + ], + }; + + // send event + const event: ConversationTurnEvent = { + conversationId: message.conversationId, + currentMessageId: message.id, + graphqlApiEndpoint: graphqlApiEndpoint, + request: { + headers: { authorization: accessToken }, + }, + ...this.getCommonEventProperties(streamResponse), + }; + + // Inject failure + event.modelConfiguration.modelId = 'invalidId'; + await this.insertMessage(apolloClient, message); + + const response = await this.executeConversationTurn( + event, + defaultConversationHandlerFunction, + apolloClient + ); + assert.ok(response.errors); + assert.ok(response.errors[0]); + assert.strictEqual(response.errors[0].errorType, 'ValidationException'); + assert.match( + response.errors[0].message, + /provided model identifier is invalid/ + ); + }; + private insertMessage = async ( apolloClient: ApolloClient, message: CreateConversationMessageChatInput diff --git a/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts b/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts index 0b8f9dc75fe..07c19400c2d 100644 --- a/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts +++ b/packages/integration-tests/src/test-projects/conversation-handler/amplify/data/resource.ts @@ -86,11 +86,17 @@ const schema = a.schema({ tools: a.ref('MockTool').array(), }), + MockConversationTurnError: a.customType({ + errorType: a.string(), + message: a.string(), + }), + ConversationMessageAssistantResponse: a .model({ conversationId: a.id(), associatedUserMessageId: a.id(), content: a.string(), + errors: a.ref('MockConversationTurnError').array(), }) .authorization((allow) => [allow.authenticated(), allow.owner()]), @@ -99,7 +105,7 @@ const schema = a.schema({ // always conversationId: a.id().required(), associatedUserMessageId: a.id().required(), - contentBlockIndex: a.integer().required(), + contentBlockIndex: a.integer(), accumulatedTurnContent: a.ref('MockContentBlock').array(), // these describe chunks or end of block @@ -110,6 +116,9 @@ const schema = a.schema({ // when message is complete stopReason: a.string(), + + // error + errors: a.ref('MockConversationTurnError').array(), }) .secondaryIndexes((index) => [ index('conversationId').sortKeys(['associatedUserMessageId']), From 613bca948e11fca78e196f43e5e54f3daa8dfa9c Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 31 Oct 2024 12:13:04 -0700 Subject: [PATCH 074/199] Remove tool usage for non current turns when looking up message history (#2174) * Remove tool usage for non current turns when looking up message history * remove that. --- .changeset/long-toys-suffer.md | 5 + ...ersation_message_history_retriever.test.ts | 251 ++++++++++++++++++ .../conversation_message_history_retriever.ts | 83 +++++- 3 files changed, 337 insertions(+), 2 deletions(-) create mode 100644 .changeset/long-toys-suffer.md diff --git a/.changeset/long-toys-suffer.md b/.changeset/long-toys-suffer.md new file mode 100644 index 00000000000..fe92fd87580 --- /dev/null +++ b/.changeset/long-toys-suffer.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': patch +--- + +Remove tool usage for non current turns when looking up message history diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts index a25b4fae176..f17583d1ef0 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts @@ -449,6 +449,257 @@ void describe('Conversation message history retriever', () => { }, ], }, + { + name: 'Removes tool usage from non-current turns', + mockListResponseMessages: [ + { + id: 'someNonCurrentMessageId1', + conversationId: event.conversationId, + role: 'user', + content: [ + { + text: 'nonCurrentMessage1', + }, + ], + }, + { + id: 'someNonCurrentMessageId2', + associatedUserMessageId: 'someNonCurrentMessageId1', + conversationId: event.conversationId, + role: 'assistant', + content: [ + { + text: 'nonCurrentMessage2', + }, + { + toolUse: { + name: 'testToolUse1', + toolUseId: 'testToolUseId1', + input: undefined, + }, + }, + ], + }, + { + id: 'someNonCurrentMessageId3', + conversationId: event.conversationId, + role: 'user', + content: [ + { + toolResult: { + status: 'success', + toolUseId: 'testToolUseId1', + content: undefined, + }, + }, + ], + }, + { + id: 'someNonCurrentMessageId4', + associatedUserMessageId: 'someNonCurrentMessageId3', + conversationId: event.conversationId, + role: 'assistant', + content: [ + { + text: 'nonCurrentMessage3', + }, + { + toolUse: { + name: 'testToolUse2', + toolUseId: 'testToolUseId2', + input: undefined, + }, + }, + ], + }, + { + id: 'someNonCurrentMessageId5', + conversationId: event.conversationId, + role: 'user', + content: [ + { + toolResult: { + status: 'success', + toolUseId: 'testToolUseId2', + content: undefined, + }, + }, + ], + }, + { + id: 'someNonCurrentMessageId5', + associatedUserMessageId: 'someNonCurrentMessageId5', + conversationId: event.conversationId, + role: 'assistant', + content: [ + { + text: 'nonCurrentMessage4', + }, + ], + }, + // Current turn with multiple tool use. + { + id: 'someCurrentMessageId1', + conversationId: event.conversationId, + role: 'user', + content: [ + { + text: 'currentMessage1', + }, + ], + }, + { + id: 'someCurrentMessageId2', + associatedUserMessageId: 'someCurrentMessageId1', + conversationId: event.conversationId, + role: 'assistant', + content: [ + { + text: 'currentMessage2', + }, + { + toolUse: { + name: 'testToolUse3', + toolUseId: 'testToolUseId3', + input: undefined, + }, + }, + ], + }, + { + id: 'someCurrentMessageId3', + conversationId: event.conversationId, + role: 'user', + content: [ + { + toolResult: { + status: 'success', + toolUseId: 'testToolUseId3', + content: undefined, + }, + }, + ], + }, + { + id: 'someCurrentMessageId4', + associatedUserMessageId: 'someCurrentMessageId3', + conversationId: event.conversationId, + role: 'assistant', + content: [ + { + text: 'currentMessage3', + }, + { + toolUse: { + name: 'testToolUse4', + toolUseId: 'testToolUseId4', + input: undefined, + }, + }, + ], + }, + { + id: event.currentMessageId, + conversationId: event.conversationId, + role: 'user', + content: [ + { + toolResult: { + status: 'success', + toolUseId: 'testToolUseId2', + content: undefined, + }, + }, + ], + }, + ], + expectedMessages: [ + { + role: 'user', + content: [ + { + text: 'nonCurrentMessage1', + }, + ], + }, + { + role: 'assistant', + content: [ + { + text: 'nonCurrentMessage2', + }, + { + text: 'nonCurrentMessage3', + }, + { + text: 'nonCurrentMessage4', + }, + ], + }, + { + role: 'user', + content: [ + { + text: 'currentMessage1', + }, + ], + }, + { + role: 'assistant', + content: [ + { + text: 'currentMessage2', + }, + { + toolUse: { + name: 'testToolUse3', + toolUseId: 'testToolUseId3', + input: undefined, + }, + }, + ], + }, + { + role: 'user', + content: [ + { + toolResult: { + status: 'success', + toolUseId: 'testToolUseId3', + content: undefined, + }, + }, + ], + }, + { + role: 'assistant', + content: [ + { + text: 'currentMessage3', + }, + { + toolUse: { + name: 'testToolUse4', + toolUseId: 'testToolUseId4', + input: undefined, + }, + }, + ], + }, + { + role: 'user', + content: [ + { + toolResult: { + status: 'success', + toolUseId: 'testToolUseId2', + content: undefined, + }, + }, + ], + }, + ], + }, ]; for (const testCase of testCases) { diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts index e39b1d69442..2cb9f88372d 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts @@ -1,4 +1,8 @@ -import { ConversationMessage, ConversationTurnEvent } from './types'; +import { + ConversationMessage, + ConversationMessageContentBlock, + ConversationTurnEvent, +} from './types'; import { GraphqlRequestExecutor } from './graphql_request_executor'; export type ConversationHistoryMessageItem = ConversationMessage & { @@ -137,7 +141,7 @@ export class ConversationMessageHistoryRetriever { }); // Reconcile history and inject aiContext - return messages.reduce((acc, current) => { + const orderedMessages = messages.reduce((acc, current) => { // Bedrock expects that message history is user->assistant->user->assistant->... and so on. // The chronological order doesn't assure this ordering if there were any concurrent messages sent. // Therefore, conversation is ordered by user's messages only and corresponding assistant messages are inserted @@ -176,6 +180,81 @@ export class ConversationMessageHistoryRetriever { } return acc; }, [] as Array); + + // Remove tool usage from non-current turn and squash messages. + return this.squashNonCurrentTurns(orderedMessages); + }; + + /** + * This function removes tool usage from non-current turns. + * The tool usage and result blocks don't matter after a turn is completed, + * but do cost extra tokens to process. + * The algorithm is as follows: + * 1. Find where current turn begins. I.e. last user message that isn't tool block. + * 2. Remove toolUse and toolResult blocks before current turn. + * 3. Squash continuous sequences of messages that belong to same 'message.role'. + */ + private squashNonCurrentTurns = (messages: Array) => { + const isNonToolBlockPredicate = ( + contentBlock: ConversationMessageContentBlock + ) => !contentBlock.toolUse && !contentBlock.toolResult; + + // find where current turn begins. I.e. last user message that is not related to tools + const lastNonToolUseUserMessageIndex = messages.findLastIndex((message) => { + return ( + message.role === 'user' && message.content.find(isNonToolBlockPredicate) + ); + }); + + // No non-current turns, don't transform. + if (lastNonToolUseUserMessageIndex <= 0) { + return messages; + } + + const squashedMessages: Array = []; + + // Define a "buffer". I.e. a message we keep around and squash content on. + let currentSquashedMessage: ConversationMessage | undefined = undefined; + // Process messages before current turn begins + // Remove tool usage blocks. + // Combine content for consecutive message that have same role. + for (let i = 0; i < lastNonToolUseUserMessageIndex; i++) { + const currentMessage = messages[i]; + const currentMessageRole = currentMessage.role; + const currentMessageNonToolContent = currentMessage.content.filter( + isNonToolBlockPredicate + ); + if (currentMessageNonToolContent.length === 0) { + // Tool only message. Nothing to squash, skip; + continue; + } + + if (!currentSquashedMessage) { + // Nothing squashed yet, initialize the buffer. + currentSquashedMessage = { + role: currentMessageRole, + content: currentMessageNonToolContent, + }; + } else if (currentSquashedMessage.role === currentMessageRole) { + // if role is same append content. + currentSquashedMessage.content.push(...currentMessageNonToolContent); + } else { + // if role flips push current squashed message and re-initialize the buffer. + squashedMessages.push(currentSquashedMessage); + currentSquashedMessage = { + role: currentMessageRole, + content: currentMessageNonToolContent, + }; + } + } + // flush the last buffer. + if (currentSquashedMessage) { + squashedMessages.push(currentSquashedMessage); + } + + // Append current turn as is. + squashedMessages.push(...messages.slice(lastNonToolUseUserMessageIndex)); + return squashedMessages; }; private getCurrentMessage = From 4418a51ffff7579be20689c0c738a89d1de3f98f Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 31 Oct 2024 13:34:58 -0700 Subject: [PATCH 075/199] Update lint-staged (#2176) --- package-lock.json | 482 ++++++++++++++++++++++++++++++---------------- package.json | 2 +- 2 files changed, 313 insertions(+), 171 deletions(-) diff --git a/package-lock.json b/package-lock.json index 866fae1fc69..9c4763ee18d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,7 @@ "fs-extra": "^11.1.1", "glob": "^10.1.0", "husky": "^8.0.3", - "lint-staged": "^13.2.1", + "lint-staged": "^15.2.10", "prettier": "^2.8.7", "rimraf": "^5.0.0", "semver": "^7.5.4", @@ -19060,16 +19060,16 @@ } }, "node_modules/ansi-escapes": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", - "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", "dev": true, "license": "MIT", "dependencies": { - "type-fest": "^1.0.2" + "environment": "^1.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -20878,16 +20878,16 @@ } }, "node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", "dependencies": { - "restore-cursor": "^4.0.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -20906,22 +20906,76 @@ } }, "node_modules/cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", "dev": true, "license": "MIT", "dependencies": { "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" + "string-width": "^7.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/cli-width": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", @@ -21049,13 +21103,13 @@ } }, "node_modules/commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/comment-parser": { @@ -21869,6 +21923,19 @@ "node": ">=4" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -23948,6 +24015,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -25822,13 +25902,16 @@ } }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -25839,28 +25922,28 @@ "license": "MIT" }, "node_modules/lint-staged": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.3.0.tgz", - "integrity": "sha512-mPRtrYnipYYv1FEE134ufbWpeggNTo+O/UPzngoaKzbzHAthvR55am+8GfHTnqNRQVRRrYQLGW9ZyUoD7DsBHQ==", + "version": "15.2.10", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", + "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "5.3.0", - "commander": "11.0.0", - "debug": "4.3.4", - "execa": "7.2.0", - "lilconfig": "2.1.0", - "listr2": "6.6.1", - "micromatch": "4.0.5", - "pidtree": "0.6.0", - "string-argv": "0.3.2", - "yaml": "2.3.1" + "chalk": "~5.3.0", + "commander": "~12.1.0", + "debug": "~4.3.6", + "execa": "~8.0.1", + "lilconfig": "~3.1.2", + "listr2": "~8.2.4", + "micromatch": "~4.0.8", + "pidtree": "~0.6.0", + "string-argv": "~0.3.2", + "yaml": "~2.5.0" }, "bin": { "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=18.12.0" }, "funding": { "url": "https://opencollective.com/lint-staged" @@ -25879,123 +25962,107 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/lint-staged/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/listr2": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", + "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=18.0.0" } }, - "node_modules/lint-staged/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + "node": ">=12" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/lint-staged/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/lint-staged/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=14.18.0" - } + "license": "MIT" }, - "node_modules/lint-staged/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=8.6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/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, - "license": "MIT" - }, - "node_modules/lint-staged/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } }, - "node_modules/listr2": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", - "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, "license": "MIT", "dependencies": { - "cli-truncate": "^3.1.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^5.0.1", - "rfdc": "^1.3.0", - "wrap-ansi": "^8.1.0" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" + "node": ">=18" }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/locate-path": { @@ -26211,20 +26278,20 @@ } }, "node_modules/log-update": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", - "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-escapes": "^5.0.0", - "cli-cursor": "^4.0.0", - "slice-ansi": "^5.0.0", - "strip-ansi": "^7.0.1", - "wrap-ansi": "^8.0.1" + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -26243,6 +26310,77 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/log-update/node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -26259,6 +26397,24 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", @@ -26503,6 +26659,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -28581,55 +28750,38 @@ } }, "node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "license": "MIT", "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/restore-cursor/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/restore-cursor/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "mimic-function": "^5.0.0" }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -30304,19 +30456,6 @@ "node": ">=4" } }, - "node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -31353,11 +31492,14 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", "dev": true, "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } diff --git a/package.json b/package.json index fd58cf6c046..877a54832b1 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "fs-extra": "^11.1.1", "glob": "^10.1.0", "husky": "^8.0.3", - "lint-staged": "^13.2.1", + "lint-staged": "^15.2.10", "prettier": "^2.8.7", "rimraf": "^5.0.0", "semver": "^7.5.4", From 11d62fea741270ab73a111afe9e8ef4991c2acc4 Mon Sep 17 00:00:00 2001 From: vigy02 Date: Thu, 31 Oct 2024 13:49:22 -0700 Subject: [PATCH 076/199] feat: Add support for custom Lambda function email senders in Auth construct (#2087) * fix: clearing the .amplify/generated/env/ before synthesis * fix: clearing the .amplify/generated/env/ before synthesis * fix: clearing the .amplify/generated/env/ before synthesis * fix: clearing the .amplify/generated/env/ before synthesis * fix: clearing the .amplify/generated/env/ before synthesis * fix: clearing the .amplify/generated/env/ before synthesis * chore: add changeset * fix: clearing the .amplify/generated/env/ before synthesis * fix: clearing the .amplify/generated/env/ before synthesis * fix: clearing the .amplify/generated/env/ before synthesis * fix: Clear generated env directory before shim generation * fix: Clear generated env directory before shim generation * fix: Clear generated env directory before shim generation * fix: Clear generated env directory before shim generation * chore: add changeset * fix: Clear generated env directory before shim generation * fix: Clear generated env directory before shim generation * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * feat: adding custom lambda function trigger for email * cleanup * chore: removed unwanted changesets * chore: preparing for merge * chore: preparing for merge * chore: preparing for merge * feat: added a functionality to translate auth-props for custom fucntion email * chore: added changeset * chore: added new API * feat: added a functionality to translate auth-props for custom fucntion email * feat: added a test case to cover custom function in backend-auth * feat: added a test case to cover custom function in backend-auth * feat: added a test case to cover custom function in backend-auth * feat: added a test case to cover custom function in backend-auth * feat: added a test case to cover custom function in backend-auth * feat: narrowed down the permission by updating the conditions * Refactored the code * Refactored the code * added a test case for checking lambdaTrigger={} empty condition * added a test case for checking lambdaTrigger={} empty condition * Merge Branch * Merge Branch * Merge Branch * Merge Branch * Merge Branch * Merge Branch * Merge Branch * Merge Branch * Merge Branch * Merge Branch * fixed the code to use addTrigger instead of manually setting up permissions * added KMS Key for customEmailSender * changed KMS key to not read-only * changed the test case to include lambdaArn * add a test case validation for KMS key * fixed the code to use addTrigger instead of manually setting up permissions * added KMS Key for customEmailSender * changed KMS key to not read-only * changed the test case to include lambdaArn * add a test case validation for KMS key * detect transform errors with multiple errors (#2102) * detect transform errors with multiple errors * new method of getting multiple transform errors * Add minify option to defineFunction (#2093) * Add minify option to defineFunction * Add unit tests and e2e tests when set minify option to false * Add changeset * Update API.md * add bundling options * Update .changeset/pink-rockets-dance.md * use optional chaining * include funcNoMinify into function.ts --------- Co-authored-by: Kamil Sobol * upgrade constructs (#2103) * Remove deprecated messages field from event (#2106) * detect generic CFN stack creation errors (#2108) * Fix cdk tests when new dependencies are shipped to npm. (#2107) * Fix cdk tests when new dependencies are shipped to npm. * try this * try this * try this * try this * API changes * Update API changes * Added kmsKeyArn for custom user KMS keys * chore: added changesets and updated API's * chore: added changesets * Added integration tests for customEmailSender * updated the API files to reflect master * feat: added customSenderEmail with types and added exceptions to eslint dict * chore: Updated API * chore: Updated API * chore: updated API * Delete packages/ai-constructs/API.md * chore: Updated API * chore: delete unused file * chore: update changeset * chore: update changeset * chore: Updated API and changeSets * chore: Updated the API from main * API updates to resolve conflicting naming * Updated the types in backend-auth * chore: Updated changesets * Added custom Email handler function and refactored the types of auth-construct * chore: updated API --------- Co-authored-by: Roshane Pascual Co-authored-by: MURAKAMI Masahiko Co-authored-by: Kamil Sobol Co-authored-by: Kamil Sobol --- .changeset/curvy-pans-brake.md | 8 + .eslint_dictionary.json | 3 + packages/auth-construct/API.md | 9 +- packages/auth-construct/src/construct.ts | 72 +++++++-- packages/auth-construct/src/index.ts | 1 + packages/auth-construct/src/types.ts | 17 ++- packages/backend-auth/API.md | 14 +- packages/backend-auth/src/factory.test.ts | 140 ++++++++++++++++++ packages/backend-auth/src/factory.ts | 26 +++- .../backend-auth/src/translate_auth_props.ts | 53 ++++++- packages/backend-auth/src/types.ts | 10 ++ .../data_storage_auth_with_triggers.test.ts | 1 + .../data_storage_auth_with_triggers.ts | 105 ++++++++++++- .../amplify/auth/resource.ts | 9 +- .../amplify/backend.ts | 24 +++ .../func-src/handler_custom_email_sender.ts | 28 ++++ .../amplify/function.ts | 5 + .../amplify/test_factories.ts | 2 + .../hotswap-update-files/function.ts | 5 + 19 files changed, 506 insertions(+), 26 deletions(-) create mode 100644 .changeset/curvy-pans-brake.md create mode 100644 packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_custom_email_sender.ts diff --git a/.changeset/curvy-pans-brake.md b/.changeset/curvy-pans-brake.md new file mode 100644 index 00000000000..6d6cc9dd7ae --- /dev/null +++ b/.changeset/curvy-pans-brake.md @@ -0,0 +1,8 @@ +--- +'@aws-amplify/auth-construct': minor +'@aws-amplify/backend-auth': minor +'@aws-amplify/backend': minor +'@aws-amplify/integration-tests': minor +--- + +Add support for custom Lambda function email senders in Auth construct diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index c987d863b63..399dd952f7a 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -40,6 +40,7 @@ "datasync", "debounce", "declarator", + "decrypt", "deployer", "deprecations", "deprecator", @@ -146,6 +147,7 @@ "sigint", "signout", "signup", + "SKey", "sms", "stderr", "stdin", @@ -159,6 +161,7 @@ "synthing", "testname", "testnamebucket", + "testuser", "timestamps", "tmpdir", "todos", diff --git a/packages/auth-construct/API.md b/packages/auth-construct/API.md index 6afb8647cbd..f3c895c6ce2 100644 --- a/packages/auth-construct/API.md +++ b/packages/auth-construct/API.md @@ -9,6 +9,7 @@ import { AuthResources } from '@aws-amplify/plugin-types'; import { aws_cognito } from 'aws-cdk-lib'; import { BackendOutputStorageStrategy } from '@aws-amplify/plugin-types'; import { Construct } from 'constructs'; +import { IFunction } from 'aws-cdk-lib/aws-lambda'; import { NumberAttributeConstraints } from 'aws-cdk-lib/aws-cognito'; import { ResourceProvider } from '@aws-amplify/plugin-types'; import { SecretValue } from 'aws-cdk-lib'; @@ -47,7 +48,7 @@ export type AuthProps = { externalProviders?: ExternalProviderOptions; }; senders?: { - email: Pick; + email: Pick | CustomEmailSender; }; userAttributes?: UserAttributes; multifactor?: MFA; @@ -84,6 +85,12 @@ export type CustomAttributeString = CustomAttributeBase & StringAttributeConstra dataType: 'String'; }; +// @public +export type CustomEmailSender = { + handler: IFunction; + kmsKeyArn?: string; +}; + // @public export type EmailLogin = true | EmailLoginSettings; diff --git a/packages/auth-construct/src/construct.ts b/packages/auth-construct/src/construct.ts index 13c6680b652..84f9455d7b9 100644 --- a/packages/auth-construct/src/construct.ts +++ b/packages/auth-construct/src/construct.ts @@ -34,6 +34,7 @@ import { UserPoolIdentityProviderOidc, UserPoolIdentityProviderSaml, UserPoolIdentityProviderSamlMetadataType, + UserPoolOperation, UserPoolProps, } from 'aws-cdk-lib/aws-cognito'; import { FederatedPrincipal, Role } from 'aws-cdk-lib/aws-iam'; @@ -51,6 +52,7 @@ import { StackMetadataBackendOutputStorageStrategy, } from '@aws-amplify/backend-output-storage'; import * as path from 'path'; +import { IKey, Key } from 'aws-cdk-lib/aws-kms'; type DefaultRoles = { auth: Role; unAuth: Role }; type IdentityProviderSetupResult = { @@ -130,6 +132,11 @@ export class AmplifyAuth role: Role; }; } = {}; + /** + * The KMS key used for encrypting custom email sender data. + * This is only set when using a custom email sender. + */ + private customEmailSenderKMSkey: IKey | undefined; /** * Create a new Auth construct with AuthProps. @@ -141,24 +148,39 @@ export class AmplifyAuth props: AuthProps = DEFAULTS.IF_NO_PROPS_PROVIDED ) { super(scope, id); - this.name = props.name ?? ''; this.domainPrefix = props.loginWith.externalProviders?.domainPrefix; - // UserPool this.computedUserPoolProps = this.getUserPoolProps(props); + this.userPool = new cognito.UserPool( this, `${this.name}UserPool`, this.computedUserPoolProps ); + /** + * Configure custom email sender for Cognito User Pool + * Grant necessary permissions for Lambda function to decrypt emails + * and allow Cognito to invoke the Lambda function + */ + if ( + props.senders?.email && + 'handler' in props.senders.email && + this.customEmailSenderKMSkey + ) { + this.customEmailSenderKMSkey.grantDecrypt(props.senders.email.handler); + this.customEmailSenderKMSkey.grantEncrypt(props.senders.email.handler); + this.userPool.addTrigger( + UserPoolOperation.of('customEmailSender'), + props.senders.email.handler + ); + } // UserPool - External Providers (Oauth, SAML, OIDC) and User Pool Domain this.providerSetupResult = this.setupExternalProviders( this.userPool, props.loginWith ); - // UserPool Client const userPoolClient = new cognito.UserPoolClient( this, @@ -478,7 +500,30 @@ export class AmplifyAuth }, { standardAttributes: {}, customAttributes: {} } ); - + /** + * Handle KMS key for custom email sender + * If a custom email sender is provided, we either use the provided KMS key ARN + * or create a new KMS key if one is not provided. + */ + if (props.senders?.email && 'handler' in props.senders.email) { + if (props.senders.email.kmsKeyArn) { + // Use the provided KMS key ARN + this.customEmailSenderKMSkey = Key.fromKeyArn( + this, + `${this.name}CustomSenderKey`, + props.senders.email.kmsKeyArn + ); + } else { + // Create a new KMS key if not provided + this.customEmailSenderKMSkey = new Key( + props.senders.email.handler.stack, + `${this.name}CustomSenderKey`, + { + enableKeyRotation: true, + } + ); + } + } const userPoolProps: UserPoolProps = { signInCaseSensitive: DEFAULTS.SIGN_IN_CASE_SENSITIVE, signInAliases: { @@ -503,15 +548,15 @@ export class AmplifyAuth customAttributes: { ...customAttributes, }, - email: props.senders - ? cognito.UserPoolEmail.withSES({ - fromEmail: props.senders.email.fromEmail, - fromName: props.senders.email.fromName, - replyTo: props.senders.email.replyTo, - sesRegion: Stack.of(this).region, - }) - : undefined, - + email: + props.senders && 'fromEmail' in props.senders.email + ? cognito.UserPoolEmail.withSES({ + fromEmail: props.senders.email.fromEmail, + fromName: props.senders.email.fromName, + replyTo: props.senders.email.replyTo, + sesRegion: Stack.of(this).region, + }) + : undefined, selfSignUpEnabled: DEFAULTS.ALLOW_SELF_SIGN_UP, mfa: mfaMode, mfaMessage: this.getMFAMessage(props.multifactor), @@ -528,6 +573,7 @@ export class AmplifyAuth props.loginWith.email?.userInvitation ) : undefined, + customSenderKmsKey: this.customEmailSenderKMSkey, }; return userPoolProps; }; diff --git a/packages/auth-construct/src/index.ts b/packages/auth-construct/src/index.ts index 13af450f20c..85e3aa6c6c1 100644 --- a/packages/auth-construct/src/index.ts +++ b/packages/auth-construct/src/index.ts @@ -26,6 +26,7 @@ export { CustomAttributeBoolean, CustomAttributeDateTime, CustomAttributeBase, + CustomEmailSender, } from './types.js'; export { AmplifyAuth } from './construct.js'; export { triggerEvents } from './trigger_events.js'; diff --git a/packages/auth-construct/src/types.ts b/packages/auth-construct/src/types.ts index 5083ffb73c4..c3d4ddbbeaa 100644 --- a/packages/auth-construct/src/types.ts +++ b/packages/auth-construct/src/types.ts @@ -9,6 +9,7 @@ import { UserPoolIdentityProviderSamlMetadata, UserPoolSESOptions, } from 'aws-cdk-lib/aws-cognito'; +import { IFunction } from 'aws-cdk-lib/aws-lambda'; export type VerificationEmailWithLink = { /** * The type of verification. Must be one of "CODE" or "LINK". @@ -380,6 +381,14 @@ export type CustomAttribute = export type UserAttributes = StandardAttributes & Record<`custom:${string}`, CustomAttribute>; +/** + * CustomEmailSender type for configuring a custom Lambda function for email sending + */ +export type CustomEmailSender = { + handler: IFunction; + kmsKeyArn?: string; +}; + /** * Input props for the AmplifyAuth construct */ @@ -417,11 +426,15 @@ export type AuthProps = { */ senders?: { /** - * Configure Cognito to send emails from SES + * Configure Cognito to send emails from SES or a custom message trigger * SES configurations enable the use of customized email sender addresses and names + * Custom message triggers enable the use of third-party email providers when sending email notifications to users * @see https://docs.amplify.aws/react/build-a-backend/auth/moving-to-production/#email + * @see https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-email-sender.html */ - email: Pick; + email: + | Pick + | CustomEmailSender; }; /** * The set of attributes that are required for every user in the user pool. Read more on attributes here - https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html diff --git a/packages/backend-auth/API.md b/packages/backend-auth/API.md index f9b6247cedf..a1c07703d79 100644 --- a/packages/backend-auth/API.md +++ b/packages/backend-auth/API.md @@ -5,6 +5,7 @@ ```ts import { AmazonProviderProps } from '@aws-amplify/auth-construct'; +import { AmplifyFunction } from '@aws-amplify/plugin-types'; import { AppleProviderProps } from '@aws-amplify/auth-construct'; import { AuthProps } from '@aws-amplify/auth-construct'; import { AuthResources } from '@aws-amplify/plugin-types'; @@ -16,12 +17,14 @@ import { ExternalProviderOptions } from '@aws-amplify/auth-construct'; import { FacebookProviderProps } from '@aws-amplify/auth-construct'; import { FunctionResources } from '@aws-amplify/plugin-types'; import { GoogleProviderProps } from '@aws-amplify/auth-construct'; +import { IFunction } from 'aws-cdk-lib/aws-lambda'; import { OidcProviderProps } from '@aws-amplify/auth-construct'; import { ResourceAccessAcceptor } from '@aws-amplify/plugin-types'; import { ResourceAccessAcceptorFactory } from '@aws-amplify/plugin-types'; import { ResourceProvider } from '@aws-amplify/plugin-types'; import { StackProvider } from '@aws-amplify/plugin-types'; import { TriggerEvent } from '@aws-amplify/auth-construct'; +import { UserPoolSESOptions } from 'aws-cdk-lib/aws-cognito'; // @public export type ActionIam = 'addUserToGroup' | 'createGroup' | 'createUser' | 'deleteGroup' | 'deleteUser' | 'deleteUserAttributes' | 'disableUser' | 'enableUser' | 'forgetDevice' | 'getDevice' | 'getGroup' | 'getUser' | 'listUsers' | 'listUsersInGroup' | 'listGroups' | 'listDevices' | 'listGroupsForUser' | 'removeUserFromGroup' | 'resetUserPassword' | 'setUserMfaPreference' | 'setUserPassword' | 'setUserSettings' | 'updateDeviceStatus' | 'updateGroup' | 'updateUserAttributes'; @@ -36,10 +39,13 @@ export type AmazonProviderFactoryProps = Omit & { +export type AmplifyAuthProps = Expand & { loginWith: Expand; triggers?: Partial>>>; access?: AuthAccessGenerator; + senders?: { + email: Pick | CustomEmailSender; + }; }>; // @public @@ -80,6 +86,12 @@ export type AuthLoginWithFactoryProps = Omit & ResourceAccessAcceptorFactory & StackProvider; +// @public +export type CustomEmailSender = { + handler: ConstructFactory | IFunction; + kmsKeyArn?: string; +}; + // @public export const defineAuth: (props: AmplifyAuthProps) => ConstructFactory; diff --git a/packages/backend-auth/src/factory.test.ts b/packages/backend-auth/src/factory.test.ts index bbf285b7cd8..a01c5f59352 100644 --- a/packages/backend-auth/src/factory.test.ts +++ b/packages/backend-auth/src/factory.test.ts @@ -26,6 +26,8 @@ import { import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; import { AmplifyUserError } from '@aws-amplify/platform-core'; import { CfnFunction } from 'aws-cdk-lib/aws-lambda'; +import { Key } from 'aws-cdk-lib/aws-kms'; +import { CustomEmailSender } from './types.js'; const createStackAndSetContext = (): Stack => { const app = new App(); @@ -355,6 +357,144 @@ void describe('AmplifyAuthFactory', () => { }); }); }); + + void it('sets customEmailSender when function is provided as email sender', () => { + const testFunc = new aws_lambda.Function(stack, 'testFunc', { + code: aws_lambda.Code.fromInline('test placeholder'), + runtime: aws_lambda.Runtime.NODEJS_18_X, + handler: 'index.handler', + }); + const funcStub: ConstructFactory> = { + getInstance: () => { + return { + resources: { + lambda: testFunc, + cfnResources: { + cfnFunction: testFunc.node.findChild('Resource') as CfnFunction, + }, + }, + }; + }, + }; + const customEmailSender: CustomEmailSender = { + handler: funcStub, + }; + resetFactoryCount(); + + const authWithTriggerFactory = defineAuth({ + loginWith: { email: true }, + senders: { email: customEmailSender }, + }); + + const backendAuth = authWithTriggerFactory.getInstance(getInstanceProps); + + const template = Template.fromStack(backendAuth.stack); + + template.hasResourceProperties('AWS::Cognito::UserPool', { + LambdaConfig: { + CustomEmailSender: { + LambdaArn: { + Ref: Match.stringLikeRegexp('testFunc'), + }, + }, + KMSKeyID: { + Ref: Match.stringLikeRegexp('CustomSenderKey'), + }, + }, + }); + }); + void it('ensures empty lambdaTriggers do not remove triggers added elsewhere', () => { + const testFunc = new aws_lambda.Function(stack, 'testFunc', { + code: aws_lambda.Code.fromInline('test placeholder'), + runtime: aws_lambda.Runtime.NODEJS_18_X, + handler: 'index.handler', + }); + const funcStub: ConstructFactory> = { + getInstance: () => { + return { + resources: { + lambda: testFunc, + cfnResources: { + cfnFunction: testFunc.node.findChild('Resource') as CfnFunction, + }, + }, + }; + }, + }; + const customEmailSender: CustomEmailSender = { + handler: funcStub, + }; + resetFactoryCount(); + + const authWithTriggerFactory = defineAuth({ + loginWith: { email: true }, + senders: { email: customEmailSender }, + triggers: { preSignUp: funcStub }, + }); + + const backendAuth = authWithTriggerFactory.getInstance(getInstanceProps); + + const template = Template.fromStack(backendAuth.stack); + template.hasResourceProperties('AWS::Cognito::UserPool', { + LambdaConfig: { + PreSignUp: { + Ref: Match.stringLikeRegexp('testFunc'), + }, + CustomEmailSender: { + LambdaArn: { + Ref: Match.stringLikeRegexp('testFunc'), + }, + }, + KMSKeyID: { + Ref: Match.stringLikeRegexp('CustomSenderKey'), + }, + }, + }); + }); + void it('uses provided KMS key ARN and sets up custom email sender', () => { + const customKmsKeyArn = new Key(stack, `CustomSenderKey`, { + enableKeyRotation: true, + }); + const testFunc = new aws_lambda.Function(stack, 'testFunc', { + code: aws_lambda.Code.fromInline('test placeholder'), + runtime: aws_lambda.Runtime.NODEJS_18_X, + handler: 'index.handler', + }); + const funcStub: ConstructFactory> = { + getInstance: () => ({ + resources: { + lambda: testFunc, + cfnResources: { + cfnFunction: testFunc.node.findChild('Resource') as CfnFunction, + }, + }, + }), + }; + const customEmailSender: CustomEmailSender = { + handler: funcStub, + kmsKeyArn: customKmsKeyArn.keyArn, + }; + resetFactoryCount(); + + const authWithTriggerFactory = defineAuth({ + loginWith: { email: true }, + senders: { + email: customEmailSender, + }, + triggers: { preSignUp: funcStub }, + }); + + const backendAuth = authWithTriggerFactory.getInstance(getInstanceProps); + const template = Template.fromStack(backendAuth.stack); + + template.hasResourceProperties('AWS::Cognito::UserPool', { + LambdaConfig: { + KMSKeyID: { + Ref: Match.stringLikeRegexp('CustomSenderKey'), + }, + }, + }); + }); }); const upperCaseFirstChar = (str: string) => { diff --git a/packages/backend-auth/src/factory.ts b/packages/backend-auth/src/factory.ts index 02edd4695c4..48d411f07d3 100644 --- a/packages/backend-auth/src/factory.ts +++ b/packages/backend-auth/src/factory.ts @@ -1,6 +1,10 @@ import * as path from 'path'; import { Policy } from 'aws-cdk-lib/aws-iam'; -import { UserPool, UserPoolOperation } from 'aws-cdk-lib/aws-cognito'; +import { + UserPool, + UserPoolOperation, + UserPoolSESOptions, +} from 'aws-cdk-lib/aws-cognito'; import { AmplifyUserError, TagName } from '@aws-amplify/platform-core'; import { AmplifyAuth, @@ -20,12 +24,16 @@ import { ResourceProvider, StackProvider, } from '@aws-amplify/plugin-types'; -import { translateToAuthConstructLoginWith } from './translate_auth_props.js'; +import { + translateToAuthConstructLoginWith, + translateToAuthConstructSenders, +} from './translate_auth_props.js'; import { authAccessBuilder as _authAccessBuilder } from './access_builder.js'; import { AuthAccessPolicyArbiterFactory } from './auth_access_policy_arbiter.js'; import { AuthAccessGenerator, AuthLoginWithFactoryProps, + CustomEmailSender, Expand, } from './types.js'; import { UserPoolAccessPolicyFactory } from './userpool_access_policy_factory.js'; @@ -36,7 +44,7 @@ export type BackendAuth = ResourceProvider & StackProvider; export type AmplifyAuthProps = Expand< - Omit & { + Omit & { /** * Specify how you would like users to log in. You can choose from email, phone, and even external providers such as LoginWithAmazon. */ @@ -60,6 +68,14 @@ export type AmplifyAuthProps = Expand< * access: (allow) => [allow.resource(groupManager).to(["manageGroups"])] */ access?: AuthAccessGenerator; + /** + * Configure email sender options + */ + senders?: { + email: + | Pick + | CustomEmailSender; + }; } >; @@ -142,6 +158,10 @@ class AmplifyAuthGenerator implements ConstructContainerEntryGenerator { this.props.loginWith, backendSecretResolver ), + senders: translateToAuthConstructSenders( + this.props.senders, + this.getInstanceProps + ), outputStorageStrategy: this.getInstanceProps.outputStorageStrategy, }; if (authProps.loginWith.externalProviders) { diff --git a/packages/backend-auth/src/translate_auth_props.ts b/packages/backend-auth/src/translate_auth_props.ts index f4b20fff374..fad144ef6b1 100644 --- a/packages/backend-auth/src/translate_auth_props.ts +++ b/packages/backend-auth/src/translate_auth_props.ts @@ -6,7 +6,10 @@ import { GoogleProviderProps, OidcProviderProps, } from '@aws-amplify/auth-construct'; -import { BackendSecretResolver } from '@aws-amplify/plugin-types'; +import { + BackendSecretResolver, + ConstructFactoryGetInstanceProps, +} from '@aws-amplify/plugin-types'; import { AmazonProviderFactoryProps, AppleProviderFactoryProps, @@ -16,6 +19,8 @@ import { GoogleProviderFactoryProps, OidcProviderFactoryProps, } from './types.js'; +import { IFunction } from 'aws-cdk-lib/aws-lambda'; +import { AmplifyAuthProps } from './factory.js'; /** * Translate an Auth factory's loginWith to its Auth construct counterpart. Backend secret fields will be resolved @@ -79,6 +84,52 @@ export const translateToAuthConstructLoginWith = ( return result; }; +/** + * Translates the senders property from AmplifyAuthProps to AuthProps format. + * @param senders - The senders object from AmplifyAuthProps. + * @param getInstanceProps - Properties used to get an instance of the sender. + * @returns The translated senders object in AuthProps format, or undefined if no valid sender is provided. + * @description + * This function handles the translation of the 'senders' property, specifically for email senders. + * If no senders are provided or if there's no email sender, it returns undefined. + * If the email sender has a 'getInstance' method, it retrieves the Lambda function and returns it. + * Otherwise, it returns the email sender as is. + */ +export const translateToAuthConstructSenders = ( + senders: AmplifyAuthProps['senders'] | undefined, + getInstanceProps: ConstructFactoryGetInstanceProps +): AuthProps['senders'] | undefined => { + if (!senders || !senders.email) { + return undefined; + } + + // Handle CustomEmailSender type + if ('handler' in senders.email) { + const lambda: IFunction = + 'getInstance' in senders.email.handler + ? senders.email.handler.getInstance(getInstanceProps).resources.lambda + : senders.email.handler; + + return { + email: { + handler: lambda, + kmsKeyArn: senders.email.kmsKeyArn, + }, + }; + } + + // Handle SES configuration + if ('fromEmail' in senders.email) { + return { + email: senders.email, + }; + } + + // If none of the above, return the email configuration as-is + return { + email: senders.email, + }; +}; const translateAmazonProps = ( backendSecretResolver: BackendSecretResolver, diff --git a/packages/backend-auth/src/types.ts b/packages/backend-auth/src/types.ts index 8b7c018febb..46c199f1a11 100644 --- a/packages/backend-auth/src/types.ts +++ b/packages/backend-auth/src/types.ts @@ -8,6 +8,7 @@ import { OidcProviderProps, } from '@aws-amplify/auth-construct'; import { + AmplifyFunction, BackendSecret, ConstructFactory, ConstructFactoryGetInstanceProps, @@ -15,6 +16,7 @@ import { ResourceAccessAcceptorFactory, ResourceProvider, } from '@aws-amplify/plugin-types'; +import { IFunction } from 'aws-cdk-lib/aws-lambda'; /** * This utility allows us to expand nested types in auto complete prompts. @@ -252,3 +254,11 @@ export type ActionIam = | 'updateDeviceStatus' | 'updateGroup' | 'updateUserAttributes'; + +/** + * CustomEmailSender type for configuring a custom Lambda function for email sending + */ +export type CustomEmailSender = { + handler: ConstructFactory | IFunction; + kmsKeyArn?: string; +}; diff --git a/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts b/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts index 7570ece6fd7..37c7e3c456c 100644 --- a/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts +++ b/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts @@ -52,6 +52,7 @@ void it('data storage auth with triggers', () => { assertExpectedLogicalIds(templates.defaultNodeFunc, 'AWS::Lambda::Function', [ 'defaultNodeFunctionlambda5C194062', 'echoFunclambdaE17DCA46', + 'funcCustomEmailSenderlambda3CCBA9A6', 'funcNoMinifylambda91CDF3E0', 'funcWithAwsSdklambda5F770AD7', 'funcWithSchedulelambda0B6E4271', diff --git a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts index 893c3bb8ffa..d9924bc2381 100644 --- a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts +++ b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts @@ -33,6 +33,10 @@ import { import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; import isMatch from 'lodash.ismatch'; import { TextWriter, ZipReader } from '@zip.js/zip.js'; +import { + AdminCreateUserCommand, + CognitoIdentityProviderClient, +} from '@aws-sdk/client-cognito-identity-provider'; /** * Creates test projects with data, storage, and auth categories. @@ -68,7 +72,10 @@ export class DataStorageAuthWithTriggerTestProjectCreator private readonly cloudTrailClient: CloudTrailClient = new CloudTrailClient( e2eToolingClientConfig ), - private readonly resourceFinder: DeployedResourcesFinder = new DeployedResourcesFinder() + private readonly resourceFinder: DeployedResourcesFinder = new DeployedResourcesFinder(), + private readonly cognitoClient: CognitoIdentityProviderClient = new CognitoIdentityProviderClient( + e2eToolingClientConfig + ) ) {} createProject = async (e2eProjectDir: string): Promise => { @@ -87,7 +94,8 @@ export class DataStorageAuthWithTriggerTestProjectCreator this.iamClient, this.sqsClient, this.cloudTrailClient, - this.resourceFinder + this.resourceFinder, + this.cognitoClient ); await fs.cp( project.sourceProjectAmplifyDirURL, @@ -154,7 +162,8 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { private readonly iamClient: IAMClient, private readonly sqsClient: SQSClient, private readonly cloudTrailClient: CloudTrailClient, - private readonly resourceFinder: DeployedResourcesFinder + private readonly resourceFinder: DeployedResourcesFinder, + private readonly cognitoClient: CognitoIdentityProviderClient ) { super( name, @@ -262,12 +271,19 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { 'AWS::Lambda::Function', (name) => name.includes('funcNoMinify') ); + const funcCustomEmailSender = + await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::Lambda::Function', + (name) => name.includes('funcCustomEmailSender') + ); assert.equal(defaultNodeLambda.length, 1); assert.equal(node16Lambda.length, 1); assert.equal(funcWithSsm.length, 1); assert.equal(funcWithAwsSdk.length, 1); assert.equal(funcWithSchedule.length, 1); + assert.equal(funcCustomEmailSender.length, 1); const expectedResponse = { s3TestContent: 'this is some test content', @@ -279,7 +295,9 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { await this.checkLambdaResponse(defaultNodeLambda[0], expectedResponse); await this.checkLambdaResponse(node16Lambda[0], expectedResponse); await this.checkLambdaResponse(funcWithSsm[0], 'It is working'); - await this.checkLambdaResponse(funcWithAwsSdk[0], 'It is working'); + + // Custom email sender assertion + await this.assertCustomEmailSenderWorks(backendId); await this.assertScheduleInvokesFunction(backendId); @@ -642,4 +660,83 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { ); } }; + + private assertCustomEmailSenderWorks = async ( + backendId: BackendIdentifier + ) => { + const TIMEOUT_MS = 1000 * 60 * 2; // 2 minutes + const startTime = Date.now(); + const queue = await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::SQS::Queue', + (name) => name.includes('customEmailSenderQueue') + ); + + assert.strictEqual(queue.length, 1, 'Custom email sender queue not found'); + + // Trigger an email sending operation + await this.triggerEmailSending(backendId); + + // Wait for the SQS message + let messageReceived = false; + while (Date.now() - startTime < TIMEOUT_MS && !messageReceived) { + const response = await this.sqsClient.send( + new ReceiveMessageCommand({ + QueueUrl: queue[0], + WaitTimeSeconds: 20, + }) + ); + + if (response.Messages && response.Messages.length > 0) { + messageReceived = true; + // Verify the message content + const messageBody = JSON.parse(response.Messages[0].Body || '{}'); + assert.strictEqual( + messageBody.message, + 'Custom Email Sender is working', + 'Unexpected message content' + ); + + // Delete the message + await this.sqsClient.send( + new DeleteMessageCommand({ + QueueUrl: queue[0], + ReceiptHandle: response.Messages[0].ReceiptHandle!, + }) + ); + } + } + + assert.strictEqual( + messageReceived, + true, + 'Custom email sender was not triggered within the timeout period' + ); + }; + + private triggerEmailSending = async (backendId: BackendIdentifier) => { + const userPoolId = await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::Cognito::UserPool', + () => true + ); + + assert.strictEqual(userPoolId.length, 1, 'User pool not found'); + + const username = `testuser_${Date.now()}@example.com`; + const password = 'TestPassword123!'; + + await this.cognitoClient.send( + new AdminCreateUserCommand({ + UserPoolId: userPoolId[0], + Username: username, + TemporaryPassword: password, + UserAttributes: [ + { Name: 'email', Value: username }, + { Name: 'email_verified', Value: 'true' }, + ], + }) + ); + // The creation of a new user should trigger the custom email sender + }; } diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts index 92733f12ec8..14c55e503ec 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts @@ -1,5 +1,9 @@ import { defineAuth, secret } from '@aws-amplify/backend'; -import { defaultNodeFunc } from '../function.js'; +import { defaultNodeFunc, funcCustomEmailSender } from '../function.js'; + +const customEmailSenderFunction = { + handler: funcCustomEmailSender, +}; export const auth = defineAuth({ loginWith: { @@ -21,6 +25,9 @@ export const auth = defineAuth({ logoutUrls: ['https://logout.com'], }, }, + senders: { + email: customEmailSenderFunction, + }, triggers: { postConfirmation: defaultNodeFunc, }, diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/backend.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/backend.ts index d9d298e5557..c47ce414bd1 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/backend.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/backend.ts @@ -26,3 +26,27 @@ if (scheduleFunctionLambdaRole) { ); } backend.funcWithSchedule.addEnvironment('SQS_QUEUE_URL', queue.queueUrl); + +// Queue setup for customEmailSender + +const customEmailSenderLambda = backend.funcCustomEmailSender.resources.lambda; +const customEmailSenderLambdaRole = customEmailSenderLambda.role; +const customEmailSenderQueueStack = Stack.of(customEmailSenderLambda); +const emailSenderQueue = new Queue( + customEmailSenderQueueStack, + 'amplify-customEmailSenderQueue' +); + +if (customEmailSenderLambdaRole) { + emailSenderQueue.grantSendMessages( + Role.fromRoleArn( + customEmailSenderQueueStack, + 'CustomEmailSenderLambdaExecutionRole', + customEmailSenderLambdaRole.roleArn + ) + ); +} +backend.funcCustomEmailSender.addEnvironment( + 'CUSTOM_EMAIL_SENDER_SQS_QUEUE_URL', + emailSenderQueue.queueUrl +); diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_custom_email_sender.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_custom_email_sender.ts new file mode 100644 index 00000000000..e52be2ea0a1 --- /dev/null +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_custom_email_sender.ts @@ -0,0 +1,28 @@ +import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs'; + +/** + * This function asserts that custom email sender function is working properly + */ +export const handler = async () => { + const sqsClient = new SQSClient({ region: process.env.region }); + + const queueUrl = process.env.CUSTOM_EMAIL_SENDER_SQS_QUEUE_URL; + + if (!queueUrl) { + throw new Error('SQS_QUEUE_URL is not set in environment variables'); + } + + const messageBody = JSON.stringify({ + message: 'Custom Email Sender is working', + timeStamp: new Date().toISOString(), + }); + + await sqsClient.send( + new SendMessageCommand({ + QueueUrl: queueUrl, + MessageBody: messageBody, + }) + ); + + return 'It is working'; +}; diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/function.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/function.ts index 7e367aca48b..7265fb2ac49 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/function.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/function.ts @@ -58,3 +58,8 @@ export const funcNoMinify = defineFunction({ minify: false, }, }); + +export const funcCustomEmailSender = defineFunction({ + name: 'funcCustomEmailSender', + entry: './func-src/handler_custom_email_sender.ts', +}); diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/test_factories.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/test_factories.ts index d782b45be15..73ee6b04798 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/test_factories.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/test_factories.ts @@ -6,6 +6,7 @@ import { node16Func, funcWithSchedule, funcNoMinify, + funcCustomEmailSender, } from './function.js'; import { storage } from './storage/resource.js'; import { auth } from './auth/resource.js'; @@ -20,4 +21,5 @@ export const dataStorageAuthWithTriggers = { funcWithAwsSdk, funcWithSchedule, funcNoMinify, + funcCustomEmailSender, }; diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/hotswap-update-files/function.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/hotswap-update-files/function.ts index 542465f8595..a0abf3e3021 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/hotswap-update-files/function.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/hotswap-update-files/function.ts @@ -60,3 +60,8 @@ export const funcNoMinify = defineFunction({ minify: false, }, }); + +export const funcCustomEmailSender = defineFunction({ + name: 'funcCustomEmailSender', + entry: './func-src/handler_custom_email_sender.ts', +}); From f678495e29423567f6f544674d1f2c6f7ab69311 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 31 Oct 2024 14:48:41 -0700 Subject: [PATCH 077/199] Update verdaccio (#2177) --- package-lock.json | 978 ++++++++++++++++++++++++++-------------------- package.json | 2 +- 2 files changed, 551 insertions(+), 429 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9c4763ee18d..fb897043263 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,7 +56,7 @@ "tsx": "^4.6.1", "typedoc": "^0.25.3", "typescript": "~5.2.0", - "verdaccio": "^5.24.1" + "verdaccio": "^6.0.1" }, "engines": { "node": ">=18.16.0" @@ -13842,9 +13842,9 @@ } }, "node_modules/@cypress/request": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", - "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.5.tgz", + "integrity": "sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -13854,14 +13854,14 @@ "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "http-signature": "~1.3.6", + "form-data": "~4.0.0", + "http-signature": "~1.4.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "6.10.4", + "qs": "6.13.0", "safe-buffer": "^5.1.2", "tough-cookie": "^4.1.3", "tunnel-agent": "^0.6.0", @@ -18193,21 +18193,21 @@ "license": "ISC" }, "node_modules/@verdaccio/auth": { - "version": "8.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/auth/-/auth-8.0.0-next-8.1.tgz", - "integrity": "sha512-sPmHdnYuRSMgABCsTJEfz8tb/smONsWVg0g4KK2QycyYZ/A+RwZLV1JLiQb4wzu9zvS0HSloqWqkWlyNHW3mtw==", + "version": "8.0.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/auth/-/auth-8.0.0-next-8.3.tgz", + "integrity": "sha512-x7/gt4R41i5hat5dVT2WfwTeWolSKTo3k8t12ZBhnf+R+L/a79dQ7/sR8JIT6R9A+nkkFn+RiPspH75H7lprZg==", "dev": true, "license": "MIT", "dependencies": { - "@verdaccio/config": "8.0.0-next-8.1", - "@verdaccio/core": "8.0.0-next-8.1", - "@verdaccio/loaders": "8.0.0-next-8.1", - "@verdaccio/logger": "8.0.0-next-8.1", - "@verdaccio/signature": "8.0.0-next-8.0", - "@verdaccio/utils": "7.0.1-next-8.1", + "@verdaccio/config": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/loaders": "8.0.0-next-8.3", + "@verdaccio/logger": "8.0.0-next-8.3", + "@verdaccio/signature": "8.0.0-next-8.1", + "@verdaccio/utils": "8.1.0-next-8.3", "debug": "4.3.7", "lodash": "4.17.21", - "verdaccio-htpasswd": "13.0.0-next-8.1" + "verdaccio-htpasswd": "13.0.0-next-8.3" }, "engines": { "node": ">=18" @@ -18217,6 +18217,52 @@ "url": "https://opencollective.com/verdaccio" } }, + "node_modules/@verdaccio/auth/node_modules/@verdaccio/utils": { + "version": "8.1.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", + "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@verdaccio/core": "8.0.0-next-8.3", + "lodash": "4.17.21", + "minimatch": "7.4.6", + "semver": "7.6.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/verdaccio" + } + }, + "node_modules/@verdaccio/auth/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@verdaccio/auth/node_modules/minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@verdaccio/commons-api": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/@verdaccio/commons-api/-/commons-api-10.2.0.tgz", @@ -18243,21 +18289,41 @@ "license": "MIT" }, "node_modules/@verdaccio/config": { - "version": "8.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/config/-/config-8.0.0-next-8.1.tgz", - "integrity": "sha512-goDVOH4e8xMUxjHybJpi5HwIecVFqzJ9jeNFrRUgtUUn0PtFuNMHgxOeqDKRVboZhc5HK90yed8URK/1O6VsUw==", + "version": "8.0.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/config/-/config-8.0.0-next-8.3.tgz", + "integrity": "sha512-bxlesiVi7A1GAHurq7RLFAFd67NTySSwtVMw7D1Ku2Q3v6kAF4TLqxKUrOaA14k0Zk4qyRu4OgXzbjDg0oARcQ==", "dev": true, "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.1", - "@verdaccio/utils": "7.0.1-next-8.1", + "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/utils": "8.1.0-next-8.3", "debug": "4.3.7", "js-yaml": "4.1.0", "lodash": "4.17.21", "minimatch": "7.4.6" }, "engines": { - "node": ">=14" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/verdaccio" + } + }, + "node_modules/@verdaccio/config/node_modules/@verdaccio/utils": { + "version": "8.1.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", + "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@verdaccio/core": "8.0.0-next-8.3", + "lodash": "4.17.21", + "minimatch": "7.4.6", + "semver": "7.6.3" + }, + "engines": { + "node": ">=12" }, "funding": { "type": "opencollective", @@ -18311,9 +18377,9 @@ } }, "node_modules/@verdaccio/core": { - "version": "8.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-8.0.0-next-8.1.tgz", - "integrity": "sha512-kQRCB2wgXEh8H88G51eQgAFK9IxmnBtkQ8sY5FbmB6PbBkyHrbGcCp+2mtRqqo36j0W1VAlfM3XzoknMy6qQnw==", + "version": "8.0.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-8.0.0-next-8.3.tgz", + "integrity": "sha512-DPJmWANbpbtJN+cfz5CN4kfAl15F5JCv5qRAIQB9sOTNWjTw26cRpMYHFB9/buBQPuH3mWCjOwE6c+EVlvObLg==", "dev": true, "license": "MIT", "dependencies": { @@ -18325,7 +18391,7 @@ "semver": "7.6.3" }, "engines": { - "node": ">=14" + "node": ">=18" }, "funding": { "type": "opencollective", @@ -18386,13 +18452,12 @@ } }, "node_modules/@verdaccio/loaders": { - "version": "8.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/loaders/-/loaders-8.0.0-next-8.1.tgz", - "integrity": "sha512-mqGCUBs862g8mICZwX8CG92p1EZ1Un0DJ2DB7+iVu2TYaEeKoHoIdafabVdiYrbOjLcAOOBrMKE1Wnn14eLxpA==", + "version": "8.0.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/loaders/-/loaders-8.0.0-next-8.3.tgz", + "integrity": "sha512-7bIOdi+U1xSLRu0s1XxQwrV3zzzFaVaTX7JKFgj2tQvMy9AgzlpjbW1CqaH8OTVEqq03Pwvwj5hQlcvyzCwm1A==", "dev": true, "license": "MIT", "dependencies": { - "@verdaccio/logger": "8.0.0-next-8.1", "debug": "4.3.7", "lodash": "4.17.21" }, @@ -18461,14 +18526,14 @@ "license": "MIT" }, "node_modules/@verdaccio/logger": { - "version": "8.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/logger/-/logger-8.0.0-next-8.1.tgz", - "integrity": "sha512-w5kR0/umQkfH2F4PK5Fz9T6z3xz+twewawKLPTUfAgrVAOiWxcikGhhcHWhSGiJ0lPqIa+T0VYuLWMeVeDirGw==", + "version": "8.0.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/logger/-/logger-8.0.0-next-8.3.tgz", + "integrity": "sha512-rLjv/W9QfFHT80L1L/xXQwY3DaZ03NIq64/Bb4GHOudZgzl8C5Efe6u1q8Ti+jqXVAKCzEswMNISdq2XIQ+azQ==", "dev": true, "license": "MIT", "dependencies": { - "@verdaccio/logger-commons": "8.0.0-next-8.1", - "pino": "8.17.2" + "@verdaccio/logger-commons": "8.0.0-next-8.3", + "pino": "9.4.0" }, "engines": { "node": ">=18" @@ -18478,195 +18543,164 @@ "url": "https://opencollective.com/verdaccio" } }, - "node_modules/@verdaccio/logger-7": { - "version": "8.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/logger-7/-/logger-7-8.0.0-next-8.1.tgz", - "integrity": "sha512-V+/B1Wnct3IZ90q6HkI1a3dqbS0ds7s/5WPrS5cmBeLEw78/OGgF76XkhI2+lett7Un1CjVow7mcebOWcZ/Sqw==", + "node_modules/@verdaccio/logger-commons": { + "version": "8.0.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/logger-commons/-/logger-commons-8.0.0-next-8.3.tgz", + "integrity": "sha512-eMG0UDh66JcPX8ez57HCpsZ0FE9G0pCZ51Xei1MeCFVgNLkzrEcnwOcEGZhd3Tew79A4wGgjFFFywkrRIIomwg==", "dev": true, "license": "MIT", "dependencies": { - "@verdaccio/logger-commons": "8.0.0-next-8.1", - "pino": "7.11.0" + "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/logger-prettify": "8.0.0-next-8.1", + "colorette": "2.0.20", + "debug": "4.3.7" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/verdaccio" } }, - "node_modules/@verdaccio/logger-7/node_modules/duplexify": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.2" - } - }, - "node_modules/@verdaccio/logger-7/node_modules/on-exit-leak-free": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz", - "integrity": "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@verdaccio/logger-7/node_modules/pino": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-7.11.0.tgz", - "integrity": "sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==", + "node_modules/@verdaccio/logger-prettify": { + "version": "8.0.0-next-8.1", + "resolved": "https://registry.npmjs.org/@verdaccio/logger-prettify/-/logger-prettify-8.0.0-next-8.1.tgz", + "integrity": "sha512-vLhaGq0q7wtMCcqa0aQY6QOsMNarhTu/l4e6Z8mG/5LUH95GGLsBwpXLnKS94P3deIjsHhc9ycnEmG39txbQ1w==", "dev": true, "license": "MIT", "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.0.0", - "on-exit-leak-free": "^0.2.0", - "pino-abstract-transport": "v0.5.0", - "pino-std-serializers": "^4.0.0", - "process-warning": "^1.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.1.0", - "safe-stable-stringify": "^2.1.0", - "sonic-boom": "^2.2.1", - "thread-stream": "^0.15.1" + "colorette": "2.0.20", + "dayjs": "1.11.13", + "lodash": "4.17.21", + "pino-abstract-transport": "1.2.0", + "sonic-boom": "3.8.1" }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/@verdaccio/logger-7/node_modules/pino-abstract-transport": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz", - "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "duplexify": "^4.1.2", - "split2": "^4.0.0" + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/verdaccio" } }, - "node_modules/@verdaccio/logger-7/node_modules/pino-std-serializers": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", - "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@verdaccio/logger-7/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/@verdaccio/middleware": { + "version": "8.0.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/middleware/-/middleware-8.0.0-next-8.3.tgz", + "integrity": "sha512-yUe4BGA2/o4GnFuKdquU53kI07xsLMKiMvZfj+M0ZFnFCshCqKmXOoSR8hbzQReUm/OZQSLOppliJn68xmXEVQ==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "@verdaccio/config": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/url": "13.0.0-next-8.3", + "@verdaccio/utils": "8.1.0-next-8.3", + "debug": "4.3.7", + "express": "4.21.0", + "express-rate-limit": "5.5.1", + "lodash": "4.17.21", + "lru-cache": "7.18.3", + "mime": "2.6.0" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/@verdaccio/logger-7/node_modules/real-require": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz", - "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12.13.0" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/verdaccio" } }, - "node_modules/@verdaccio/logger-7/node_modules/sonic-boom": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz", - "integrity": "sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==", + "node_modules/@verdaccio/middleware/node_modules/@verdaccio/utils": { + "version": "8.1.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", + "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", "dev": true, "license": "MIT", "dependencies": { - "atomic-sleep": "^1.0.0" + "@verdaccio/core": "8.0.0-next-8.3", + "lodash": "4.17.21", + "minimatch": "7.4.6", + "semver": "7.6.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/verdaccio" } }, - "node_modules/@verdaccio/logger-7/node_modules/thread-stream": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-0.15.2.tgz", - "integrity": "sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==", + "node_modules/@verdaccio/middleware/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", "dependencies": { - "real-require": "^0.1.0" + "balanced-match": "^1.0.0" } }, - "node_modules/@verdaccio/logger-commons": { - "version": "8.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/logger-commons/-/logger-commons-8.0.0-next-8.1.tgz", - "integrity": "sha512-jCge//RT4uaK7MarhpzcJeJ5Uvtu/DbJ1wvJQyGiFe+9AvxDGm3EUFXvawLFZ0lzYhmLt1nvm7kevcc3vOm2ZQ==", + "node_modules/@verdaccio/middleware/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true, "license": "MIT", - "dependencies": { - "@verdaccio/core": "8.0.0-next-8.1", - "@verdaccio/logger-prettify": "8.0.0-next-8.0", - "colorette": "2.0.20", - "debug": "4.3.7" - }, "engines": { - "node": ">=12" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/verdaccio" + "node": ">= 0.6" } }, - "node_modules/@verdaccio/logger-prettify": { - "version": "8.0.0-next-8.0", - "resolved": "https://registry.npmjs.org/@verdaccio/logger-prettify/-/logger-prettify-8.0.0-next-8.0.tgz", - "integrity": "sha512-7mAFHZF2NPTubrOXYp2+fbMjRW5MMWXMeS3LcpupMAn5uPp6jkKEM8NC4IVJEevC5Ph4vPVZqpoPDpgXHEaV3Q==", + "node_modules/@verdaccio/middleware/node_modules/express": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "dev": true, "license": "MIT", "dependencies": { - "colorette": "2.0.20", - "dayjs": "1.11.13", - "lodash": "4.17.21", - "pino-abstract-transport": "1.1.0", - "sonic-boom": "3.8.0" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">=12" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/verdaccio" + "node": ">= 0.10.0" } }, - "node_modules/@verdaccio/middleware": { - "version": "8.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/middleware/-/middleware-8.0.0-next-8.1.tgz", - "integrity": "sha512-GpAdJYky1WmOERpxPoCkVSwTTJIsVAjqf2a2uQNvi7R3UZhs059JKhWcZjJMVCGV0uz9xgQvtb3DEuYGHqyaOg==", + "node_modules/@verdaccio/middleware/node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "license": "MIT", "dependencies": { - "@verdaccio/config": "8.0.0-next-8.1", - "@verdaccio/core": "8.0.0-next-8.1", - "@verdaccio/url": "13.0.0-next-8.1", - "@verdaccio/utils": "7.0.1-next-8.1", - "debug": "4.3.7", - "express": "4.21.0", - "express-rate-limit": "5.5.1", - "lodash": "4.17.21", - "lru-cache": "7.18.3", - "mime": "2.6.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/verdaccio" + "ms": "2.0.0" } }, "node_modules/@verdaccio/middleware/node_modules/lru-cache": { @@ -18692,14 +18726,37 @@ "node": ">=4.0.0" } }, + "node_modules/@verdaccio/middleware/node_modules/minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@verdaccio/middleware/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, "node_modules/@verdaccio/search-indexer": { - "version": "8.0.0-next-8.0", - "resolved": "https://registry.npmjs.org/@verdaccio/search-indexer/-/search-indexer-8.0.0-next-8.0.tgz", - "integrity": "sha512-VS9axVt8XAueiPceVCgaj9nlvYj5s/T4MkAILSf2rVZeFFOMUyxU3mddUCajSHzL+YpqCuzLLL9865sRRzOJ9w==", + "version": "8.0.0-next-8.1", + "resolved": "https://registry.npmjs.org/@verdaccio/search-indexer/-/search-indexer-8.0.0-next-8.1.tgz", + "integrity": "sha512-Mwwg2o9GicZd6uiCbjBk6xZiWAH/O/2NbEkicPZINFIoJKy1NUihS4RexdDjcsxKEBEggGZXCkzHjzhfaZv1Gg==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "type": "opencollective", @@ -18707,9 +18764,9 @@ } }, "node_modules/@verdaccio/signature": { - "version": "8.0.0-next-8.0", - "resolved": "https://registry.npmjs.org/@verdaccio/signature/-/signature-8.0.0-next-8.0.tgz", - "integrity": "sha512-klcc2UlCvQxXDV65Qewo2rZOfv7S1y8NekS/8uurSaCTjU35T+fz+Pbqz1S9XK9oQlMp4vCQ7w3iMPWQbvphEQ==", + "version": "8.0.0-next-8.1", + "resolved": "https://registry.npmjs.org/@verdaccio/signature/-/signature-8.0.0-next-8.1.tgz", + "integrity": "sha512-lHD/Z2FoPQTtDYz6ZlXhj/lrg0SFirHrwCGt/cibl1GlePpx78WPdo03tgAyl0Qf+I35n484/gR1l9eixBQqYw==", "dev": true, "license": "MIT", "dependencies": { @@ -18717,7 +18774,7 @@ "jsonwebtoken": "9.0.2" }, "engines": { - "node": ">=14" + "node": ">=18" }, "funding": { "type": "opencollective", @@ -18740,73 +18797,158 @@ } }, "node_modules/@verdaccio/tarball": { - "version": "13.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/tarball/-/tarball-13.0.0-next-8.1.tgz", - "integrity": "sha512-58uimU2Bqt9+s+9ixy7wK/nPCqbOXhhhr/MQjl+otIlsUhSeATndhFzEctz/W+4MhUDg0tUnE9HC2yeNHHAo1Q==", + "version": "13.0.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/tarball/-/tarball-13.0.0-next-8.3.tgz", + "integrity": "sha512-jJatpGgiKLmTqyW4WlRpIkldd26rHDj5WTugWqa2Wxa1hVS0b6sZKT/9fEffOvjjAJv69Ued4nH1YCcB2hlhfA==", "dev": true, "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.1", - "@verdaccio/url": "13.0.0-next-8.1", - "@verdaccio/utils": "7.0.1-next-8.1", + "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/url": "13.0.0-next-8.3", + "@verdaccio/utils": "8.1.0-next-8.3", "debug": "4.3.7", "gunzip-maybe": "^1.4.2", "lodash": "4.17.21", "tar-stream": "^3.1.7" }, "engines": { - "node": ">=14" + "node": ">=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/verdaccio" } }, + "node_modules/@verdaccio/tarball/node_modules/@verdaccio/utils": { + "version": "8.1.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", + "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@verdaccio/core": "8.0.0-next-8.3", + "lodash": "4.17.21", + "minimatch": "7.4.6", + "semver": "7.6.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/verdaccio" + } + }, + "node_modules/@verdaccio/tarball/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@verdaccio/tarball/node_modules/minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@verdaccio/ui-theme": { - "version": "8.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-8.0.0-next-8.1.tgz", - "integrity": "sha512-9PxV8+jE2Tr+iy9DQW/bzny4YqOlW0mCZ9ct6jhcUW4GdfzU//gY2fBN/DDtQVmfbTy8smuj4Enyv5f0wCsnYg==", + "version": "8.0.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-8.0.0-next-8.3.tgz", + "integrity": "sha512-3A5v8bkvK5Bm+ERsZLYPv4vwo49dwfSy5a3WcAQgWde/oc6jytl/5XOZOaKX33mEiLj313k02j9ArhKuY1JjgA==", "dev": true, "license": "MIT" }, "node_modules/@verdaccio/url": { - "version": "13.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/url/-/url-13.0.0-next-8.1.tgz", - "integrity": "sha512-h6pkJf+YtogImKgOrmPP9UVG3p3gtb67gqkQU0bZnK+SEKQt6Rkek/QvtJ8MbmciagYS18bDhpI8DxqLHjDfZQ==", + "version": "13.0.0-next-8.3", + "resolved": "https://registry.npmjs.org/@verdaccio/url/-/url-13.0.0-next-8.3.tgz", + "integrity": "sha512-77BkY3j1d1ZPpmBodK2QlRwNP9tn/IneVry4RHX9j+1xNj4clvpn5rObFnVRGeYf2GPZrYZvIdzVP3BjXl0nkA==", "dev": true, "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.1", + "@verdaccio/core": "8.0.0-next-8.3", "debug": "4.3.7", "lodash": "4.17.21", "validator": "13.12.0" }, "engines": { - "node": ">=12" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/verdaccio" + } + }, + "node_modules/@verdaccio/utils": { + "version": "7.0.1-next-8.1", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-7.0.1-next-8.1.tgz", + "integrity": "sha512-cyJdRrVa+8CS7UuIQb3K3IJFjMe64v38tYiBnohSmhRbX7dX9IT3jWbjrwkqWh4KeS1CS6BYENrGG1evJ2ggrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@verdaccio/core": "8.0.0-next-8.1", + "lodash": "4.17.21", + "minimatch": "7.4.6", + "semver": "7.6.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/verdaccio" + } + }, + "node_modules/@verdaccio/utils/node_modules/@verdaccio/core": { + "version": "8.0.0-next-8.1", + "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-8.0.0-next-8.1.tgz", + "integrity": "sha512-kQRCB2wgXEh8H88G51eQgAFK9IxmnBtkQ8sY5FbmB6PbBkyHrbGcCp+2mtRqqo36j0W1VAlfM3XzoknMy6qQnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.17.1", + "core-js": "3.37.1", + "http-errors": "2.0.0", + "http-status-codes": "2.3.0", + "process-warning": "1.0.0", + "semver": "7.6.3" + }, + "engines": { + "node": ">=14" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/verdaccio" } }, - "node_modules/@verdaccio/utils": { - "version": "7.0.1-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-7.0.1-next-8.1.tgz", - "integrity": "sha512-cyJdRrVa+8CS7UuIQb3K3IJFjMe64v38tYiBnohSmhRbX7dX9IT3jWbjrwkqWh4KeS1CS6BYENrGG1evJ2ggrQ==", + "node_modules/@verdaccio/utils/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.1", - "lodash": "4.17.21", - "minimatch": "7.4.6", - "semver": "7.6.3" - }, - "engines": { - "node": ">=12" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/verdaccio" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/@verdaccio/utils/node_modules/brace-expansion": { @@ -18819,6 +18961,25 @@ "balanced-match": "^1.0.0" } }, + "node_modules/@verdaccio/utils/node_modules/core-js": { + "version": "3.37.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz", + "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/@verdaccio/utils/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/@verdaccio/utils/node_modules/minimatch": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", @@ -19351,9 +19512,9 @@ "license": "MIT" }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, "license": "MIT" }, @@ -19960,9 +20121,9 @@ } }, "node_modules/b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", "dev": true, "license": "Apache-2.0" }, @@ -20108,9 +20269,9 @@ "license": "MIT" }, "node_modules/bare-events": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", - "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", "dev": true, "license": "Apache-2.0", "optional": true @@ -20234,22 +20395,6 @@ "dev": true, "license": "MIT" }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", @@ -20986,9 +21131,9 @@ } }, "node_modules/clipanion": { - "version": "4.0.0-rc.3", - "resolved": "https://registry.npmjs.org/clipanion/-/clipanion-4.0.0-rc.3.tgz", - "integrity": "sha512-+rJOJMt2N6Oikgtfqmo/Duvme7uz3SIedL2b6ycgCztQMiTfr3aQh2DDyLHl+QUPClKMNpSg3gDJFvNQYIcq1g==", + "version": "4.0.0-rc.4", + "resolved": "https://registry.npmjs.org/clipanion/-/clipanion-4.0.0-rc.4.tgz", + "integrity": "sha512-CXkMQxU6s9GklO/1f714dkKBMu1lopS1WFF0B8o4AxPykR1hpozxSiUZ5ZUeBjfPgCWqbcNOtZVFhB8Lkfp1+Q==", "dev": true, "license": "MIT", "workspaces": [ @@ -21242,9 +21387,9 @@ "license": "MIT" }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "dev": true, "license": "MIT", "engines": { @@ -23361,9 +23506,9 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dev": true, "license": "MIT", "dependencies": { @@ -23372,7 +23517,7 @@ "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -23427,22 +23572,6 @@ "dev": true, "license": "MIT" }, - "node_modules/express/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -23871,18 +24000,18 @@ } }, "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.12" + "node": ">= 6" } }, "node_modules/formdata-polyfill": { @@ -24493,15 +24622,15 @@ } }, "node_modules/http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", "dev": true, "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^2.0.2", - "sshpk": "^1.14.1" + "sshpk": "^1.18.0" }, "engines": { "node": ">=0.10" @@ -26759,66 +26888,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/mv": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mkdirp": "~0.5.1", - "ncp": "~2.0.0", - "rimraf": "~2.4.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/mv/node_modules/glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mv/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mv/node_modules/rimraf": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^6.0.1" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/mysql2": { "version": "3.9.9", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.9.tgz", @@ -26900,16 +26969,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "license": "MIT" }, - "node_modules/ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", - "dev": true, - "license": "MIT", - "bin": { - "ncp": "bin/ncp" - } - }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -27864,32 +27923,32 @@ } }, "node_modules/pino": { - "version": "8.17.2", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.17.2.tgz", - "integrity": "sha512-LA6qKgeDMLr2ux2y/YiUt47EfgQ+S9LznBWOJdN3q1dx2sv0ziDLUBeVpyVv17TEcGCBuWf0zNtg3M5m1NhhWQ==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.4.0.tgz", + "integrity": "sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==", "dev": true, "license": "MIT", "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "v1.1.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^3.0.0", + "pino-abstract-transport": "^1.2.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^4.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.7.0", - "thread-stream": "^2.0.0" + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "node_modules/pino-abstract-transport": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz", - "integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -27981,19 +28040,29 @@ } }, "node_modules/pino-std-serializers": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", - "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", "dev": true, "license": "MIT" }, "node_modules/pino/node_modules/process-warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", - "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz", + "integrity": "sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==", "dev": true, "license": "MIT" }, + "node_modules/pino/node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "dev": true, + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/pkg-dir": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", @@ -28288,13 +28357,13 @@ } }, "node_modules/qs": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", - "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -29395,9 +29464,9 @@ } }, "node_modules/sonic-boom": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.0.tgz", - "integrity": "sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", + "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", "dev": true, "license": "MIT", "dependencies": { @@ -30118,14 +30187,11 @@ } }, "node_modules/text-decoder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.0.tgz", - "integrity": "sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz", + "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } + "license": "Apache-2.0" }, "node_modules/text-table": { "version": "0.2.0", @@ -30134,9 +30200,9 @@ "license": "MIT" }, "node_modules/thread-stream": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", - "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", "dev": true, "license": "MIT", "dependencies": { @@ -30924,33 +30990,33 @@ } }, "node_modules/verdaccio": { - "version": "5.32.2", - "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-5.32.2.tgz", - "integrity": "sha512-QnVYIUvwB884fwVcA/D+x7AabsRPlTPyYAKMtExm8kJjiH+s2LGK2qX2o3I4VmYXqBR3W9b8gEnyQnGwQhUPsw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-6.0.1.tgz", + "integrity": "sha512-fGP5V18Pz3yIDZNZjQrBqRnMr4sDn8fPO7eoZTIX2D7MvqoGELRG88Townq8PhYU5KAPZ2c2OnNRr8SKReO3Ag==", "dev": true, "license": "MIT", "dependencies": { - "@cypress/request": "3.0.1", - "@verdaccio/auth": "8.0.0-next-8.1", - "@verdaccio/config": "8.0.0-next-8.1", - "@verdaccio/core": "8.0.0-next-8.1", + "@cypress/request": "3.0.5", + "@verdaccio/auth": "8.0.0-next-8.3", + "@verdaccio/config": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.3", "@verdaccio/local-storage-legacy": "11.0.2", - "@verdaccio/logger-7": "8.0.0-next-8.1", - "@verdaccio/middleware": "8.0.0-next-8.1", - "@verdaccio/search-indexer": "8.0.0-next-8.0", - "@verdaccio/signature": "8.0.0-next-8.0", + "@verdaccio/logger": "8.0.0-next-8.3", + "@verdaccio/middleware": "8.0.0-next-8.3", + "@verdaccio/search-indexer": "8.0.0-next-8.1", + "@verdaccio/signature": "8.0.0-next-8.1", "@verdaccio/streams": "10.2.1", - "@verdaccio/tarball": "13.0.0-next-8.1", - "@verdaccio/ui-theme": "8.0.0-next-8.1", - "@verdaccio/url": "13.0.0-next-8.1", + "@verdaccio/tarball": "13.0.0-next-8.3", + "@verdaccio/ui-theme": "8.0.0-next-8.3", + "@verdaccio/url": "13.0.0-next-8.3", "@verdaccio/utils": "7.0.1-next-8.1", - "async": "3.2.5", - "clipanion": "4.0.0-rc.3", + "async": "3.2.6", + "clipanion": "4.0.0-rc.4", "compression": "1.7.4", "cors": "2.8.5", - "debug": "^4.3.5", - "envinfo": "7.13.0", - "express": "4.21.0", + "debug": "4.3.7", + "envinfo": "7.14.0", + "express": "4.21.1", "express-rate-limit": "5.5.1", "fast-safe-stringify": "2.1.1", "handlebars": "4.7.8", @@ -30962,18 +31028,17 @@ "lru-cache": "7.18.3", "mime": "3.0.0", "mkdirp": "1.0.4", - "mv": "2.1.1", "pkginfo": "0.4.1", "semver": "7.6.3", "validator": "13.12.0", - "verdaccio-audit": "13.0.0-next-8.1", - "verdaccio-htpasswd": "13.0.0-next-8.1" + "verdaccio-audit": "13.0.0-next-8.3", + "verdaccio-htpasswd": "13.0.0-next-8.3" }, "bin": { "verdaccio": "bin/verdaccio" }, "engines": { - "node": ">=14" + "node": ">=18" }, "funding": { "type": "opencollective", @@ -30981,35 +31046,105 @@ } }, "node_modules/verdaccio-audit": { - "version": "13.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-13.0.0-next-8.1.tgz", - "integrity": "sha512-EEfUeC1kHuErtwF9FC670W+EXHhcl+iuigONkcprwRfkPxmdBs+Hx36745hgAMZ9SCqedNECaycnGF3tZ3VYfw==", + "version": "13.0.0-next-8.3", + "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-13.0.0-next-8.3.tgz", + "integrity": "sha512-/mgRfsg+RENtUggcf0xnfPKNJJqidyKING3nyHgS3vABE6CBe4/fWQKs67X4mfCFiIVLf0PiOTFGT8tmwSZubA==", "dev": true, "license": "MIT", "dependencies": { - "@verdaccio/config": "8.0.0-next-8.1", - "@verdaccio/core": "8.0.0-next-8.1", + "@verdaccio/config": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.3", "express": "4.21.0", "https-proxy-agent": "5.0.1", "node-fetch": "cjs" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/verdaccio" } }, + "node_modules/verdaccio-audit/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/verdaccio-audit/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/verdaccio-audit/node_modules/express": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/verdaccio-audit/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, "node_modules/verdaccio-htpasswd": { - "version": "13.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-13.0.0-next-8.1.tgz", - "integrity": "sha512-BfvmO+ZdbwfttOwrdTPD6Bccr1ZfZ9Tk/9wpXamxdWB/XPWlk3FtyGsvqCmxsInRLPhQ/FSk9c3zRCGvICTFYg==", + "version": "13.0.0-next-8.3", + "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-13.0.0-next-8.3.tgz", + "integrity": "sha512-Nl2rEEyGHJQ/1/93BE9TxBMRN4tKF/51VYTb+hWDQFhDEDAR20rcvJ4ND0jOIIZljI7Lg/WrCPBh90u5IyPJ5Q==", "dev": true, "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.1", - "@verdaccio/file-locking": "13.0.0-next-8.0", + "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/file-locking": "13.0.0-next-8.1", "apache-md5": "1.1.8", "bcryptjs": "2.4.3", "core-js": "3.37.1", @@ -31018,7 +31153,7 @@ "unix-crypt-td-js": "1.1.4" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "type": "opencollective", @@ -31026,16 +31161,16 @@ } }, "node_modules/verdaccio-htpasswd/node_modules/@verdaccio/file-locking": { - "version": "13.0.0-next-8.0", - "resolved": "https://registry.npmjs.org/@verdaccio/file-locking/-/file-locking-13.0.0-next-8.0.tgz", - "integrity": "sha512-28XRwpKiE3Z6KsnwE7o8dEM+zGWOT+Vef7RVJyUlG176JVDbGGip3HfCmFioE1a9BklLyGEFTu6D69BzfbRkzA==", + "version": "13.0.0-next-8.1", + "resolved": "https://registry.npmjs.org/@verdaccio/file-locking/-/file-locking-13.0.0-next-8.1.tgz", + "integrity": "sha512-9PhfRKXsWaWJkON/2/jdG5/N+9Kk4UINvbMGuJz0A/PbzIYfVrBhry7fcnjn6hFKxVPTbSOSSztRzLF30nmBFg==", "dev": true, "license": "MIT", "dependencies": { "lockfile": "1.0.4" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "type": "opencollective", @@ -31061,19 +31196,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/verdaccio/node_modules/envinfo": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", - "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==", - "dev": true, - "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/verdaccio/node_modules/handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", diff --git a/package.json b/package.json index 877a54832b1..56f0ad37f5a 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "tsx": "^4.6.1", "typedoc": "^0.25.3", "typescript": "~5.2.0", - "verdaccio": "^5.24.1" + "verdaccio": "^6.0.1" }, "workspaces": [ "packages/*" From c4f7a5c93fc67dc3aab1cb073ef43afc7425631d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 16:22:11 -0700 Subject: [PATCH 078/199] Version Packages (#2165) Co-authored-by: github-actions[bot] --- .changeset/bright-jokes-draw.md | 6 ------ .changeset/curvy-pans-brake.md | 8 -------- .changeset/fresh-candles-leave.md | 5 ----- .changeset/long-toys-suffer.md | 5 ----- .changeset/lovely-fishes-cheer.md | 20 ------------------- .changeset/orange-nails-cross.md | 5 ----- .changeset/spicy-rules-speak.md | 2 -- .changeset/strong-camels-march.md | 2 -- .changeset/thirty-cheetahs-clap.md | 2 -- packages/ai-constructs/CHANGELOG.md | 13 ++++++++++++ packages/ai-constructs/package.json | 6 +++--- packages/auth-construct/CHANGELOG.md | 13 ++++++++++++ packages/auth-construct/package.json | 6 +++--- packages/backend-ai/CHANGELOG.md | 12 +++++++++++ packages/backend-ai/package.json | 8 ++++---- packages/backend-auth/CHANGELOG.md | 15 ++++++++++++++ packages/backend-auth/package.json | 10 +++++----- packages/backend-data/CHANGELOG.md | 9 +++++++++ packages/backend-data/package.json | 8 ++++---- packages/backend-deployer/CHANGELOG.md | 8 ++++++++ packages/backend-deployer/package.json | 4 ++-- packages/backend-function/CHANGELOG.md | 9 +++++++++ packages/backend-function/package.json | 8 ++++---- packages/backend-output-storage/CHANGELOG.md | 8 ++++++++ packages/backend-output-storage/package.json | 4 ++-- .../backend-platform-test-stubs/CHANGELOG.md | 8 ++++++++ .../backend-platform-test-stubs/package.json | 4 ++-- packages/backend-storage/CHANGELOG.md | 9 +++++++++ packages/backend-storage/package.json | 8 ++++---- packages/backend/CHANGELOG.md | 19 ++++++++++++++++++ packages/backend/package.json | 16 +++++++-------- packages/cli-core/CHANGELOG.md | 6 ++++++ packages/cli-core/package.json | 2 +- packages/cli/CHANGELOG.md | 18 +++++++++++++++++ packages/cli/package.json | 14 ++++++------- packages/client-config/CHANGELOG.md | 8 ++++++++ packages/client-config/package.json | 4 ++-- packages/integration-tests/CHANGELOG.md | 10 ++++++++++ packages/integration-tests/package.json | 14 ++++++------- packages/plugin-types/CHANGELOG.md | 6 ++++++ packages/plugin-types/package.json | 2 +- packages/sandbox/CHANGELOG.md | 12 +++++++++++ packages/sandbox/package.json | 10 +++++----- packages/schema-generator/CHANGELOG.md | 7 +++++++ packages/schema-generator/package.json | 2 +- 45 files changed, 255 insertions(+), 120 deletions(-) delete mode 100644 .changeset/bright-jokes-draw.md delete mode 100644 .changeset/curvy-pans-brake.md delete mode 100644 .changeset/fresh-candles-leave.md delete mode 100644 .changeset/long-toys-suffer.md delete mode 100644 .changeset/lovely-fishes-cheer.md delete mode 100644 .changeset/orange-nails-cross.md delete mode 100644 .changeset/spicy-rules-speak.md delete mode 100644 .changeset/strong-camels-march.md delete mode 100644 .changeset/thirty-cheetahs-clap.md diff --git a/.changeset/bright-jokes-draw.md b/.changeset/bright-jokes-draw.md deleted file mode 100644 index 9a3649dfde2..00000000000 --- a/.changeset/bright-jokes-draw.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/cli-core': minor -'@aws-amplify/backend-cli': minor ---- - -update ctrl+c behavior to always print guidance to delete and exit with no prompt diff --git a/.changeset/curvy-pans-brake.md b/.changeset/curvy-pans-brake.md deleted file mode 100644 index 6d6cc9dd7ae..00000000000 --- a/.changeset/curvy-pans-brake.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@aws-amplify/auth-construct': minor -'@aws-amplify/backend-auth': minor -'@aws-amplify/backend': minor -'@aws-amplify/integration-tests': minor ---- - -Add support for custom Lambda function email senders in Auth construct diff --git a/.changeset/fresh-candles-leave.md b/.changeset/fresh-candles-leave.md deleted file mode 100644 index 67c5f741d60..00000000000 --- a/.changeset/fresh-candles-leave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': minor ---- - -Propagate errors to AppSync diff --git a/.changeset/long-toys-suffer.md b/.changeset/long-toys-suffer.md deleted file mode 100644 index fe92fd87580..00000000000 --- a/.changeset/long-toys-suffer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': patch ---- - -Remove tool usage for non current turns when looking up message history diff --git a/.changeset/lovely-fishes-cheer.md b/.changeset/lovely-fishes-cheer.md deleted file mode 100644 index 34797dbcdba..00000000000 --- a/.changeset/lovely-fishes-cheer.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -'@aws-amplify/backend-platform-test-stubs': patch -'@aws-amplify/backend-output-storage': patch -'@aws-amplify/integration-tests': patch -'@aws-amplify/backend-deployer': patch -'@aws-amplify/backend-function': patch -'@aws-amplify/schema-generator': patch -'@aws-amplify/backend-storage': patch -'@aws-amplify/auth-construct': patch -'@aws-amplify/ai-constructs': patch -'@aws-amplify/client-config': patch -'@aws-amplify/backend-auth': patch -'@aws-amplify/backend-data': patch -'@aws-amplify/plugin-types': patch -'@aws-amplify/backend-ai': patch -'@aws-amplify/backend': patch -'@aws-amplify/sandbox': patch ---- - -update aws-cdk lib to ^2.158.0 diff --git a/.changeset/orange-nails-cross.md b/.changeset/orange-nails-cross.md deleted file mode 100644 index abae0c0d0bf..00000000000 --- a/.changeset/orange-nails-cross.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/schema-generator': patch ---- - -Upgrade @aws-amplify/graphql-schema-generator to v0.11.0 diff --git a/.changeset/spicy-rules-speak.md b/.changeset/spicy-rules-speak.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/spicy-rules-speak.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/strong-camels-march.md b/.changeset/strong-camels-march.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/strong-camels-march.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/thirty-cheetahs-clap.md b/.changeset/thirty-cheetahs-clap.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/thirty-cheetahs-clap.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index ad27b99ebb4..1d965443802 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/ai-constructs +## 0.8.0 + +### Minor Changes + +- 37dd87c: Propagate errors to AppSync + +### Patch Changes + +- 613bca9: Remove tool usage for non current turns when looking up message history +- b56d344: update aws-cdk lib to ^2.158.0 +- Updated dependencies [b56d344] + - @aws-amplify/plugin-types@1.3.1 + ## 0.7.0 ### Minor Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index 3f33f4089a4..dff30ea297d 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "0.7.0", + "version": "0.8.0", "type": "commonjs", "publishConfig": { "access": "public" @@ -28,13 +28,13 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.0.1", + "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/client-bedrock-runtime": "^3.622.0", "@smithy/types": "^3.3.0", "json-schema-to-ts": "^3.1.1" }, "devDependencies": { - "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/backend-output-storage": "^1.1.3", "typescript": "^5.0.0" }, "peerDependencies": { diff --git a/packages/auth-construct/CHANGELOG.md b/packages/auth-construct/CHANGELOG.md index 0131feac702..50e25ead8dc 100644 --- a/packages/auth-construct/CHANGELOG.md +++ b/packages/auth-construct/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/auth-construct +## 1.4.0 + +### Minor Changes + +- 11d62fe: Add support for custom Lambda function email senders in Auth construct + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 +- Updated dependencies [b56d344] + - @aws-amplify/backend-output-storage@1.1.3 + - @aws-amplify/plugin-types@1.3.1 + ## 1.3.2 ### Patch Changes diff --git a/packages/auth-construct/package.json b/packages/auth-construct/package.json index d3e121ee4d3..d24130f5ab6 100644 --- a/packages/auth-construct/package.json +++ b/packages/auth-construct/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/auth-construct", - "version": "1.3.2", + "version": "1.4.0", "type": "commonjs", "publishConfig": { "access": "public" @@ -20,8 +20,8 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/util-arn-parser": "^3.568.0" }, "peerDependencies": { diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index e1763c6149a..9f8a59c7591 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/backend-ai +## 0.3.5 + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 +- Updated dependencies [37dd87c] +- Updated dependencies [613bca9] +- Updated dependencies [b56d344] + - @aws-amplify/ai-constructs@0.8.0 + - @aws-amplify/backend-output-storage@1.1.3 + - @aws-amplify/plugin-types@1.3.1 + ## 0.3.4 ### Patch Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 1a254af2974..2b4f5de6918 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "0.3.4", + "version": "0.3.5", "type": "module", "publishConfig": { "access": "public" @@ -22,12 +22,12 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.7.0", + "@aws-amplify/ai-constructs": "^0.8.0", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.0.2", + "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.0.1" + "@aws-amplify/plugin-types": "^1.3.1" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0", diff --git a/packages/backend-auth/CHANGELOG.md b/packages/backend-auth/CHANGELOG.md index 8aa5b2b4a36..9359e6b29e9 100644 --- a/packages/backend-auth/CHANGELOG.md +++ b/packages/backend-auth/CHANGELOG.md @@ -1,5 +1,20 @@ # @aws-amplify/backend-auth +## 1.3.0 + +### Minor Changes + +- 11d62fe: Add support for custom Lambda function email senders in Auth construct + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 +- Updated dependencies [11d62fe] +- Updated dependencies [b56d344] + - @aws-amplify/auth-construct@1.4.0 + - @aws-amplify/backend-output-storage@1.1.3 + - @aws-amplify/plugin-types@1.3.1 + ## 1.2.0 ### Minor Changes diff --git a/packages/backend-auth/package.json b/packages/backend-auth/package.json index 89d71470380..dd5dab4e33d 100644 --- a/packages/backend-auth/package.json +++ b/packages/backend-auth/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-auth", - "version": "1.2.0", + "version": "1.3.0", "type": "module", "publishConfig": { "access": "public" @@ -19,12 +19,12 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/plugin-types": "^1.3.0" + "@aws-amplify/auth-construct": "^1.4.0", + "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/plugin-types": "^1.3.1" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.5", + "@aws-amplify/backend-platform-test-stubs": "^0.3.6", "@aws-amplify/platform-core": "^1.0.6" }, "peerDependencies": { diff --git a/packages/backend-data/CHANGELOG.md b/packages/backend-data/CHANGELOG.md index 9696801d2e8..8b9078ce797 100644 --- a/packages/backend-data/CHANGELOG.md +++ b/packages/backend-data/CHANGELOG.md @@ -1,5 +1,14 @@ # @aws-amplify/backend-data +## 1.1.6 + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 +- Updated dependencies [b56d344] + - @aws-amplify/backend-output-storage@1.1.3 + - @aws-amplify/plugin-types@1.3.1 + ## 1.1.5 ### Patch Changes diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index b9c14d8db9b..bf836021cc6 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-data", - "version": "1.1.5", + "version": "1.1.6", "type": "module", "publishConfig": { "access": "public" @@ -20,7 +20,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-amplify/data-schema": "^1.0.0", - "@aws-amplify/backend-platform-test-stubs": "^0.3.5", + "@aws-amplify/backend-platform-test-stubs": "^0.3.6", "@aws-amplify/platform-core": "^1.0.7" }, "peerDependencies": { @@ -28,10 +28,10 @@ "constructs": "^10.0.0" }, "dependencies": { - "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/data-construct": "^1.10.1", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/plugin-types": "^1.3.1", "@aws-amplify/data-schema-types": "^1.2.0" } } diff --git a/packages/backend-deployer/CHANGELOG.md b/packages/backend-deployer/CHANGELOG.md index 6d5d1c843b4..24861f0c55d 100644 --- a/packages/backend-deployer/CHANGELOG.md +++ b/packages/backend-deployer/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-deployer +## 1.1.6 + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 +- Updated dependencies [b56d344] + - @aws-amplify/plugin-types@1.3.1 + ## 1.1.5 ### Patch Changes diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index 07573125589..30cf07f86be 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-deployer", - "version": "1.1.5", + "version": "1.1.6", "type": "module", "publishConfig": { "access": "public" @@ -20,7 +20,7 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/plugin-types": "^1.3.1", "execa": "^8.0.1", "tsx": "^4.6.1" }, diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index 9eb523919d4..f80ea88b9a1 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,14 @@ # @aws-amplify/backend-function +## 1.7.3 + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 +- Updated dependencies [b56d344] + - @aws-amplify/backend-output-storage@1.1.3 + - @aws-amplify/plugin-types@1.3.1 + ## 1.7.2 ### Patch Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index eee31585122..23cb901821a 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.7.2", + "version": "1.7.3", "type": "module", "publishConfig": { "access": "public" @@ -20,12 +20,12 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/plugin-types": "^1.3.0", + "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/plugin-types": "^1.3.1", "execa": "^8.0.1" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.5", + "@aws-amplify/backend-platform-test-stubs": "^0.3.6", "@aws-amplify/platform-core": "^1.1.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", diff --git a/packages/backend-output-storage/CHANGELOG.md b/packages/backend-output-storage/CHANGELOG.md index e8b0e9f9a58..89eac4993f0 100644 --- a/packages/backend-output-storage/CHANGELOG.md +++ b/packages/backend-output-storage/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-output-storage +## 1.1.3 + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 +- Updated dependencies [b56d344] + - @aws-amplify/plugin-types@1.3.1 + ## 1.1.2 ### Patch Changes diff --git a/packages/backend-output-storage/package.json b/packages/backend-output-storage/package.json index f593321058f..839a1351da5 100644 --- a/packages/backend-output-storage/package.json +++ b/packages/backend-output-storage/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-output-storage", - "version": "1.1.2", + "version": "1.1.3", "type": "commonjs", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.2.2" + "@aws-amplify/plugin-types": "^1.3.1" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0" diff --git a/packages/backend-platform-test-stubs/CHANGELOG.md b/packages/backend-platform-test-stubs/CHANGELOG.md index 0b73fa2b05a..436f8411ba5 100644 --- a/packages/backend-platform-test-stubs/CHANGELOG.md +++ b/packages/backend-platform-test-stubs/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-platform-test-stubs +## 0.3.6 + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 +- Updated dependencies [b56d344] + - @aws-amplify/plugin-types@1.3.1 + ## 0.3.5 ### Patch Changes diff --git a/packages/backend-platform-test-stubs/package.json b/packages/backend-platform-test-stubs/package.json index a395b969db2..4d8ab06e8fd 100644 --- a/packages/backend-platform-test-stubs/package.json +++ b/packages/backend-platform-test-stubs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-platform-test-stubs", - "version": "0.3.5", + "version": "0.3.6", "type": "module", "private": true, "exports": { @@ -16,7 +16,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/plugin-types": "^1.3.1", "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } diff --git a/packages/backend-storage/CHANGELOG.md b/packages/backend-storage/CHANGELOG.md index e5eb78f3b45..c164598d795 100644 --- a/packages/backend-storage/CHANGELOG.md +++ b/packages/backend-storage/CHANGELOG.md @@ -1,5 +1,14 @@ # @aws-amplify/backend-storage +## 1.2.2 + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 +- Updated dependencies [b56d344] + - @aws-amplify/backend-output-storage@1.1.3 + - @aws-amplify/plugin-types@1.3.1 + ## 1.2.1 ### Patch Changes diff --git a/packages/backend-storage/package.json b/packages/backend-storage/package.json index 334997595b6..e6f193aedce 100644 --- a/packages/backend-storage/package.json +++ b/packages/backend-storage/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-storage", - "version": "1.2.1", + "version": "1.2.2", "type": "module", "publishConfig": { "access": "public" @@ -20,11 +20,11 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.1", - "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/plugin-types": "^1.3.0" + "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/plugin-types": "^1.3.1" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.5", + "@aws-amplify/backend-platform-test-stubs": "^0.3.6", "@aws-amplify/platform-core": "^1.0.6" }, "peerDependencies": { diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 271e7ff53a4..27dc519f0c1 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,24 @@ # @aws-amplify/backend +## 1.6.0 + +### Minor Changes + +- 11d62fe: Add support for custom Lambda function email senders in Auth construct + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 +- Updated dependencies [11d62fe] +- Updated dependencies [b56d344] + - @aws-amplify/backend-auth@1.3.0 + - @aws-amplify/backend-output-storage@1.1.3 + - @aws-amplify/backend-function@1.7.3 + - @aws-amplify/backend-storage@1.2.2 + - @aws-amplify/client-config@1.5.1 + - @aws-amplify/backend-data@1.1.6 + - @aws-amplify/plugin-types@1.3.1 + ## 1.5.2 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index c3bd1a6d13b..e325f118ed1 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.5.2", + "version": "1.6.0", "type": "module", "publishConfig": { "access": "public" @@ -26,16 +26,16 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/data-schema": "^1.0.0", - "@aws-amplify/backend-auth": "^1.2.0", - "@aws-amplify/backend-function": "^1.7.2", - "@aws-amplify/backend-data": "^1.1.5", + "@aws-amplify/backend-auth": "^1.3.0", + "@aws-amplify/backend-function": "^1.7.3", + "@aws-amplify/backend-data": "^1.1.6", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/backend-secret": "^1.1.4", - "@aws-amplify/backend-storage": "^1.2.1", - "@aws-amplify/client-config": "^1.5.0", + "@aws-amplify/backend-storage": "^1.2.2", + "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.3.0", + "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/client-amplify": "^3.624.0", "lodash.snakecase": "^4.1.1" }, diff --git a/packages/cli-core/CHANGELOG.md b/packages/cli-core/CHANGELOG.md index 6aefdbed094..290f6fb5c1a 100644 --- a/packages/cli-core/CHANGELOG.md +++ b/packages/cli-core/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/cli-core +## 1.2.0 + +### Minor Changes + +- c3c3057: update ctrl+c behavior to always print guidance to delete and exit with no prompt + ## 1.1.3 ### Patch Changes diff --git a/packages/cli-core/package.json b/packages/cli-core/package.json index 61dd48e7121..0d1e6f022da 100644 --- a/packages/cli-core/package.json +++ b/packages/cli-core/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/cli-core", - "version": "1.1.3", + "version": "1.2.0", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 33b90df4827..c4c381a164f 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,23 @@ # @aws-amplify/backend-cli +## 1.4.0 + +### Minor Changes + +- c3c3057: update ctrl+c behavior to always print guidance to delete and exit with no prompt + +### Patch Changes + +- Updated dependencies [c3c3057] +- Updated dependencies [b56d344] +- Updated dependencies [b56d344] + - @aws-amplify/cli-core@1.2.0 + - @aws-amplify/backend-deployer@1.1.6 + - @aws-amplify/schema-generator@1.2.5 + - @aws-amplify/client-config@1.5.1 + - @aws-amplify/plugin-types@1.3.1 + - @aws-amplify/sandbox@1.2.4 + ## 1.3.0 ### Minor Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index e95347875a0..19803d2a722 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-cli", - "version": "1.3.0", + "version": "1.4.0", "description": "Command line interface for various Amplify tools", "bin": { "ampx": "lib/ampx.js", @@ -31,18 +31,18 @@ }, "homepage": "https://github.com/aws-amplify/amplify-backend#readme", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.3", + "@aws-amplify/backend-deployer": "^1.1.6", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/cli-core": "^1.1.3", - "@aws-amplify/client-config": "^1.5.0", + "@aws-amplify/cli-core": "^1.2.0", + "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/form-generator": "^1.0.3", "@aws-amplify/model-generator": "^1.0.8", "@aws-amplify/platform-core": "^1.0.5", - "@aws-amplify/plugin-types": "^1.3.0", - "@aws-amplify/sandbox": "^1.2.2", - "@aws-amplify/schema-generator": "^1.2.4", + "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/sandbox": "^1.2.4", + "@aws-amplify/schema-generator": "^1.2.5", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", diff --git a/packages/client-config/CHANGELOG.md b/packages/client-config/CHANGELOG.md index 120924601a3..571511bbdbc 100644 --- a/packages/client-config/CHANGELOG.md +++ b/packages/client-config/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/client-config +## 1.5.1 + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 +- Updated dependencies [b56d344] + - @aws-amplify/plugin-types@1.3.1 + ## 1.5.0 ### Minor Changes diff --git a/packages/client-config/package.json b/packages/client-config/package.json index 95e97b8e919..1a1afa44978 100644 --- a/packages/client-config/package.json +++ b/packages/client-config/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/client-config", - "version": "1.5.0", + "version": "1.5.1", "type": "module", "publishConfig": { "access": "public" @@ -28,7 +28,7 @@ "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/model-generator": "^1.0.7", "@aws-amplify/platform-core": "^1.0.7", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/plugin-types": "^1.3.1", "zod": "^3.22.2" }, "devDependencies": { diff --git a/packages/integration-tests/CHANGELOG.md b/packages/integration-tests/CHANGELOG.md index 24616d36960..e06cf49a9d7 100644 --- a/packages/integration-tests/CHANGELOG.md +++ b/packages/integration-tests/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/integration-tests +## 0.6.0 + +### Minor Changes + +- 11d62fe: Add support for custom Lambda function email senders in Auth construct + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 + ## 0.5.10 ### Patch Changes diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index aaabea803ea..ce7b456a1c3 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -1,20 +1,20 @@ { "name": "@aws-amplify/integration-tests", "private": true, - "version": "0.5.10", + "version": "0.6.0", "type": "module", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.7.0", - "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend": "^1.5.2", - "@aws-amplify/backend-ai": "^0.3.4", + "@aws-amplify/ai-constructs": "^0.8.0", + "@aws-amplify/auth-construct": "^1.4.0", + "@aws-amplify/backend": "^1.6.0", + "@aws-amplify/backend-ai": "^0.3.5", "@aws-amplify/backend-secret": "^1.1.4", - "@aws-amplify/client-config": "^1.4.0", + "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/client-accessanalyzer": "^3.624.0", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-bedrock-runtime": "^3.622.0", diff --git a/packages/plugin-types/CHANGELOG.md b/packages/plugin-types/CHANGELOG.md index 552d3102783..3b997236490 100644 --- a/packages/plugin-types/CHANGELOG.md +++ b/packages/plugin-types/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/plugin-types +## 1.3.1 + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/plugin-types/package.json b/packages/plugin-types/package.json index 8474a4b93fb..65404568f2f 100644 --- a/packages/plugin-types/package.json +++ b/packages/plugin-types/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/plugin-types", - "version": "1.3.0", + "version": "1.3.1", "types": "lib/index.d.ts", "type": "commonjs", "publishConfig": { diff --git a/packages/sandbox/CHANGELOG.md b/packages/sandbox/CHANGELOG.md index 8ba88ce39b3..8c047d66548 100644 --- a/packages/sandbox/CHANGELOG.md +++ b/packages/sandbox/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/sandbox +## 1.2.4 + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 +- Updated dependencies [c3c3057] +- Updated dependencies [b56d344] + - @aws-amplify/cli-core@1.2.0 + - @aws-amplify/backend-deployer@1.1.6 + - @aws-amplify/client-config@1.5.1 + - @aws-amplify/plugin-types@1.3.1 + ## 1.2.3 ### Patch Changes diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index f334a6f66c4..b28e802d6d8 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/sandbox", - "version": "1.2.3", + "version": "1.2.4", "type": "module", "publishConfig": { "access": "public" @@ -19,13 +19,13 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.3", + "@aws-amplify/backend-deployer": "^1.1.6", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/cli-core": "^1.1.3", - "@aws-amplify/client-config": "^1.3.1", + "@aws-amplify/cli-core": "^1.2.0", + "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", diff --git a/packages/schema-generator/CHANGELOG.md b/packages/schema-generator/CHANGELOG.md index f74d158e93e..488a2cbb719 100644 --- a/packages/schema-generator/CHANGELOG.md +++ b/packages/schema-generator/CHANGELOG.md @@ -1,5 +1,12 @@ # @aws-amplify/schema-generator +## 1.2.5 + +### Patch Changes + +- b56d344: update aws-cdk lib to ^2.158.0 +- b56d344: Upgrade @aws-amplify/graphql-schema-generator to v0.11.0 + ## 1.2.4 ### Patch Changes diff --git a/packages/schema-generator/package.json b/packages/schema-generator/package.json index a6b924d9a83..f860dbc5628 100644 --- a/packages/schema-generator/package.json +++ b/packages/schema-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/schema-generator", - "version": "1.2.4", + "version": "1.2.5", "type": "module", "publishConfig": { "access": "public" From a191fe504ade0fc2e1ca3ff1e2524f80ab2fa689 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 1 Nov 2024 10:10:42 -0700 Subject: [PATCH 079/199] add stack is in a state and can not be updated to error mapper (#2180) --- .changeset/funny-papayas-invent.md | 5 +++++ packages/backend-deployer/src/cdk_error_mapper.test.ts | 7 +++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 10 ++++++++++ 3 files changed, 22 insertions(+) create mode 100644 .changeset/funny-papayas-invent.md diff --git a/.changeset/funny-papayas-invent.md b/.changeset/funny-papayas-invent.md new file mode 100644 index 00000000000..1c6c23ba874 --- /dev/null +++ b/.changeset/funny-papayas-invent.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +add stack is in a state and can not be updated to error mapper diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 2976686ed18..555342a3e93 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -330,6 +330,13 @@ const testErrorMappings = [ expectedDownstreamErrorMessage: `This CDK CLI is not compatible with the CDK library used by your application. Please upgrade the CLI to the latest version. (Cloud assembly schema version mismatch: Maximum schema version supported is 36.0.0, but found 36.1.1)`, }, + { + errorMessage: `[31m amplify-some-stack failed: ValidationError: Stack:stack-arn is in UPDATE_ROLLBACK_FAILED state and can not be updated.`, + expectedTopLevelErrorMessage: + 'The CloudFormation deployment failed due to amplify-some-stack being in UPDATE_ROLLBACK_FAILED state.', + errorName: 'CloudFormationDeploymentError', + expectedDownstreamErrorMessage: undefined, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 816cc4b6f18..1b9ff691bcb 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -281,6 +281,16 @@ export class CdkErrorMapper { errorName: 'SecretNotSetError', classification: 'ERROR', }, + { + errorRegex: + /(?amplify-[a-z0-9-]+)(.*) failed: ValidationError: Stack:(.*) is in (?.*) state and can not be updated/, + humanReadableErrorMessage: + 'The CloudFormation deployment failed due to {stackName} being in {state} state.', + resolutionMessage: + 'Find more information in the CloudFormation AWS Console for this stack.', + errorName: 'CloudFormationDeploymentError', + classification: 'ERROR', + }, { // Note that the order matters, this should be the last as it captures generic CFN error errorRegex: new RegExp( From 255ca188d816668adea1ca6f366eb8505d90c11d Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Fri, 1 Nov 2024 13:35:24 -0700 Subject: [PATCH 080/199] Handle parameter not found error while deleting secret (#2186) * Handle parameter not found error while deleting secret * remove if --- .changeset/tidy-chicken-notice.md | 5 ++++ ...secret_with_amplify_error_handling.test.ts | 25 +++++++++++++++++++ .../ssm_secret_with_amplify_error_handling.ts | 9 ++++--- 3 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 .changeset/tidy-chicken-notice.md diff --git a/.changeset/tidy-chicken-notice.md b/.changeset/tidy-chicken-notice.md new file mode 100644 index 00000000000..28178599d01 --- /dev/null +++ b/.changeset/tidy-chicken-notice.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-secret': patch +--- + +Handle parameter not found error while deleting secret diff --git a/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.test.ts b/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.test.ts index f85d327d780..88c29dd5981 100644 --- a/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.test.ts +++ b/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.test.ts @@ -89,6 +89,31 @@ void describe('getSecretClientWithAmplifyErrorHandling', () => { ); }); + void it('throws AmplifyUserError if removeSecret fails due to ParameterNotFound error', async (context) => { + const notFoundError = new Error('Parameter not found error'); + notFoundError.name = 'ParameterNotFound'; + const secretsError = SecretError.createInstance(notFoundError); + context.mock.method(rawSecretClient, 'removeSecret', () => { + throw secretsError; + }); + const secretName = 'testSecretName'; + await assert.rejects( + () => + classUnderTest.removeSecret( + { + namespace: 'testSandboxId', + name: 'testSandboxName', + type: 'sandbox', + }, + secretName + ), + new AmplifyUserError('SSMParameterNotFoundError', { + message: `Failed to remove ${secretName} secret. ParameterNotFound: Parameter not found error`, + resolution: `Make sure that ${secretName} has been set. See https://docs.amplify.aws/react/deploy-and-host/fullstack-branching/secrets-and-vars/.`, + }) + ); + }); + void it('throws AmplifyFault if listSecrets fails due to a non-SSM exception other than expired credentials', async (context) => { const underlyingError = new Error('some secret error'); const secretsError = SecretError.createInstance(underlyingError); diff --git a/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.ts b/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.ts index be3d4801aea..7d18d1c4891 100644 --- a/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.ts +++ b/packages/backend-secret/src/ssm_secret_with_amplify_error_handling.ts @@ -69,7 +69,7 @@ export class SSMSecretClientWithAmplifyErrorHandling implements SecretClient { secretName ); } catch (e) { - throw this.translateToAmplifyError(e, 'Remove'); + throw this.translateToAmplifyError(e, 'Remove', { name: secretName }); } }; @@ -87,6 +87,7 @@ export class SSMSecretClientWithAmplifyErrorHandling implements SecretClient { 'ExpiredTokenException', 'ExpiredToken', 'CredentialsProviderError', + 'IncompleteSignatureException', 'InvalidSignatureException', ].includes(error.cause.name) ) { @@ -100,11 +101,13 @@ export class SSMSecretClientWithAmplifyErrorHandling implements SecretClient { } if ( error.cause.name === 'ParameterNotFound' && - apiName === 'Get' && + (apiName === 'Get' || apiName === 'Remove') && secretIdentifier ) { return new AmplifyUserError('SSMParameterNotFoundError', { - message: `Failed to get ${secretIdentifier.name} secret. ${error.cause.name}: ${error.cause?.message}`, + message: `Failed to ${apiName.toLowerCase()} ${ + secretIdentifier.name + } secret. ${error.cause.name}: ${error.cause?.message}`, resolution: `Make sure that ${secretIdentifier.name} has been set. See https://docs.amplify.aws/react/deploy-and-host/fullstack-branching/secrets-and-vars/.`, }); } From b9575eb9740c1e7c1cad41375ebbc1df6de6891d Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 1 Nov 2024 14:40:50 -0700 Subject: [PATCH 081/199] avoid client config generation race condition (#2184) * avoid client config generation race condition * remove rejectCleanupSandbox action * add changeset * try this * update deployment test template --- .changeset/tall-singers-rest.md | 2 ++ .../predicated_action_macros.ts | 15 +++------------ .../deployment/deployment.test.template.ts | 12 +++++------- .../src/test-e2e/sandbox/sandbox.test.template.ts | 6 +----- .../health_checks.test.ts | 2 -- .../src/test-project-setup/test_project_base.ts | 2 -- 6 files changed, 11 insertions(+), 28 deletions(-) create mode 100644 .changeset/tall-singers-rest.md diff --git a/.changeset/tall-singers-rest.md b/.changeset/tall-singers-rest.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/tall-singers-rest.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/process-controller/predicated_action_macros.ts b/packages/integration-tests/src/process-controller/predicated_action_macros.ts index 7f38d4789b6..4fff93a7265 100644 --- a/packages/integration-tests/src/process-controller/predicated_action_macros.ts +++ b/packages/integration-tests/src/process-controller/predicated_action_macros.ts @@ -40,16 +40,6 @@ export const confirmDeleteSandbox = () => ) .sendYes(); -/** - * Reusable predicated action: Wait for sandbox to prompt on quitting to delete all the resource and respond with no - */ -export const rejectCleanupSandbox = () => - new PredicatedActionBuilder() - .waitForLineIncludes( - 'Would you like to delete all the resources in your sandbox environment' - ) - .sendNo(); - /** * Reusable predicated action: Wait for sandbox to become idle, * then perform the specified file replacements in the backend code which will trigger sandbox again @@ -59,9 +49,10 @@ export const replaceFiles = (replacements: CopyDefinition[]) => { }; /** - * Reusable predicated action: Wait for sandbox to become idle and then quit it (CTRL-C) + * Reusable predicated action: Wait for sandbox to become idle and config to be generated and then quit it (CTRL-C) */ -export const interruptSandbox = () => waitForSandboxToBecomeIdle().sendCtrlC(); +export const interruptSandbox = () => + waitForConfigUpdateAfterDeployment().sendCtrlC(); /** * Reusable predicated action: Wait for sandbox to finish deployment and assert that the deployment time is less diff --git a/packages/integration-tests/src/test-e2e/deployment/deployment.test.template.ts b/packages/integration-tests/src/test-e2e/deployment/deployment.test.template.ts index 5c03df72827..4a17afb9b05 100644 --- a/packages/integration-tests/src/test-e2e/deployment/deployment.test.template.ts +++ b/packages/integration-tests/src/test-e2e/deployment/deployment.test.template.ts @@ -11,10 +11,7 @@ import { TestProjectBase } from '../../test-project-setup/test_project_base.js'; import { PredicatedActionBuilder } from '../../process-controller/predicated_action_queue_builder.js'; import { ampxCli } from '../../process-controller/process_controller.js'; import path from 'path'; -import { - interruptSandbox, - rejectCleanupSandbox, -} from '../../process-controller/predicated_action_macros.js'; +import { waitForSandboxToBecomeIdle } from '../../process-controller/predicated_action_macros.js'; import assert from 'node:assert'; import { TestBranch, amplifyAppPool } from '../../amplify_app_pool.js'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; @@ -127,17 +124,18 @@ export const defineDeploymentTest = ( void describe('in sequence', { concurrency: false }, () => { void it('in sandbox deploy', async () => { + const predicatedActionBuilder = new PredicatedActionBuilder(); await ampxCli( ['sandbox', '--dirToWatch', 'amplify'], testProject.projectDirPath ) .do( - new PredicatedActionBuilder().waitForLineIncludes( + predicatedActionBuilder.waitForLineIncludes( 'TypeScript validation check failed' ) ) - .do(interruptSandbox()) - .do(rejectCleanupSandbox()) + .do(waitForSandboxToBecomeIdle()) + .do(predicatedActionBuilder.sendCtrlC()) .run(); }); diff --git a/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts b/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts index cac7decd9ac..9b24b0926d6 100644 --- a/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts +++ b/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts @@ -11,7 +11,6 @@ import { ampxCli } from '../../process-controller/process_controller.js'; import { ensureDeploymentTimeLessThan, interruptSandbox, - rejectCleanupSandbox, replaceFiles, waitForConfigUpdateAfterDeployment, } from '../../process-controller/predicated_action_macros.js'; @@ -98,10 +97,7 @@ export const defineSandboxTest = (testProjectCreator: TestProjectCreator) => { } // Execute the process. - await processController - .do(interruptSandbox()) - .do(rejectCleanupSandbox()) - .run(); + await processController.do(interruptSandbox()).run(); await testProject.assertPostDeployment(sandboxBackendIdentifier); } diff --git a/packages/integration-tests/src/test-live-dependency-health-checks/health_checks.test.ts b/packages/integration-tests/src/test-live-dependency-health-checks/health_checks.test.ts index 8aae8bd8e65..19b475a9177 100644 --- a/packages/integration-tests/src/test-live-dependency-health-checks/health_checks.test.ts +++ b/packages/integration-tests/src/test-live-dependency-health-checks/health_checks.test.ts @@ -14,7 +14,6 @@ import { import { confirmDeleteSandbox, interruptSandbox, - rejectCleanupSandbox, waitForSandboxDeploymentToPrintTotalTime, } from '../process-controller/predicated_action_macros.js'; import { BackendIdentifierConversions } from '@aws-amplify/platform-core'; @@ -123,7 +122,6 @@ void describe('Live dependency health checks', { concurrency: true }, () => { await ampxCli(['sandbox'], tempDir) .do(waitForSandboxDeploymentToPrintTotalTime()) .do(interruptSandbox()) - .do(rejectCleanupSandbox()) .run(); const clientConfigStats = await fs.stat( diff --git a/packages/integration-tests/src/test-project-setup/test_project_base.ts b/packages/integration-tests/src/test-project-setup/test_project_base.ts index c6ab0284fef..1b1650a5b38 100644 --- a/packages/integration-tests/src/test-project-setup/test_project_base.ts +++ b/packages/integration-tests/src/test-project-setup/test_project_base.ts @@ -9,7 +9,6 @@ import { ampxCli } from '../process-controller/process_controller.js'; import { confirmDeleteSandbox, interruptSandbox, - rejectCleanupSandbox, waitForSandboxDeploymentToPrintTotalTime, } from '../process-controller/predicated_action_macros.js'; @@ -77,7 +76,6 @@ export abstract class TestProjectBase { }) .do(waitForSandboxDeploymentToPrintTotalTime()) .do(interruptSandbox()) - .do(rejectCleanupSandbox()) .run(); } else { await ampxCli( From 7bf0c64ea43a01e220109d30e98a353864457bf7 Mon Sep 17 00:00:00 2001 From: vigy02 Date: Fri, 1 Nov 2024 15:14:03 -0700 Subject: [PATCH 082/199] fix(backend-deployer): improve handling of expired AWS credentials (#2185) --- .changeset/cool-pumpkins-dream.md | 5 +++++ packages/backend-deployer/src/cdk_error_mapper.test.ts | 9 +++++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 6 ++++-- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 .changeset/cool-pumpkins-dream.md diff --git a/.changeset/cool-pumpkins-dream.md b/.changeset/cool-pumpkins-dream.md new file mode 100644 index 00000000000..6e79c6e783e --- /dev/null +++ b/.changeset/cool-pumpkins-dream.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +reclassify as error, UnknownFault, Error: The security token included in the request is expired diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 555342a3e93..e378f05563f 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -22,6 +22,15 @@ const testErrorMappings = [ errorName: 'ExpiredTokenError', expectedDownstreamErrorMessage: 'ExpiredToken', }, + { + errorMessage: + 'Error: The security token included in the request is expired', + expectedTopLevelErrorMessage: + 'The security token included in the request is invalid.', + errorName: 'ExpiredTokenError', + expectedDownstreamErrorMessage: + 'Error: The security token included in the request is expired', + }, { errorMessage: 'Access Denied', expectedTopLevelErrorMessage: diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 1b9ff691bcb..8a6b267042c 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -90,10 +90,12 @@ export class CdkErrorMapper { classification: AmplifyErrorClassification; }> => [ { - errorRegex: /ExpiredToken/, + errorRegex: + /ExpiredToken|Error: The security token included in the request is expired/, humanReadableErrorMessage: 'The security token included in the request is invalid.', - resolutionMessage: 'Ensure your local AWS credentials are valid.', + resolutionMessage: + "Please update your AWS credentials. You can do this by running `aws configure` or by updating your AWS credentials file. If you're using temporary credentials, you may need to obtain new ones.", errorName: 'ExpiredTokenError', classification: 'ERROR', }, From 889bdb771d796312e496e93c290763b648fc832e Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 4 Nov 2024 09:23:13 -0800 Subject: [PATCH 083/199] Handle case where synthesis renders empty cdk assembly (#2192) * Handle case where synthesis renders empty cdk assembly * fix test * undo that * actual fix --- .changeset/early-pens-eat.md | 5 +++++ .../src/cdk_error_mapper.test.ts | 14 ++++++++++++++ .../backend-deployer/src/cdk_error_mapper.ts | 16 +++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 .changeset/early-pens-eat.md diff --git a/.changeset/early-pens-eat.md b/.changeset/early-pens-eat.md new file mode 100644 index 00000000000..69b14ebf869 --- /dev/null +++ b/.changeset/early-pens-eat.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +Handle case where synthesis renders empty cdk assembly diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index e378f05563f..8dff74dd374 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -346,6 +346,20 @@ const testErrorMappings = [ errorName: 'CloudFormationDeploymentError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: `ENOENT: no such file or directory, open '.amplify/artifacts/cdk.out/manifest.json'`, + expectedTopLevelErrorMessage: + 'The Amplify backend definition is missing `defineBackend` call.', + errorName: 'MissingDefineBackendError', + expectedDownstreamErrorMessage: undefined, + }, + { + errorMessage: `ENOENT: no such file or directory, open '.amplify\\artifacts\\cdk.out\\manifest.json'`, + expectedTopLevelErrorMessage: + 'The Amplify backend definition is missing `defineBackend` call.', + errorName: 'MissingDefineBackendError', + expectedDownstreamErrorMessage: undefined, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 8a6b267042c..a10c98e45d3 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -263,6 +263,20 @@ export class CdkErrorMapper { errorName: 'BackendSynthError', classification: 'ERROR', }, + { + // This happens when 'defineBackend' call is missing in customer's app. + // 'defineBackend' creates CDK app in memory. If it's missing then no cdk.App exists in memory and nothing is rendered. + // During 'cdk synth' CDK CLI attempts to read CDK assembly after calling customer's app. + // But no files are rendered causing it to fail. + errorRegex: + /ENOENT: no such file or directory, open '\.amplify.artifacts.cdk\.out.manifest\.json'/, + humanReadableErrorMessage: + 'The Amplify backend definition is missing `defineBackend` call.', + resolutionMessage: + 'Check your backend definition in the `amplify` folder. Ensure that `amplify/backend.ts` contains `defineBackend` call.', + errorName: 'MissingDefineBackendError', + classification: 'ERROR', + }, { // "Catch all": the backend entry point file is referenced in the stack indicating a problem in customer code errorRegex: /amplify\/backend/, @@ -317,11 +331,11 @@ export type CDKDeploymentError = | 'CFNUpdateNotSupportedError' | 'CloudFormationDeploymentError' | 'FilePermissionsError' + | 'MissingDefineBackendError' | 'MultipleSandboxInstancesError' | 'ESBuildError' | 'ExpiredTokenError' | 'FileConventionError' - | 'FileConventionError' | 'ModuleNotFoundError' | 'SecretNotSetError' | 'SyntaxError'; From 96da1cdf93b9fb16c38b19332d4c8abd8d4241f5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 10:10:15 -0800 Subject: [PATCH 084/199] Version Packages (#2182) Co-authored-by: github-actions[bot] --- .changeset/cool-pumpkins-dream.md | 5 ----- .changeset/early-pens-eat.md | 5 ----- .changeset/funny-papayas-invent.md | 5 ----- .changeset/tall-singers-rest.md | 2 -- .changeset/tidy-chicken-notice.md | 5 ----- packages/backend-deployer/CHANGELOG.md | 8 ++++++++ packages/backend-deployer/package.json | 2 +- packages/backend-secret/CHANGELOG.md | 6 ++++++ packages/backend-secret/package.json | 2 +- 9 files changed, 16 insertions(+), 24 deletions(-) delete mode 100644 .changeset/cool-pumpkins-dream.md delete mode 100644 .changeset/early-pens-eat.md delete mode 100644 .changeset/funny-papayas-invent.md delete mode 100644 .changeset/tall-singers-rest.md delete mode 100644 .changeset/tidy-chicken-notice.md diff --git a/.changeset/cool-pumpkins-dream.md b/.changeset/cool-pumpkins-dream.md deleted file mode 100644 index 6e79c6e783e..00000000000 --- a/.changeset/cool-pumpkins-dream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -reclassify as error, UnknownFault, Error: The security token included in the request is expired diff --git a/.changeset/early-pens-eat.md b/.changeset/early-pens-eat.md deleted file mode 100644 index 69b14ebf869..00000000000 --- a/.changeset/early-pens-eat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -Handle case where synthesis renders empty cdk assembly diff --git a/.changeset/funny-papayas-invent.md b/.changeset/funny-papayas-invent.md deleted file mode 100644 index 1c6c23ba874..00000000000 --- a/.changeset/funny-papayas-invent.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -add stack is in a state and can not be updated to error mapper diff --git a/.changeset/tall-singers-rest.md b/.changeset/tall-singers-rest.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/tall-singers-rest.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/tidy-chicken-notice.md b/.changeset/tidy-chicken-notice.md deleted file mode 100644 index 28178599d01..00000000000 --- a/.changeset/tidy-chicken-notice.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-secret': patch ---- - -Handle parameter not found error while deleting secret diff --git a/packages/backend-deployer/CHANGELOG.md b/packages/backend-deployer/CHANGELOG.md index 24861f0c55d..c31dc826cce 100644 --- a/packages/backend-deployer/CHANGELOG.md +++ b/packages/backend-deployer/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-deployer +## 1.1.7 + +### Patch Changes + +- 7bf0c64: reclassify as error, UnknownFault, Error: The security token included in the request is expired +- 889bdb7: Handle case where synthesis renders empty cdk assembly +- a191fe5: add stack is in a state and can not be updated to error mapper + ## 1.1.6 ### Patch Changes diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index 30cf07f86be..55f3c681fb2 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-deployer", - "version": "1.1.6", + "version": "1.1.7", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/backend-secret/CHANGELOG.md b/packages/backend-secret/CHANGELOG.md index 14b518a08e7..3ff634508c8 100644 --- a/packages/backend-secret/CHANGELOG.md +++ b/packages/backend-secret/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/backend-secret +## 1.1.5 + +### Patch Changes + +- 255ca18: Handle parameter not found error while deleting secret + ## 1.1.4 ### Patch Changes diff --git a/packages/backend-secret/package.json b/packages/backend-secret/package.json index e9a5d3da8ea..8d3f372b9c8 100644 --- a/packages/backend-secret/package.json +++ b/packages/backend-secret/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-secret", - "version": "1.1.4", + "version": "1.1.5", "type": "module", "publishConfig": { "access": "public" From 4e9738933b15d6d4d4518f5284cf2b851b8f3cf5 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Mon, 4 Nov 2024 12:40:36 -0800 Subject: [PATCH 085/199] add validation if layer arn region does not match function region (#2188) * add validation if layer arn region does not match function region * add backend to changeset * update function region in test and add conditional logic for region --- .changeset/tiny-cameras-happen.md | 6 +++ packages/backend-function/src/factory.test.ts | 33 +++++++++++++ packages/backend-function/src/factory.ts | 49 ++++++++++++------- .../backend-function/src/layer_parser.test.ts | 2 +- 4 files changed, 70 insertions(+), 20 deletions(-) create mode 100644 .changeset/tiny-cameras-happen.md diff --git a/.changeset/tiny-cameras-happen.md b/.changeset/tiny-cameras-happen.md new file mode 100644 index 00000000000..ee761765278 --- /dev/null +++ b/.changeset/tiny-cameras-happen.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/backend': patch +'@aws-amplify/backend-function': patch +--- + +add validation if layer arn region does not match function region diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index 127c10c33ce..872e91b5bf9 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -17,6 +17,7 @@ import { NodeVersion, defineFunction } from './factory.js'; import { lambdaWithDependencies } from './test-assets/lambda-with-dependencies/resource.js'; import { Runtime } from 'aws-cdk-lib/aws-lambda'; import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; const createStackAndSetContext = (): Stack => { const app = new App(); @@ -411,6 +412,38 @@ void describe('AmplifyFunctionFactory', () => { }); }); + void describe('layers property', () => { + void it('defaults to no layers', () => { + const lambda = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + }).getInstance(getInstanceProps); + const template = Template.fromStack(lambda.stack); + + template.resourceCountIs('AWS::Lambda::LayerVersion', 0); + }); + + void it('throws if layer arn region is not the same as function region', () => { + assert.throws( + () => + defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + layers: { + layer1: + 'arn:aws:lambda:some-region:123456789012:layer:my-layer-1:1', + }, + }).getInstance(getInstanceProps), + (error: AmplifyUserError) => { + assert.strictEqual( + error.message, + 'Region in ARN does not match function region for layer: layer1' + ); + assert.ok(error.resolution); + return true; + } + ); + }); + }); + void describe('minify property', () => { void it('sets minify to false', () => { const lambda = defineFunction({ diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 5d5a6938e26..511bf320e6f 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -23,16 +23,11 @@ import { SsmEnvironmentEntry, StackProvider, } from '@aws-amplify/plugin-types'; -import { Duration, Stack, Tags } from 'aws-cdk-lib'; +import { Duration, Lazy, Stack, Tags, Token } from 'aws-cdk-lib'; import { Rule } from 'aws-cdk-lib/aws-events'; import * as targets from 'aws-cdk-lib/aws-events-targets'; import { Policy } from 'aws-cdk-lib/aws-iam'; -import { - CfnFunction, - ILayerVersion, - LayerVersion, - Runtime, -} from 'aws-cdk-lib/aws-lambda'; +import { CfnFunction, LayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'; import { NodejsFunction, OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs'; import { Construct } from 'constructs'; import { readFileSync } from 'fs'; @@ -350,19 +345,10 @@ class FunctionGenerator implements ConstructContainerEntryGenerator { scope, backendSecretResolver, }: GenerateContainerEntryProps) => { - // resolve layers to LayerVersion objects for the NodejsFunction constructor using the scope. - const resolvedLayers = Object.entries(this.props.layers).map(([key, arn]) => - LayerVersion.fromLayerVersionArn( - scope, - `${this.props.name}-${key}-layer`, - arn - ) - ); - return new AmplifyFunction( scope, this.props.name, - { ...this.props, resolvedLayers }, + this.props, backendSecretResolver, this.outputStorageStrategy ); @@ -382,7 +368,7 @@ class AmplifyFunction constructor( scope: Construct, id: string, - props: HydratedFunctionProps & { resolvedLayers: ILayerVersion[] }, + props: HydratedFunctionProps, backendSecretResolver: BackendSecretResolver, outputStorageStrategy: BackendOutputStorageStrategy ) { @@ -390,6 +376,31 @@ class AmplifyFunction this.stack = Stack.of(scope); + // resolve layers to LayerVersion objects for the NodejsFunction constructor using the scope. + const resolvedLayers = Object.entries(props.layers).map(([key, arn]) => { + const layerRegion = arn.split(':')[3]; + // If region is an unresolved token, use lazy to get region + const region = Token.isUnresolved(this.stack.region) + ? Lazy.string({ + produce: () => this.stack.region, + }) + : this.stack.region; + + if (layerRegion !== region) { + throw new AmplifyUserError('InvalidLayerArnRegionError', { + message: `Region in ARN does not match function region for layer: ${key}`, + resolution: + 'Update the layer ARN with the same region as the function', + }); + } + + return LayerVersion.fromLayerVersionArn( + scope, + `${props.name}-${key}-layer`, + arn + ); + }); + const runtime = nodeVersionMap[props.runtime]; const require = createRequire(import.meta.url); @@ -432,7 +443,7 @@ class AmplifyFunction timeout: Duration.seconds(props.timeoutSeconds), memorySize: props.memoryMB, runtime: nodeVersionMap[props.runtime], - layers: props.resolvedLayers, + layers: resolvedLayers, bundling: { ...props.bundling, banner: bannerCode, diff --git a/packages/backend-function/src/layer_parser.test.ts b/packages/backend-function/src/layer_parser.test.ts index 4d1e8ea5cc9..a00fb306243 100644 --- a/packages/backend-function/src/layer_parser.test.ts +++ b/packages/backend-function/src/layer_parser.test.ts @@ -20,7 +20,7 @@ const createStackAndSetContext = (): Stack => { app.node.setContext('amplify-backend-name', 'testEnvName'); app.node.setContext('amplify-backend-namespace', 'testBackendId'); app.node.setContext('amplify-backend-type', 'branch'); - const stack = new Stack(app); + const stack = new Stack(app, 'Stack', { env: { region: 'us-east-1' } }); return stack; }; From d0d8d4eb0618fd87daadab2af687c8102939f38a Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 4 Nov 2024 13:45:51 -0800 Subject: [PATCH 086/199] Fix a bug where $ sign in dart outputs would fail compilation (#2194) --- .changeset/new-buckets-mix.md | 5 +++++ .../client_config_formatter_default.test.ts | 2 +- .../client-config-writer/client_config_formatter_default.ts | 4 +++- .../client_config_formatter_legacy.test.ts | 2 +- .../client-config-writer/client_config_formatter_legacy.ts | 4 +++- 5 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 .changeset/new-buckets-mix.md diff --git a/.changeset/new-buckets-mix.md b/.changeset/new-buckets-mix.md new file mode 100644 index 00000000000..ba9c62c0ef9 --- /dev/null +++ b/.changeset/new-buckets-mix.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/client-config': patch +--- + +Fix a bug where $ sign in dart outputs would fail compilation diff --git a/packages/client-config/src/client-config-writer/client_config_formatter_default.test.ts b/packages/client-config/src/client-config-writer/client_config_formatter_default.test.ts index b805229c07e..33a6e57ff0e 100644 --- a/packages/client-config/src/client-config-writer/client_config_formatter_default.test.ts +++ b/packages/client-config/src/client-config-writer/client_config_formatter_default.test.ts @@ -50,7 +50,7 @@ void describe('client config formatter', () => { ClientConfigFormat.DART ); - assert.ok(formattedConfig.startsWith("const amplifyConfig = '''")); + assert.ok(formattedConfig.startsWith("const amplifyConfig = r'''")); assert.ok( formattedConfig.includes(JSON.stringify(expectedConfigReturned, null, 2)) ); diff --git a/packages/client-config/src/client-config-writer/client_config_formatter_default.ts b/packages/client-config/src/client-config-writer/client_config_formatter_default.ts index e05e68704ae..54994f0096d 100644 --- a/packages/client-config/src/client-config-writer/client_config_formatter_default.ts +++ b/packages/client-config/src/client-config-writer/client_config_formatter_default.ts @@ -16,7 +16,9 @@ export class ClientConfigFormatterDefault implements ClientConfigFormatter { format = (clientConfig: ClientConfig, format: ClientConfigFormat): string => { switch (format) { case ClientConfigFormat.DART: { - return `const amplifyConfig = '''${JSON.stringify( + // Using raw string, i.e. r''' to disable Dart's interpolations + // because we're using special characters like $ in some outputs. + return `const amplifyConfig = r'''${JSON.stringify( clientConfig, null, 2 diff --git a/packages/client-config/src/client-config-writer/client_config_formatter_legacy.test.ts b/packages/client-config/src/client-config-writer/client_config_formatter_legacy.test.ts index 38a8f098e8e..bfe343250d2 100644 --- a/packages/client-config/src/client-config-writer/client_config_formatter_legacy.test.ts +++ b/packages/client-config/src/client-config-writer/client_config_formatter_legacy.test.ts @@ -109,7 +109,7 @@ void describe('client config formatter', () => { expectedLegacyConfig.aws_user_pools_id ); - assert.ok(formattedConfig.startsWith("const amplifyConfig = '''")); + assert.ok(formattedConfig.startsWith("const amplifyConfig = r'''")); assert.ok( formattedConfig.includes(JSON.stringify(clientConfigMobile, null, 2)) ); diff --git a/packages/client-config/src/client-config-writer/client_config_formatter_legacy.ts b/packages/client-config/src/client-config-writer/client_config_formatter_legacy.ts index 153b6add97d..fdb13552598 100644 --- a/packages/client-config/src/client-config-writer/client_config_formatter_legacy.ts +++ b/packages/client-config/src/client-config-writer/client_config_formatter_legacy.ts @@ -29,7 +29,9 @@ export class ClientConfigFormatterLegacy implements ClientConfigFormatter { }export default amplifyConfig;${os.EOL}`; } case ClientConfigFormat.DART: { - return `const amplifyConfig = '''${JSON.stringify( + // Using raw string, i.e. r''' to disable Dart's interpolations + // because we're using special characters like $ in some outputs. + return `const amplifyConfig = r'''${JSON.stringify( this.configConverter.convertToMobileConfig(legacyConfig), null, 2 From 298f971f0f973244689ff12f9a2cdbbe179d3a72 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 14:00:07 -0800 Subject: [PATCH 087/199] Version Packages (#2193) Co-authored-by: github-actions[bot] --- .changeset/new-buckets-mix.md | 5 ----- .changeset/tiny-cameras-happen.md | 6 ------ packages/backend-function/CHANGELOG.md | 6 ++++++ packages/backend-function/package.json | 2 +- packages/backend/CHANGELOG.md | 10 ++++++++++ packages/backend/package.json | 6 +++--- packages/client-config/CHANGELOG.md | 6 ++++++ packages/client-config/package.json | 2 +- 8 files changed, 27 insertions(+), 16 deletions(-) delete mode 100644 .changeset/new-buckets-mix.md delete mode 100644 .changeset/tiny-cameras-happen.md diff --git a/.changeset/new-buckets-mix.md b/.changeset/new-buckets-mix.md deleted file mode 100644 index ba9c62c0ef9..00000000000 --- a/.changeset/new-buckets-mix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/client-config': patch ---- - -Fix a bug where $ sign in dart outputs would fail compilation diff --git a/.changeset/tiny-cameras-happen.md b/.changeset/tiny-cameras-happen.md deleted file mode 100644 index ee761765278..00000000000 --- a/.changeset/tiny-cameras-happen.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/backend': patch -'@aws-amplify/backend-function': patch ---- - -add validation if layer arn region does not match function region diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index f80ea88b9a1..26030ae2eb6 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/backend-function +## 1.7.4 + +### Patch Changes + +- 4e97389: add validation if layer arn region does not match function region + ## 1.7.3 ### Patch Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index 23cb901821a..2f51b13678d 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.7.3", + "version": "1.7.4", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 27dc519f0c1..7aac7239683 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/backend +## 1.6.1 + +### Patch Changes + +- 4e97389: add validation if layer arn region does not match function region +- Updated dependencies [d0d8d4e] +- Updated dependencies [4e97389] + - @aws-amplify/client-config@1.5.2 + - @aws-amplify/backend-function@1.7.4 + ## 1.6.0 ### Minor Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index e325f118ed1..b798b978492 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.6.0", + "version": "1.6.1", "type": "module", "publishConfig": { "access": "public" @@ -27,13 +27,13 @@ "dependencies": { "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/backend-auth": "^1.3.0", - "@aws-amplify/backend-function": "^1.7.3", + "@aws-amplify/backend-function": "^1.7.4", "@aws-amplify/backend-data": "^1.1.6", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.2", - "@aws-amplify/client-config": "^1.5.1", + "@aws-amplify/client-config": "^1.5.2", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/client-amplify": "^3.624.0", diff --git a/packages/client-config/CHANGELOG.md b/packages/client-config/CHANGELOG.md index 571511bbdbc..558d349fdbd 100644 --- a/packages/client-config/CHANGELOG.md +++ b/packages/client-config/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/client-config +## 1.5.2 + +### Patch Changes + +- d0d8d4e: Fix a bug where $ sign in dart outputs would fail compilation + ## 1.5.1 ### Patch Changes diff --git a/packages/client-config/package.json b/packages/client-config/package.json index 1a1afa44978..9310fa16178 100644 --- a/packages/client-config/package.json +++ b/packages/client-config/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/client-config", - "version": "1.5.1", + "version": "1.5.2", "type": "module", "publishConfig": { "access": "public" From 1af5060408a9dd60dca641b65082d7cdd581e6f6 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 4 Nov 2024 15:26:44 -0800 Subject: [PATCH 088/199] Add metadata to user agent in conversation handler runtime (#2181) * Add metadata to user agent in conversation handler runtime * tests * remove validation. --- .changeset/thin-candles-perform.md | 5 ++ .../runtime/bedrock_converse_adapter.test.ts | 10 ++- .../runtime/bedrock_converse_adapter.ts | 30 +++---- ...ersation_message_history_retriever.test.ts | 11 ++- .../conversation_message_history_retriever.ts | 3 +- .../conversation_turn_response_sender.test.ts | 90 +++++++++++++++++-- .../conversation_turn_response_sender.ts | 22 ++++- .../event_tools_provider.ts | 3 +- .../event-tools-provider/graphql_tool.test.ts | 14 ++- .../event-tools-provider/graphql_tool.ts | 5 +- .../runtime/graphql_request_executor.test.ts | 47 +++++++++- .../runtime/graphql_request_executor.ts | 12 ++- .../runtime/user_agent_provider.test.ts | 65 ++++++++++++++ .../runtime/user_agent_provider.ts | 53 +++++++++++ 14 files changed, 330 insertions(+), 40 deletions(-) create mode 100644 .changeset/thin-candles-perform.md create mode 100644 packages/ai-constructs/src/conversation/runtime/user_agent_provider.test.ts create mode 100644 packages/ai-constructs/src/conversation/runtime/user_agent_provider.ts diff --git a/.changeset/thin-candles-perform.md b/.changeset/thin-candles-perform.md new file mode 100644 index 00000000000..dbb3f0fe716 --- /dev/null +++ b/.changeset/thin-candles-perform.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': patch +--- + +Add metadata to user agent in conversation handler runtime. diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts index ae424828c70..38a32a0128c 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts @@ -25,6 +25,7 @@ import { import { ConversationTurnEventToolsProvider } from './event-tools-provider'; import { randomBytes, randomUUID } from 'node:crypto'; import { ConversationMessageHistoryRetriever } from './conversation_message_history_retriever'; +import { UserAgentProvider } from './user_agent_provider'; void describe('Bedrock converse adapter', () => { const commonEvent: Readonly = { @@ -897,17 +898,20 @@ void describe('Bedrock converse adapter', () => { ...commonEvent, }; - event.request.headers['x-amz-user-agent'] = 'testUserAgent'; - const bedrockClient = new BedrockRuntimeClient(); const addMiddlewareMock = mock.method(bedrockClient.middlewareStack, 'add'); + const userAgentProvider = new UserAgentProvider( + {} as unknown as ConversationTurnEvent + ); + mock.method(userAgentProvider, 'getUserAgent', () => 'testUserAgent'); new BedrockConverseAdapter( event, [], bedrockClient, undefined, - messageHistoryRetriever + messageHistoryRetriever, + userAgentProvider ); assert.strictEqual(addMiddlewareMock.mock.calls.length, 1); diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts index c215efc0b48..3bb48436989 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts @@ -22,6 +22,7 @@ import { ConversationTurnEventToolsProvider } from './event-tools-provider'; import { ConversationMessageHistoryRetriever } from './conversation_message_history_retriever'; import * as bedrock from '@aws-sdk/client-bedrock-runtime'; import { ValidationError } from './errors'; +import { UserAgentProvider } from './user_agent_provider'; /** * This class is responsible for interacting with Bedrock Converse API @@ -48,23 +49,22 @@ export class BedrockConverseAdapter { private readonly messageHistoryRetriever = new ConversationMessageHistoryRetriever( event ), + userAgentProvider = new UserAgentProvider(event), private readonly logger = console ) { - if (event.request.headers['x-amz-user-agent']) { - this.bedrockClient.middlewareStack.add( - (next) => (args) => { - // @ts-expect-error Request is typed as unknown. - // But this is recommended way to alter headers per https://github.com/aws/aws-sdk-js-v3/blob/main/README.md. - args.request.headers['x-amz-user-agent'] = - event.request.headers['x-amz-user-agent']; - return next(args); - }, - { - step: 'build', - name: 'amplify-user-agent-injector', - } - ); - } + this.bedrockClient.middlewareStack.add( + (next) => (args) => { + // @ts-expect-error Request is typed as unknown. + // But this is recommended way to alter headers per https://github.com/aws/aws-sdk-js-v3/blob/main/README.md. + args.request.headers['x-amz-user-agent'] = + userAgentProvider.getUserAgent(); + return next(args); + }, + { + step: 'build', + name: 'amplify-user-agent-injector', + } + ); this.executableTools = [ ...eventToolsProvider.getEventTools(), ...additionalTools, diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts index f17583d1ef0..9215fc80da1 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.test.ts @@ -12,6 +12,7 @@ import { GetQueryOutput, ListQueryOutput, } from './conversation_message_history_retriever'; +import { UserAgentProvider } from './user_agent_provider'; type TestCase = { name: string; @@ -704,7 +705,15 @@ void describe('Conversation message history retriever', () => { for (const testCase of testCases) { void it(testCase.name, async () => { - const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); + const userAgentProvider = new UserAgentProvider( + {} as unknown as ConversationTurnEvent + ); + mock.method(userAgentProvider, 'getUserAgent', () => ''); + const graphqlRequestExecutor = new GraphqlRequestExecutor( + '', + '', + userAgentProvider + ); const executeGraphqlMock = mock.method( graphqlRequestExecutor, 'executeGraphql', diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts index 2cb9f88372d..c98889522a7 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_message_history_retriever.ts @@ -4,6 +4,7 @@ import { ConversationTurnEvent, } from './types'; import { GraphqlRequestExecutor } from './graphql_request_executor'; +import { UserAgentProvider } from './user_agent_provider'; export type ConversationHistoryMessageItem = ConversationMessage & { id: string; @@ -107,7 +108,7 @@ export class ConversationMessageHistoryRetriever { private readonly graphqlRequestExecutor = new GraphqlRequestExecutor( event.graphqlApiEndpoint, event.request.headers.authorization, - event.request.headers['x-amz-user-agent'] + new UserAgentProvider(event) ) ) {} diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts index 0e7e1fe71a4..32c579b2374 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.test.ts @@ -15,6 +15,7 @@ import { GraphqlRequest, GraphqlRequestExecutor, } from './graphql_request_executor'; +import { UserAgentProvider } from './user_agent_provider'; void describe('Conversation turn response sender', () => { const event: ConversationTurnEvent = { @@ -37,7 +38,19 @@ void describe('Conversation turn response sender', () => { }; void it('sends response back to appsync', async () => { - const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); + const userAgentProvider = new UserAgentProvider( + {} as unknown as ConversationTurnEvent + ); + const userAgentProviderMock = mock.method( + userAgentProvider, + 'getUserAgent', + () => 'testUserAgent' + ); + const graphqlRequestExecutor = new GraphqlRequestExecutor( + '', + '', + userAgentProvider + ); const executeGraphqlMock = mock.method( graphqlRequestExecutor, 'executeGraphql', @@ -47,6 +60,7 @@ void describe('Conversation turn response sender', () => { ); const sender = new ConversationTurnResponseSender( event, + userAgentProvider, graphqlRequestExecutor ); const response: Array = [ @@ -57,7 +71,14 @@ void describe('Conversation turn response sender', () => { ]; await sender.sendResponse(response); + assert.strictEqual(userAgentProviderMock.mock.calls.length, 1); + assert.deepStrictEqual(userAgentProviderMock.mock.calls[0].arguments[0], { + 'turn-response-type': 'single', + }); assert.strictEqual(executeGraphqlMock.mock.calls.length, 1); + assert.deepStrictEqual(executeGraphqlMock.mock.calls[0].arguments[1], { + userAgent: 'testUserAgent', + }); const request = executeGraphqlMock.mock.calls[0] .arguments[0] as GraphqlRequest; assert.deepStrictEqual(request, { @@ -85,7 +106,15 @@ void describe('Conversation turn response sender', () => { }); void it('serializes tool use input to JSON', async () => { - const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); + const userAgentProvider = new UserAgentProvider( + {} as unknown as ConversationTurnEvent + ); + mock.method(userAgentProvider, 'getUserAgent', () => ''); + const graphqlRequestExecutor = new GraphqlRequestExecutor( + '', + '', + userAgentProvider + ); const executeGraphqlMock = mock.method( graphqlRequestExecutor, 'executeGraphql', @@ -95,6 +124,7 @@ void describe('Conversation turn response sender', () => { ); const sender = new ConversationTurnResponseSender( event, + userAgentProvider, graphqlRequestExecutor ); const toolUseBlock: ContentBlock.ToolUseMember = { @@ -140,7 +170,19 @@ void describe('Conversation turn response sender', () => { }); void it('sends streaming response chunk back to appsync', async () => { - const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); + const userAgentProvider = new UserAgentProvider( + {} as unknown as ConversationTurnEvent + ); + const userAgentProviderMock = mock.method( + userAgentProvider, + 'getUserAgent', + () => 'testUserAgent' + ); + const graphqlRequestExecutor = new GraphqlRequestExecutor( + '', + '', + userAgentProvider + ); const executeGraphqlMock = mock.method( graphqlRequestExecutor, 'executeGraphql', @@ -150,6 +192,7 @@ void describe('Conversation turn response sender', () => { ); const sender = new ConversationTurnResponseSender( event, + userAgentProvider, graphqlRequestExecutor ); const chunk: StreamingResponseChunk = { @@ -162,7 +205,14 @@ void describe('Conversation turn response sender', () => { }; await sender.sendResponseChunk(chunk); + assert.strictEqual(userAgentProviderMock.mock.calls.length, 1); + assert.deepStrictEqual(userAgentProviderMock.mock.calls[0].arguments[0], { + 'turn-response-type': 'streaming', + }); assert.strictEqual(executeGraphqlMock.mock.calls.length, 1); + assert.deepStrictEqual(executeGraphqlMock.mock.calls[0].arguments[1], { + userAgent: 'testUserAgent', + }); const request = executeGraphqlMock.mock.calls[0] .arguments[0] as GraphqlRequest; assert.deepStrictEqual(request, { @@ -181,7 +231,15 @@ void describe('Conversation turn response sender', () => { }); void it('serializes tool use input to JSON when streaming', async () => { - const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); + const userAgentProvider = new UserAgentProvider( + {} as unknown as ConversationTurnEvent + ); + mock.method(userAgentProvider, 'getUserAgent', () => ''); + const graphqlRequestExecutor = new GraphqlRequestExecutor( + '', + '', + userAgentProvider + ); const executeGraphqlMock = mock.method( graphqlRequestExecutor, 'executeGraphql', @@ -191,6 +249,7 @@ void describe('Conversation turn response sender', () => { ); const sender = new ConversationTurnResponseSender( event, + userAgentProvider, graphqlRequestExecutor ); const toolUseBlock: ContentBlock.ToolUseMember = { @@ -242,7 +301,19 @@ void describe('Conversation turn response sender', () => { }); void it('sends errors response back to appsync', async () => { - const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); + const userAgentProvider = new UserAgentProvider( + {} as unknown as ConversationTurnEvent + ); + const userAgentProviderMock = mock.method( + userAgentProvider, + 'getUserAgent', + () => 'testUserAgent' + ); + const graphqlRequestExecutor = new GraphqlRequestExecutor( + '', + '', + userAgentProvider + ); const executeGraphqlMock = mock.method( graphqlRequestExecutor, 'executeGraphql', @@ -252,6 +323,7 @@ void describe('Conversation turn response sender', () => { ); const sender = new ConversationTurnResponseSender( event, + userAgentProvider, graphqlRequestExecutor ); const errors: Array = [ @@ -266,6 +338,14 @@ void describe('Conversation turn response sender', () => { ]; await sender.sendErrors(errors); + assert.strictEqual(userAgentProviderMock.mock.calls.length, 1); + assert.deepStrictEqual(userAgentProviderMock.mock.calls[0].arguments[0], { + 'turn-response-type': 'error', + }); + assert.strictEqual(executeGraphqlMock.mock.calls.length, 1); + assert.deepStrictEqual(executeGraphqlMock.mock.calls[0].arguments[1], { + userAgent: 'testUserAgent', + }); assert.strictEqual(executeGraphqlMock.mock.calls.length, 1); const request = executeGraphqlMock.mock.calls[0] .arguments[0] as GraphqlRequest; diff --git a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts index 723b7879599..5892b6747ca 100644 --- a/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts +++ b/packages/ai-constructs/src/conversation/runtime/conversation_turn_response_sender.ts @@ -5,6 +5,7 @@ import { } from './types.js'; import type { ContentBlock } from '@aws-sdk/client-bedrock-runtime'; import { GraphqlRequestExecutor } from './graphql_request_executor'; +import { UserAgentProvider } from './user_agent_provider'; export type MutationResponseInput = { input: { @@ -36,10 +37,11 @@ export class ConversationTurnResponseSender { */ constructor( private readonly event: ConversationTurnEvent, + private readonly userAgentProvider = new UserAgentProvider(event), private readonly graphqlRequestExecutor = new GraphqlRequestExecutor( event.graphqlApiEndpoint, event.request.headers.authorization, - event.request.headers['x-amz-user-agent'] + userAgentProvider ), private readonly logger = console ) {} @@ -50,7 +52,11 @@ export class ConversationTurnResponseSender { await this.graphqlRequestExecutor.executeGraphql< MutationResponseInput, void - >(responseMutationRequest); + >(responseMutationRequest, { + userAgent: this.userAgentProvider.getUserAgent({ + 'turn-response-type': 'single', + }), + }); }; sendResponseChunk = async (chunk: StreamingResponseChunk) => { @@ -59,7 +65,11 @@ export class ConversationTurnResponseSender { await this.graphqlRequestExecutor.executeGraphql< MutationStreamingResponseInput, void - >(responseMutationRequest); + >(responseMutationRequest, { + userAgent: this.userAgentProvider.getUserAgent({ + 'turn-response-type': 'streaming', + }), + }); }; sendErrors = async (errors: ConversationTurnError[]) => { @@ -71,7 +81,11 @@ export class ConversationTurnResponseSender { await this.graphqlRequestExecutor.executeGraphql< MutationErrorsResponseInput, void - >(responseMutationRequest); + >(responseMutationRequest, { + userAgent: this.userAgentProvider.getUserAgent({ + 'turn-response-type': 'error', + }), + }); }; private createMutationErrorsRequest = (errors: ConversationTurnError[]) => { diff --git a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.ts b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.ts index b895c757be9..19ff56dc561 100644 --- a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.ts +++ b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/event_tools_provider.ts @@ -1,6 +1,7 @@ import { ConversationTurnEvent, ExecutableTool } from '../types'; import { GraphQlTool } from './graphql_tool'; import { GraphQlQueryFactory } from './graphql_query_factory'; +import { UserAgentProvider } from '../user_agent_provider'; /** * Creates executable tools from definitions in conversation turn event. @@ -29,7 +30,7 @@ export class ConversationTurnEventToolsProvider { graphqlApiEndpoint, query, this.event.request.headers.authorization, - this.event.request.headers['x-amz-user-agent'] + new UserAgentProvider(this.event) ); }); return tools ?? []; diff --git a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.test.ts b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.test.ts index 68308ebb7b2..d764dca5088 100644 --- a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.test.ts @@ -6,11 +6,17 @@ import { GraphqlRequestExecutor, } from '../graphql_request_executor'; import { DocumentType } from '@smithy/types'; +import { UserAgentProvider } from '../user_agent_provider'; +import { ConversationTurnEvent } from '../types'; void describe('GraphQl tool', () => { const graphQlEndpoint = 'http://test.endpoint/'; const query = 'testQuery'; const accessToken = 'testAccessToken'; + const userAgentProvider = new UserAgentProvider( + {} as unknown as ConversationTurnEvent + ); + mock.method(userAgentProvider, 'getUserAgent', () => ''); const createGraphQlTool = ( graphqlRequestExecutor: GraphqlRequestExecutor @@ -22,7 +28,7 @@ void describe('GraphQl tool', () => { graphQlEndpoint, query, accessToken, - '', + userAgentProvider, graphqlRequestExecutor ); }; @@ -31,7 +37,11 @@ void describe('GraphQl tool', () => { const testResponse = { test: 'response', }; - const graphqlRequestExecutor = new GraphqlRequestExecutor('', '', ''); + const graphqlRequestExecutor = new GraphqlRequestExecutor( + '', + '', + userAgentProvider + ); const executeGraphqlMock = mock.method( graphqlRequestExecutor, 'executeGraphql', diff --git a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts index 8890b5f78b0..dcd37368a31 100644 --- a/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts +++ b/packages/ai-constructs/src/conversation/runtime/event-tools-provider/graphql_tool.ts @@ -2,6 +2,7 @@ import { ExecutableTool, JSONSchema, ToolInputSchema } from '../types'; import type { ToolResultContentBlock } from '@aws-sdk/client-bedrock-runtime'; import { DocumentType } from '@smithy/types'; import { GraphqlRequestExecutor } from '../graphql_request_executor'; +import { UserAgentProvider } from '../user_agent_provider'; /** * A tool that use GraphQl queries. @@ -17,11 +18,11 @@ export class GraphQlTool implements ExecutableTool { readonly graphQlEndpoint: string, private readonly query: string, readonly accessToken: string, - readonly userAgent: string, + readonly userAgentProvider: UserAgentProvider, private readonly graphqlRequestExecutor = new GraphqlRequestExecutor( graphQlEndpoint, accessToken, - userAgent + userAgentProvider ) ) {} diff --git a/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.test.ts b/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.test.ts index 674bad3ea32..fe605b21711 100644 --- a/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.test.ts @@ -2,11 +2,17 @@ import { describe, it, mock } from 'node:test'; import assert from 'node:assert'; import { text } from 'node:stream/consumers'; import { GraphqlRequestExecutor } from './graphql_request_executor'; +import { UserAgentProvider } from './user_agent_provider'; +import { ConversationTurnEvent } from './types'; void describe('Graphql executor test', () => { const graphqlEndpoint = 'http://fake.endpoint/'; const accessToken = 'testToken'; const userAgent = 'testUserAgent'; + const userAgentProvider = new UserAgentProvider( + {} as unknown as ConversationTurnEvent + ); + mock.method(userAgentProvider, 'getUserAgent', () => userAgent); void it('sends request to appsync', async () => { const fetchMock = mock.fn( @@ -18,7 +24,7 @@ void describe('Graphql executor test', () => { const executor = new GraphqlRequestExecutor( graphqlEndpoint, accessToken, - userAgent, + userAgentProvider, fetchMock ); const query = 'testQuery'; @@ -47,6 +53,41 @@ void describe('Graphql executor test', () => { }); }); + void it('method provided user agent takes precedence', async () => { + const fetchMock = mock.fn( + fetch, + (): Promise => + // Mock successful Appsync response + Promise.resolve(new Response('{}', { status: 200 })) + ); + const executor = new GraphqlRequestExecutor( + graphqlEndpoint, + accessToken, + userAgentProvider, + fetchMock + ); + const query = 'testQuery'; + const variables = { + testVariableKey: 'testVariableValue', + }; + await executor.executeGraphql( + { + query, + variables, + }, + { + userAgent: 'methodScopedUserAgent', + } + ); + + assert.strictEqual(fetchMock.mock.calls.length, 1); + const request: Request = fetchMock.mock.calls[0].arguments[0] as Request; + assert.strictEqual( + request.headers.get('x-amz-user-agent'), + 'methodScopedUserAgent' + ); + }); + void it('throws if response is not 2xx', async () => { const fetchMock = mock.fn( fetch, @@ -62,7 +103,7 @@ void describe('Graphql executor test', () => { const executor = new GraphqlRequestExecutor( graphqlEndpoint, accessToken, - userAgent, + userAgentProvider, fetchMock ); const query = 'testQuery'; @@ -102,7 +143,7 @@ void describe('Graphql executor test', () => { const executor = new GraphqlRequestExecutor( graphqlEndpoint, accessToken, - userAgent, + userAgentProvider, fetchMock ); const query = 'testQuery'; diff --git a/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.ts b/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.ts index a025b75cff6..60f1af44bb3 100644 --- a/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.ts +++ b/packages/ai-constructs/src/conversation/runtime/graphql_request_executor.ts @@ -1,3 +1,5 @@ +import { UserAgentProvider } from './user_agent_provider'; + export type GraphqlRequest = { query: string; variables: TVariables; @@ -15,19 +17,23 @@ export class GraphqlRequestExecutor { constructor( private readonly graphQlEndpoint: string, private readonly accessToken: string, - private readonly userAgent: string, + private readonly userAgentProvider: UserAgentProvider, private readonly _fetch = fetch ) {} executeGraphql = async ( - request: GraphqlRequest + request: GraphqlRequest, + options?: { + userAgent?: string; + } ): Promise => { const httpRequest = new Request(this.graphQlEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/graphql', Authorization: this.accessToken, - 'x-amz-user-agent': this.userAgent, + 'x-amz-user-agent': + options?.userAgent ?? this.userAgentProvider.getUserAgent(), }, body: JSON.stringify({ query: request.query, diff --git a/packages/ai-constructs/src/conversation/runtime/user_agent_provider.test.ts b/packages/ai-constructs/src/conversation/runtime/user_agent_provider.test.ts new file mode 100644 index 00000000000..6309e8de953 --- /dev/null +++ b/packages/ai-constructs/src/conversation/runtime/user_agent_provider.test.ts @@ -0,0 +1,65 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert'; +import * as fs from 'node:fs'; +import path from 'path'; +import { UserAgentProvider } from './user_agent_provider'; +import { ConversationTurnEvent } from './types'; + +void describe('User Agent provider', () => { + // Read package json from disk (i.e., in a different way than actual implementation does). + const packageVersion = JSON.parse( + fs.readFileSync( + path.resolve(__dirname, '..', '..', '..', 'package.json'), + 'utf-8' + ) + ).version; + + void it('adds package information as metadata when user agent is present in the event', () => { + const userAgentProvider = new UserAgentProvider({ + request: { + headers: { + 'x-amz-user-agent': 'lib/foo#1.2.3', + }, + }, + } as unknown as ConversationTurnEvent); + + const userAgent = userAgentProvider.getUserAgent(); + + assert.strictEqual( + userAgent, + `lib/foo#1.2.3 md/amplify-ai-constructs#${packageVersion}` + ); + }); + + void it('adds package information as lib when user agent is not present in the event', () => { + const userAgentProvider = new UserAgentProvider({ + request: { + headers: {}, + }, + } as unknown as ConversationTurnEvent); + + const userAgent = userAgentProvider.getUserAgent(); + + assert.strictEqual( + userAgent, + `lib/amplify-ai-constructs#${packageVersion}` + ); + }); + + void it('adds additional metadata', () => { + const userAgentProvider = new UserAgentProvider({ + request: { + headers: {}, + }, + } as unknown as ConversationTurnEvent); + + const userAgent = userAgentProvider.getUserAgent({ + 'turn-response-type': 'streaming', + }); + + assert.strictEqual( + userAgent, + `lib/amplify-ai-constructs#${packageVersion} md/turn-response-type#streaming` + ); + }); +}); diff --git a/packages/ai-constructs/src/conversation/runtime/user_agent_provider.ts b/packages/ai-constructs/src/conversation/runtime/user_agent_provider.ts new file mode 100644 index 00000000000..a958b4eb94e --- /dev/null +++ b/packages/ai-constructs/src/conversation/runtime/user_agent_provider.ts @@ -0,0 +1,53 @@ +import { ConversationTurnEvent } from './types'; + +// This is intentional. There's no other way to read package version. +// 1. The 'imports' field in package.json won't work because this is CommonJS package. +// 2. We can't use `fs.readFile`. This file is bundled by ESBuild. ESBuild needs to know to bundle package.json +// That is achievable by either require or import statements. +// 3. The package.json is outside the rootDir defined in tsconfig.json +// Imports require tsconfig to be broken down (as explained here https://stackoverflow.com/questions/55753163/package-json-is-not-under-rootdir). +// This would however would not work with our scripts that check tsconfig files for correctness. +// 4. Hardcoding version in the code, as opposed to reading package.json file isn't great option either. +// +// Therefore, using require as least problematic solution here. +// eslint-disable-next-line @typescript-eslint/no-var-requires +const packageVersion = require('../../../package.json').version; +// Compliant with https://www.rfc-editor.org/rfc/rfc5234. +const packageName = 'amplify-ai-constructs'; + +export type UserAgentAdditionalMetadata = { + // These keys are user agent friendly intentionally. + // eslint-disable-next-line @typescript-eslint/naming-convention + 'turn-response-type'?: 'single' | 'streaming' | 'error'; +}; + +/** + * Provides user agent. + */ +export class UserAgentProvider { + /** + * Creates user agent provider instance. + */ + constructor(private readonly event: ConversationTurnEvent) {} + + getUserAgent = (additionalMetadata?: UserAgentAdditionalMetadata): string => { + let userAgent = this.event.request.headers['x-amz-user-agent']; + + // append library version + if (userAgent) { + // if user agent was forwarded from AppSync then append our package information as metadata. + userAgent = `${userAgent} md/${packageName}#${packageVersion}`; + } else { + // if user agent was not forwarded use our package information as library. + userAgent = `lib/${packageName}#${packageVersion}`; + } + + if (additionalMetadata) { + Object.entries(additionalMetadata).forEach(([key, value]) => { + userAgent = `${userAgent} md/${key}#${value}`; + }); + } + + return userAgent; + }; +} From 583a3f296dce3b6ecd33aaac2bfd48b729c6f8fc Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Tue, 5 Nov 2024 13:44:13 -0800 Subject: [PATCH 089/199] Fix detection of AmplifyErrors (#2200) * Fix detection of AmplifyErrors * fix check * fix check --- .changeset/green-cups-jam.md | 10 +++++ .eslint_dictionary.json | 1 + packages/backend-data/src/factory.ts | 2 +- packages/backend-deployer/src/cdk_deployer.ts | 2 +- .../commands/sandbox/sandbox_command.test.ts | 2 +- .../sandbox/sandbox_event_handler_factory.ts | 2 +- packages/cli/src/error_handler.ts | 4 +- packages/eslint-rules/src/index.ts | 3 ++ .../amplify_error_no_instance_of.test.ts | 28 +++++++++++++ .../src/rules/amplify_error_no_instance_of.ts | 41 +++++++++++++++++++ packages/platform-core/API.md | 1 + .../src/errors/amplify_error.test.ts | 7 ++-- .../platform-core/src/errors/amplify_error.ts | 22 +++++++++- packages/sandbox/src/file_watching_sandbox.ts | 4 +- 14 files changed, 117 insertions(+), 12 deletions(-) create mode 100644 .changeset/green-cups-jam.md create mode 100644 packages/eslint-rules/src/rules/amplify_error_no_instance_of.test.ts create mode 100644 packages/eslint-rules/src/rules/amplify_error_no_instance_of.ts diff --git a/.changeset/green-cups-jam.md b/.changeset/green-cups-jam.md new file mode 100644 index 00000000000..e9f2672f9cb --- /dev/null +++ b/.changeset/green-cups-jam.md @@ -0,0 +1,10 @@ +--- +'@aws-amplify/platform-core': minor +'@aws-amplify/backend-deployer': patch +'@aws-amplify/backend-data': patch +'@aws-amplify/backend': patch +'@aws-amplify/sandbox': patch +'@aws-amplify/backend-cli': patch +--- + +Fix detection of AmplifyErrors diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index 399dd952f7a..cd255e5445a 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -81,6 +81,7 @@ "idps", "implementors", "inheritdoc", + "instanceof", "interop", "invokable", "invoker", diff --git a/packages/backend-data/src/factory.ts b/packages/backend-data/src/factory.ts index c40ad8db704..ebe3d1b0835 100644 --- a/packages/backend-data/src/factory.ts +++ b/packages/backend-data/src/factory.ts @@ -184,7 +184,7 @@ class DataGenerator implements ConstructContainerEntryGenerator { this.props.authorizationModes ); } catch (error) { - if (error instanceof AmplifyError) { + if (AmplifyError.isAmplifyError(error)) { throw error; } throw new AmplifyUserError( diff --git a/packages/backend-deployer/src/cdk_deployer.ts b/packages/backend-deployer/src/cdk_deployer.ts index 586f68d7462..0771673117c 100644 --- a/packages/backend-deployer/src/cdk_deployer.ts +++ b/packages/backend-deployer/src/cdk_deployer.ts @@ -86,7 +86,7 @@ export class CDKDeployer implements BackendDeployer { } catch (typeError: unknown) { if ( synthError && - typeError instanceof AmplifyError && + AmplifyError.isAmplifyError(typeError) && typeError.cause?.message.match( /Cannot find module '\$amplify\/env\/.*' or its corresponding type declarations/ ) diff --git a/packages/cli/src/commands/sandbox/sandbox_command.test.ts b/packages/cli/src/commands/sandbox/sandbox_command.test.ts index 6d7df80f9b1..a05bfa3168b 100644 --- a/packages/cli/src/commands/sandbox/sandbox_command.test.ts +++ b/packages/cli/src/commands/sandbox/sandbox_command.test.ts @@ -121,7 +121,7 @@ void describe('sandbox command', () => { () => commandRunner.runCommand(`sandbox --identifier ${invalidIdentifier}`), // invalid identifier (err: TestCommandError) => { - assert.ok(err.error instanceof AmplifyError); + assert.ok(AmplifyError.isAmplifyError(err.error)); assert.strictEqual( err.error.message, 'Invalid --identifier provided: invalid@' diff --git a/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.ts b/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.ts index 858008c76e8..5d03b11c2c7 100644 --- a/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.ts +++ b/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.ts @@ -64,7 +64,7 @@ export class SandboxEventHandlerFactory { return; } const deployError = args[0]; - if (deployError && deployError instanceof AmplifyError) { + if (deployError && AmplifyError.isAmplifyError(deployError)) { await usageDataEmitter.emitFailure(deployError, { command: 'Sandbox', }); diff --git a/packages/cli/src/error_handler.ts b/packages/cli/src/error_handler.ts index 522f2cb9ec1..806020e9861 100644 --- a/packages/cli/src/error_handler.ts +++ b/packages/cli/src/error_handler.ts @@ -111,7 +111,7 @@ const handleError = async ({ printMessagePreamble?.(); - if (error instanceof AmplifyError) { + if (AmplifyError.isAmplifyError(error)) { printer.print(format.error(`${error.name}: ${error.message}`)); if (error.resolution) { @@ -141,7 +141,7 @@ const handleError = async ({ } await usageDataEmitter?.emitFailure( - error instanceof AmplifyError + AmplifyError.isAmplifyError(error) ? error : AmplifyError.fromError( error && error instanceof Error ? error : new Error(message) diff --git a/packages/eslint-rules/src/index.ts b/packages/eslint-rules/src/index.ts index 88ee3632028..2daf628e91c 100644 --- a/packages/eslint-rules/src/index.ts +++ b/packages/eslint-rules/src/index.ts @@ -2,9 +2,11 @@ import { noEmptyCatchRule } from './rules/no_empty_catch.js'; import { amplifyErrorNameRule } from './rules/amplify_error_name.js'; import { preferAmplifyErrorsRule } from './rules/prefer_amplify_errors.js'; import { noAmplifyErrors } from './rules/no_amplify_errors.js'; +import { amplifyErrorNoInstanceOf } from './rules/amplify_error_no_instance_of'; export const rules: Record = { 'amplify-error-name': amplifyErrorNameRule, + 'amplify-error-no-instanceof': amplifyErrorNoInstanceOf, 'no-empty-catch': noEmptyCatchRule, 'prefer-amplify-errors': preferAmplifyErrorsRule, 'no-amplify-errors': noAmplifyErrors, @@ -15,6 +17,7 @@ export const configs = { plugins: ['amplify-backend-rules'], rules: { 'amplify-backend-rules/amplify-error-name': 'error', + 'amplify-backend-rules/amplify-error-no-instanceof': 'error', 'amplify-backend-rules/no-empty-catch': 'error', 'amplify-backend-rules/prefer-amplify-errors': 'off', 'amplify-backend-rules/no-amplify-errors': 'off', diff --git a/packages/eslint-rules/src/rules/amplify_error_no_instance_of.test.ts b/packages/eslint-rules/src/rules/amplify_error_no_instance_of.test.ts new file mode 100644 index 00000000000..c805e88fbdd --- /dev/null +++ b/packages/eslint-rules/src/rules/amplify_error_no_instance_of.test.ts @@ -0,0 +1,28 @@ +import * as nodeTest from 'node:test'; +import { RuleTester } from '@typescript-eslint/rule-tester'; +import { amplifyErrorNoInstanceOf } from './amplify_error_no_instance_of.js'; + +RuleTester.afterAll = nodeTest.after; +// See https://typescript-eslint.io/packages/rule-tester/#with-specific-frameworks +// Node test runner methods return promises which are not relevant in the context of testing. +// We do ignore them in other places with void keyword. +// eslint-disable-next-line @typescript-eslint/no-misused-promises +RuleTester.it = nodeTest.it; +// eslint-disable-next-line @typescript-eslint/no-misused-promises +RuleTester.describe = nodeTest.describe; + +const ruleTester = new RuleTester(); + +ruleTester.run('amplify-error-no-instanceof', amplifyErrorNoInstanceOf, { + valid: ['e instanceof Error'], + invalid: [ + { + code: 'e instanceof AmplifyError', + errors: [ + { + messageId: 'noInstanceOfWithAmplifyError', + }, + ], + }, + ], +}); diff --git a/packages/eslint-rules/src/rules/amplify_error_no_instance_of.ts b/packages/eslint-rules/src/rules/amplify_error_no_instance_of.ts new file mode 100644 index 00000000000..bd040d2134b --- /dev/null +++ b/packages/eslint-rules/src/rules/amplify_error_no_instance_of.ts @@ -0,0 +1,41 @@ +import { ESLintUtils } from '@typescript-eslint/utils'; + +/** + * This rule flags empty catch blocks. Even if they contain comments. + * + * This rule differs from built in https://github.com/eslint/eslint/blob/main/lib/rules/no-empty.js + * in such a way that it uses typescript-eslint and typescript AST + * which does not include comments as statements in catch clause body block. + */ +export const amplifyErrorNoInstanceOf = ESLintUtils.RuleCreator.withoutDocs({ + create(context) { + return { + // This naming comes from @typescript-eslint/utils types. + // eslint-disable-next-line @typescript-eslint/naming-convention + BinaryExpression(node) { + if ( + node.operator === 'instanceof' && + node.right.type === 'Identifier' && + node.right.name === 'AmplifyError' + ) { + context.report({ + messageId: 'noInstanceOfWithAmplifyError', + node, + }); + } + }, + }; + }, + meta: { + docs: { + description: 'Instanceof operator must not be used with AmplifyError.', + }, + messages: { + noInstanceOfWithAmplifyError: + 'Do not use instanceof with AmplifyError. Use AmplifyError.isAmplifyError instead.', + }, + type: 'problem', + schema: [], + }, + defaultOptions: [], +}); diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index 928d4ada67a..b0230e19c64 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -24,6 +24,7 @@ export abstract class AmplifyError extends Error { static fromError: (error: unknown) => AmplifyError<'UnknownFault' | 'CredentialsError' | 'InvalidCommandInputError' | 'DomainNotFoundError' | 'SyntaxError'>; // (undocumented) static fromStderr: (_stderr: string) => AmplifyError | undefined; + static isAmplifyError: (error: unknown) => error is AmplifyError; // (undocumented) readonly link?: string; // (undocumented) diff --git a/packages/platform-core/src/errors/amplify_error.test.ts b/packages/platform-core/src/errors/amplify_error.test.ts index 1f19ac057d5..2da01c0e280 100644 --- a/packages/platform-core/src/errors/amplify_error.test.ts +++ b/packages/platform-core/src/errors/amplify_error.test.ts @@ -165,7 +165,7 @@ void describe('AmplifyError.fromError', async () => { yargsErrors.forEach((error) => { const actual = AmplifyError.fromError(error); assert.ok( - actual instanceof AmplifyError && + AmplifyError.isAmplifyError(actual) && actual.name === 'InvalidCommandInputError', `Failed the test for error ${error.message}` ); @@ -175,7 +175,8 @@ void describe('AmplifyError.fromError', async () => { const error = new Error('getaddrinfo ENOTFOUND some-domain.com'); const actual = AmplifyError.fromError(error); assert.ok( - actual instanceof AmplifyError && actual.name === 'DomainNotFoundError', + AmplifyError.isAmplifyError(actual) && + actual.name === 'DomainNotFoundError', `Failed the test for error ${error.message}` ); }); @@ -184,7 +185,7 @@ void describe('AmplifyError.fromError', async () => { error.name = 'SyntaxError'; const actual = AmplifyError.fromError(error); assert.ok( - actual instanceof AmplifyError && actual.name === 'SyntaxError', + AmplifyError.isAmplifyError(actual) && actual.name === 'SyntaxError', `Failed the test for error ${error.message}` ); }); diff --git a/packages/platform-core/src/errors/amplify_error.ts b/packages/platform-core/src/errors/amplify_error.ts index e9c524d5726..a61963a3203 100644 --- a/packages/platform-core/src/errors/amplify_error.ts +++ b/packages/platform-core/src/errors/amplify_error.ts @@ -44,7 +44,7 @@ export abstract class AmplifyError extends Error { this.code = options.code; this.link = options.link; - if (cause && cause instanceof AmplifyError) { + if (cause && AmplifyError.isAmplifyError(cause)) { cause.serializedError = undefined; } this.serializedError = JSON.stringify( @@ -98,6 +98,26 @@ export abstract class AmplifyError extends Error { return undefined; }; + /** + * This function is a type predicate for AmplifyError. + * See https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates. + * + * Checks if error is an AmplifyError by inspecting if required properties are set. + * This is recommended instead of instanceof operator. + * The instance of operator does not work as expected if AmplifyError class is loaded + * from multiple sources, for example when package manager decides to not de-duplicate dependencies. + * See https://github.com/nodejs/node/issues/17943. + */ + static isAmplifyError = (error: unknown): error is AmplifyError => { + return ( + error instanceof Error && + 'classification' in error && + (error.classification === 'ERROR' || error.classification === 'FAULT') && + typeof error.name === 'string' && + typeof error.message === 'string' + ); + }; + static fromError = ( error: unknown ): AmplifyError< diff --git a/packages/sandbox/src/file_watching_sandbox.ts b/packages/sandbox/src/file_watching_sandbox.ts index 39891c1347c..62e9a5cb94b 100644 --- a/packages/sandbox/src/file_watching_sandbox.ts +++ b/packages/sandbox/src/file_watching_sandbox.ts @@ -273,7 +273,7 @@ export class FileWatchingSandbox extends EventEmitter implements Sandbox { // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-userpool.html#cfn-cognito-userpool-aliasattributes // offer to recreate the sandbox or revert the change if ( - error instanceof AmplifyError && + AmplifyError.isAmplifyError(error) && error.name === 'CFNUpdateNotSupportedError' ) { await this.handleUnsupportedDestructiveChanges(options); @@ -385,7 +385,7 @@ export class FileWatchingSandbox extends EventEmitter implements Sandbox { message = `${message}\nCaused By: ${error.cause.message}\n`; } - if (error instanceof AmplifyError && error.resolution) { + if (AmplifyError.isAmplifyError(error) && error.resolution) { message = `${message}\nResolution: ${error.resolution}\n`; } } else message = String(error); From f2314b85e03badbf1a7174743e70aa2b69f27c69 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:18:36 -0800 Subject: [PATCH 090/199] Version Packages (#2195) Co-authored-by: github-actions[bot] --- .changeset/green-cups-jam.md | 10 ---------- .changeset/thin-candles-perform.md | 5 ----- packages/ai-constructs/CHANGELOG.md | 8 ++++++++ packages/ai-constructs/package.json | 4 ++-- packages/backend-data/CHANGELOG.md | 6 ++++++ packages/backend-data/package.json | 4 ++-- packages/backend-deployer/CHANGELOG.md | 8 ++++++++ packages/backend-deployer/package.json | 4 ++-- packages/backend/CHANGELOG.md | 9 +++++++++ packages/backend/package.json | 6 +++--- packages/cli/CHANGELOG.md | 10 ++++++++++ packages/cli/package.json | 8 ++++---- packages/platform-core/CHANGELOG.md | 6 ++++++ packages/platform-core/package.json | 2 +- packages/sandbox/CHANGELOG.md | 9 +++++++++ packages/sandbox/package.json | 6 +++--- 16 files changed, 73 insertions(+), 32 deletions(-) delete mode 100644 .changeset/green-cups-jam.md delete mode 100644 .changeset/thin-candles-perform.md diff --git a/.changeset/green-cups-jam.md b/.changeset/green-cups-jam.md deleted file mode 100644 index e9f2672f9cb..00000000000 --- a/.changeset/green-cups-jam.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@aws-amplify/platform-core': minor -'@aws-amplify/backend-deployer': patch -'@aws-amplify/backend-data': patch -'@aws-amplify/backend': patch -'@aws-amplify/sandbox': patch -'@aws-amplify/backend-cli': patch ---- - -Fix detection of AmplifyErrors diff --git a/.changeset/thin-candles-perform.md b/.changeset/thin-candles-perform.md deleted file mode 100644 index dbb3f0fe716..00000000000 --- a/.changeset/thin-candles-perform.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': patch ---- - -Add metadata to user agent in conversation handler runtime. diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index 1d965443802..48831fdf10c 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/ai-constructs +## 0.8.1 + +### Patch Changes + +- 1af5060: Add metadata to user agent in conversation handler runtime. +- Updated dependencies [583a3f2] + - @aws-amplify/platform-core@1.2.0 + ## 0.8.0 ### Minor Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index dff30ea297d..4ca1106feb1 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "0.8.0", + "version": "0.8.1", "type": "commonjs", "publishConfig": { "access": "public" @@ -27,7 +27,7 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/platform-core": "^1.1.0", + "@aws-amplify/platform-core": "^1.2.0", "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/client-bedrock-runtime": "^3.622.0", "@smithy/types": "^3.3.0", diff --git a/packages/backend-data/CHANGELOG.md b/packages/backend-data/CHANGELOG.md index 8b9078ce797..99884312f3b 100644 --- a/packages/backend-data/CHANGELOG.md +++ b/packages/backend-data/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/backend-data +## 1.1.7 + +### Patch Changes + +- 583a3f2: Fix detection of AmplifyErrors + ## 1.1.6 ### Patch Changes diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index bf836021cc6..69d5f245979 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-data", - "version": "1.1.6", + "version": "1.1.7", "type": "module", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "devDependencies": { "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.0.7" + "@aws-amplify/platform-core": "^1.2.0" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0", diff --git a/packages/backend-deployer/CHANGELOG.md b/packages/backend-deployer/CHANGELOG.md index c31dc826cce..63b1f4ec38c 100644 --- a/packages/backend-deployer/CHANGELOG.md +++ b/packages/backend-deployer/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-deployer +## 1.1.8 + +### Patch Changes + +- 583a3f2: Fix detection of AmplifyErrors +- Updated dependencies [583a3f2] + - @aws-amplify/platform-core@1.2.0 + ## 1.1.7 ### Patch Changes diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index 55f3c681fb2..555ebe641a2 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-deployer", - "version": "1.1.7", + "version": "1.1.8", "type": "module", "publishConfig": { "access": "public" @@ -19,7 +19,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.0.6", + "@aws-amplify/platform-core": "^1.2.0", "@aws-amplify/plugin-types": "^1.3.1", "execa": "^8.0.1", "tsx": "^4.6.1" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 7aac7239683..5e1f459d4dd 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @aws-amplify/backend +## 1.6.2 + +### Patch Changes + +- 583a3f2: Fix detection of AmplifyErrors +- Updated dependencies [583a3f2] + - @aws-amplify/platform-core@1.2.0 + - @aws-amplify/backend-data@1.1.7 + ## 1.6.1 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index b798b978492..25a1e023158 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.6.1", + "version": "1.6.2", "type": "module", "publishConfig": { "access": "public" @@ -28,13 +28,13 @@ "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/backend-auth": "^1.3.0", "@aws-amplify/backend-function": "^1.7.4", - "@aws-amplify/backend-data": "^1.1.6", + "@aws-amplify/backend-data": "^1.1.7", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.2", "@aws-amplify/client-config": "^1.5.2", - "@aws-amplify/platform-core": "^1.1.0", + "@aws-amplify/platform-core": "^1.2.0", "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/client-amplify": "^3.624.0", "lodash.snakecase": "^4.1.1" diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index c4c381a164f..8259daaccb7 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/backend-cli +## 1.4.1 + +### Patch Changes + +- 583a3f2: Fix detection of AmplifyErrors +- Updated dependencies [583a3f2] + - @aws-amplify/platform-core@1.2.0 + - @aws-amplify/backend-deployer@1.1.8 + - @aws-amplify/sandbox@1.2.5 + ## 1.4.0 ### Minor Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 19803d2a722..b765f084fad 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-cli", - "version": "1.4.0", + "version": "1.4.1", "description": "Command line interface for various Amplify tools", "bin": { "ampx": "lib/ampx.js", @@ -31,7 +31,7 @@ }, "homepage": "https://github.com/aws-amplify/amplify-backend#readme", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.6", + "@aws-amplify/backend-deployer": "^1.1.8", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.0", @@ -39,9 +39,9 @@ "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/form-generator": "^1.0.3", "@aws-amplify/model-generator": "^1.0.8", - "@aws-amplify/platform-core": "^1.0.5", + "@aws-amplify/platform-core": "^1.2.0", "@aws-amplify/plugin-types": "^1.3.1", - "@aws-amplify/sandbox": "^1.2.4", + "@aws-amplify/sandbox": "^1.2.5", "@aws-amplify/schema-generator": "^1.2.5", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", diff --git a/packages/platform-core/CHANGELOG.md b/packages/platform-core/CHANGELOG.md index 51c7f3c8f49..81f027b825c 100644 --- a/packages/platform-core/CHANGELOG.md +++ b/packages/platform-core/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/platform-core +## 1.2.0 + +### Minor Changes + +- 583a3f2: Fix detection of AmplifyErrors + ## 1.1.0 ### Minor Changes diff --git a/packages/platform-core/package.json b/packages/platform-core/package.json index d62d258aa92..c24f9b8382c 100644 --- a/packages/platform-core/package.json +++ b/packages/platform-core/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/platform-core", - "version": "1.1.0", + "version": "1.2.0", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/sandbox/CHANGELOG.md b/packages/sandbox/CHANGELOG.md index 8c047d66548..904304aaa8f 100644 --- a/packages/sandbox/CHANGELOG.md +++ b/packages/sandbox/CHANGELOG.md @@ -1,5 +1,14 @@ # @aws-amplify/sandbox +## 1.2.5 + +### Patch Changes + +- 583a3f2: Fix detection of AmplifyErrors +- Updated dependencies [583a3f2] + - @aws-amplify/platform-core@1.2.0 + - @aws-amplify/backend-deployer@1.1.8 + ## 1.2.4 ### Patch Changes diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index b28e802d6d8..d0897748011 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/sandbox", - "version": "1.2.4", + "version": "1.2.5", "type": "module", "publishConfig": { "access": "public" @@ -19,12 +19,12 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.6", + "@aws-amplify/backend-deployer": "^1.1.8", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.0", "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/platform-core": "^1.0.6", + "@aws-amplify/platform-core": "^1.2.0", "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", From 042f3a5c153b77e3f9e52974d5ade7789fafd909 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Tue, 5 Nov 2024 16:09:22 -0800 Subject: [PATCH 091/199] Handle type predicates in api check. (#2201) --- .../api_usage_statements_generators.ts | 13 ++++++++++++- .../without-breaks/project-without-breaks/API.md | 6 ++++++ .../project-without-breaks/src/index.ts | 10 ++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/scripts/components/api-changes-validator/api_usage_statements_generators.ts b/scripts/components/api-changes-validator/api_usage_statements_generators.ts index 3249c91d826..d4e94526a90 100644 --- a/scripts/components/api-changes-validator/api_usage_statements_generators.ts +++ b/scripts/components/api-changes-validator/api_usage_statements_generators.ts @@ -566,7 +566,18 @@ export class CallableUsageStatementsGenerator ).generate().usageStatement ?? ''; let returnValueAssignmentTarget = ''; if (this.functionType.type.kind !== ts.SyntaxKind.VoidKeyword) { - returnValueAssignmentTarget = `const returnValue: ${this.functionType.type.getText()} = `; + let returnType; + if (this.functionType.type.kind === ts.SyntaxKind.TypePredicate) { + // Example type predicate looks like this + // '(input: unknown) => input is SampleType;' + // It's a special syntax that tells compiler that it's safe to assume + // type after invoking the check. + // But when it comes to value assignment this is treated as boolean. + returnType = 'boolean'; + } else { + returnType = this.functionType.type.getText(); + } + returnValueAssignmentTarget = `const returnValue: ${returnType} = `; } const minParameterUsage = new CallableParameterUsageStatementsGenerator( diff --git a/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/API.md b/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/API.md index 410b6690e22..aec702b4f83 100644 --- a/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/API.md +++ b/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/API.md @@ -84,4 +84,10 @@ export type SampleTypeThatReferencesFunction = { export type SampleIgnoredType = { someProperty: string; }; + +export const sampleTypePredicate: (input: unknown) => input is SampleType; + +export class SampleClassWithTypePredicate { + static sampleTypePredicate: (input: unknown) => input is SampleType; +} ``` diff --git a/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/src/index.ts b/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/src/index.ts index a6f2201d71e..1243ed5af25 100644 --- a/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/src/index.ts +++ b/scripts/components/api-changes-validator/test-resources/test-projects/without-breaks/project-without-breaks/src/index.ts @@ -115,3 +115,13 @@ export type SampleTypeThatReferencesFunction = { export type SampleIgnoredType = { someProperty: number; }; + +export const sampleTypePredicate = (input: unknown): input is SampleType => { + throw new Error(); +}; + +export class SampleClassWithTypePredicate { + static sampleTypePredicate = (input: unknown): input is SampleType => { + throw new Error(); + }; +} From 7f2f68b9de49ca5fecacb670892faa1c548f278a Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 6 Nov 2024 12:23:49 -0800 Subject: [PATCH 092/199] Handle errors when checking CDK bootstrap (#2204) --- .changeset/eleven-numbers-hide.md | 5 +++++ .../src/cdk_error_mapper.test.ts | 20 +++++++++++++++++ .../backend-deployer/src/cdk_error_mapper.ts | 22 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 .changeset/eleven-numbers-hide.md diff --git a/.changeset/eleven-numbers-hide.md b/.changeset/eleven-numbers-hide.md new file mode 100644 index 00000000000..9e91b8a2372 --- /dev/null +++ b/.changeset/eleven-numbers-hide.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +Handle errors when checking CDK bootstrap. diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 8dff74dd374..20f01df933f 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -96,6 +96,26 @@ const testErrorMappings = [ errorName: 'BootstrapNotDetectedError', expectedDownstreamErrorMessage: 'Is this account bootstrapped', }, + { + errorMessage: + // eslint-disable-next-line spellcheck/spell-checker + "This CDK deployment requires bootstrap stack version '6', but during the confirmation via SSM parameter /cdk-bootstrap/hnb659fds/version the following error occurred: AccessDeniedException", + expectedTopLevelErrorMessage: + 'Unable to detect CDK bootstrap stack due to permission issues.', + errorName: 'BootstrapDetectionError', + expectedDownstreamErrorMessage: + // eslint-disable-next-line spellcheck/spell-checker + "This CDK deployment requires bootstrap stack version '6', but during the confirmation via SSM parameter /cdk-bootstrap/hnb659fds/version the following error occurred: AccessDeniedException", + }, + { + errorMessage: + "This CDK deployment requires bootstrap stack version '6', found '5'. Please run 'cdk bootstrap'.", + expectedTopLevelErrorMessage: + 'This AWS account and region has outdated CDK bootstrap stack.', + errorName: 'BootstrapOutdatedError', + expectedDownstreamErrorMessage: + "This CDK deployment requires bootstrap stack version '6', found '5'. Please run 'cdk bootstrap'.", + }, { errorMessage: 'Amplify Backend not found in amplify/backend.ts', expectedTopLevelErrorMessage: diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index a10c98e45d3..994791f4e3f 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -118,6 +118,26 @@ export class CdkErrorMapper { errorName: 'BootstrapNotDetectedError', classification: 'ERROR', }, + { + errorRegex: + /This CDK deployment requires bootstrap stack version \S+, found \S+\. Please run 'cdk bootstrap'\./, + humanReadableErrorMessage: + 'This AWS account and region has outdated CDK bootstrap stack.', + resolutionMessage: + 'Run `cdk bootstrap aws://{YOUR_ACCOUNT_ID}/{YOUR_REGION}` locally to re-bootstrap.', + errorName: 'BootstrapOutdatedError', + classification: 'ERROR', + }, + { + errorRegex: + /This CDK deployment requires bootstrap stack version \S+, but during the confirmation via SSM parameter \S+ the following error occurred: AccessDeniedException/, + humanReadableErrorMessage: + 'Unable to detect CDK bootstrap stack due to permission issues.', + resolutionMessage: + "Ensure that AWS credentials have an IAM policy that grants read access to 'arn:aws:ssm:*:*:parameter/cdk-bootstrap/*' SSM parameters.", + errorName: 'BootstrapDetectionError', + classification: 'ERROR', + }, { errorRegex: /This CDK CLI is not compatible with the CDK library used by your application\. Please upgrade the CLI to the latest version\./, @@ -326,6 +346,8 @@ export type CDKDeploymentError = | 'BackendBuildError' | 'BackendSynthError' | 'BootstrapNotDetectedError' + | 'BootstrapDetectionError' + | 'BootstrapOutdatedError' | 'CDKResolveAWSAccountError' | 'CDKVersionMismatchError' | 'CFNUpdateNotSupportedError' From f08abe44c0a02d80d3985f718e610140ed6cca3c Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 6 Nov 2024 15:04:45 -0800 Subject: [PATCH 093/199] Handle case when AWS region is configured as blank string (#2206) --- .changeset/short-dryers-tell.md | 5 +++++ packages/cli/src/command_middleware.test.ts | 13 +++++++++++++ packages/cli/src/command_middleware.ts | 10 +++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 .changeset/short-dryers-tell.md diff --git a/.changeset/short-dryers-tell.md b/.changeset/short-dryers-tell.md new file mode 100644 index 00000000000..267a7abdb4b --- /dev/null +++ b/.changeset/short-dryers-tell.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-cli': patch +--- + +Handle case when AWS region is configured as blank string diff --git a/packages/cli/src/command_middleware.test.ts b/packages/cli/src/command_middleware.test.ts index 313b4a20039..386e4c7ab27 100644 --- a/packages/cli/src/command_middleware.test.ts +++ b/packages/cli/src/command_middleware.test.ts @@ -122,6 +122,19 @@ void describe('commandMiddleware', () => { } }); + void it('throws error if region is blank', async () => { + process.env.AWS_REGION = ''; + delete process.env.AWS_DEFAULT_REGION; + try { + await commandMiddleware.ensureAwsCredentialAndRegion( + {} as ArgumentsCamelCase<{ profile: string | undefined }> + ); + assert.fail('expect to throw error'); + } catch (err) { + assert.match((err as Error).message, /The AWS region is blank/); + } + }); + void it('throws error if a profile is provided and no other credential providers', async () => { try { await commandMiddleware.ensureAwsCredentialAndRegion({ diff --git a/packages/cli/src/command_middleware.ts b/packages/cli/src/command_middleware.ts index a06d288f74d..c1da30912d6 100644 --- a/packages/cli/src/command_middleware.ts +++ b/packages/cli/src/command_middleware.ts @@ -60,8 +60,9 @@ export class CommandMiddleware { } // Check region. + let region: string | undefined = undefined; try { - await loadConfig(NODE_REGION_CONFIG_OPTIONS, { + region = await loadConfig(NODE_REGION_CONFIG_OPTIONS, { ignoreCache: true, })(); } catch (err) { @@ -77,6 +78,13 @@ export class CommandMiddleware { err as Error ); } + if (!region.trim()) { + throw new AmplifyUserError('InvalidCredentialError', { + message: 'The AWS region is blank', + resolution: + 'Ensure that a valid AWS region is provided in profile configuration or AWS_REGION environment variable.', + }); + } }; /** From 06bf37e746254d49c7a6a6074449be63505f1f08 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 7 Nov 2024 13:57:49 -0800 Subject: [PATCH 094/199] Fix region usage in e2e tests (#2210) --- .changeset/cool-zoos-enjoy.md | 2 ++ .../test-project-setup/data_storage_auth_with_triggers.ts | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 .changeset/cool-zoos-enjoy.md diff --git a/.changeset/cool-zoos-enjoy.md b/.changeset/cool-zoos-enjoy.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/cool-zoos-enjoy.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts index d9924bc2381..8412f6a8958 100644 --- a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts +++ b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts @@ -186,10 +186,12 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { ? environment[amplifySharedSecretNameKey] : createAmplifySharedSecretName(); const { region } = e2eToolingClientConfig; - const env = { + const env: Record = { [amplifySharedSecretNameKey]: this.amplifySharedSecret, - AWS_REGION: region ?? '', }; + if (region) { + env.AWS_REGION = region; + } await this.setUpDeployEnvironment(backendIdentifier); await super.deploy(backendIdentifier, env); From 469b9f2535ea45c6a4243fb9a1a58bfec21a6b96 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Fri, 8 Nov 2024 11:48:05 -0800 Subject: [PATCH 095/199] Split some of auth and functions e2e tests (#2211) --- .changeset/orange-days-look.md | 2 + .../src/test-e2e/backend_output.test.ts | 3 - ...nced_auth_and_functions.deployment.test.ts | 4 + ...dvanced_auth_and_functions.sandbox.test.ts | 4 + .../data_storage_auth_with_triggers.test.ts | 5 - .../advanced_auth_and_functions.ts | 335 ++++++++++++++++++ .../data_storage_auth_with_triggers.ts | 225 +----------- .../amplify/auth/resource.ts | 15 + .../amplify/backend.ts | 48 +++ .../func-src/handler_custom_email_sender.ts | 0 .../amplify/func-src/handler_no_minify.ts | 0 .../amplify/func-src/handler_with_aws_sdk.ts | 0 .../amplify/func-src/handler_with_aws_sqs.ts | 0 .../amplify/func-src/handler_with_ssm.ts | 0 .../amplify/function.ts | 30 ++ .../amplify/test_factories.ts | 17 + .../amplify/auth/resource.ts | 9 +- .../amplify/backend.ts | 44 --- .../amplify/function.ts | 29 -- .../amplify/test_factories.ts | 15 +- .../hotswap-update-files/function.ts | 29 -- 21 files changed, 463 insertions(+), 351 deletions(-) create mode 100644 .changeset/orange-days-look.md create mode 100644 packages/integration-tests/src/test-e2e/deployment/advanced_auth_and_functions.deployment.test.ts create mode 100644 packages/integration-tests/src/test-e2e/sandbox/advanced_auth_and_functions.sandbox.test.ts create mode 100644 packages/integration-tests/src/test-project-setup/advanced_auth_and_functions.ts create mode 100644 packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/auth/resource.ts create mode 100644 packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/backend.ts rename packages/integration-tests/src/test-projects/{data-storage-auth-with-triggers-ts => advanced-auth-and-functions}/amplify/func-src/handler_custom_email_sender.ts (100%) rename packages/integration-tests/src/test-projects/{data-storage-auth-with-triggers-ts => advanced-auth-and-functions}/amplify/func-src/handler_no_minify.ts (100%) rename packages/integration-tests/src/test-projects/{data-storage-auth-with-triggers-ts => advanced-auth-and-functions}/amplify/func-src/handler_with_aws_sdk.ts (100%) rename packages/integration-tests/src/test-projects/{data-storage-auth-with-triggers-ts => advanced-auth-and-functions}/amplify/func-src/handler_with_aws_sqs.ts (100%) rename packages/integration-tests/src/test-projects/{data-storage-auth-with-triggers-ts => advanced-auth-and-functions}/amplify/func-src/handler_with_ssm.ts (100%) create mode 100644 packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/function.ts create mode 100644 packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/test_factories.ts diff --git a/.changeset/orange-days-look.md b/.changeset/orange-days-look.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/orange-days-look.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/test-e2e/backend_output.test.ts b/packages/integration-tests/src/test-e2e/backend_output.test.ts index 0cde32198c3..8f8d5c6f2dc 100644 --- a/packages/integration-tests/src/test-e2e/backend_output.test.ts +++ b/packages/integration-tests/src/test-e2e/backend_output.test.ts @@ -21,7 +21,6 @@ import { S3Client } from '@aws-sdk/client-s3'; import { IAMClient } from '@aws-sdk/client-iam'; import { DeployedResourcesFinder } from '../find_deployed_resource.js'; import { DataStorageAuthWithTriggerTestProjectCreator } from '../test-project-setup/data_storage_auth_with_triggers.js'; -import { SQSClient } from '@aws-sdk/client-sqs'; import { setupDeployedBackendClient } from '../test-project-setup/setup_deployed_backend_client.js'; import { CloudTrailClient } from '@aws-sdk/client-cloudtrail'; @@ -46,7 +45,6 @@ void describe( const lambdaClient = new LambdaClient(e2eToolingClientConfig); const s3Client = new S3Client(e2eToolingClientConfig); const iamClient = new IAMClient(e2eToolingClientConfig); - const sqsClient = new SQSClient(e2eToolingClientConfig); const resourceFinder = new DeployedResourcesFinder(cfnClient); const dataStorageAuthWithTriggerTestProjectCreator = new DataStorageAuthWithTriggerTestProjectCreator( @@ -56,7 +54,6 @@ void describe( lambdaClient, s3Client, iamClient, - sqsClient, cloudTrailClient, resourceFinder ); diff --git a/packages/integration-tests/src/test-e2e/deployment/advanced_auth_and_functions.deployment.test.ts b/packages/integration-tests/src/test-e2e/deployment/advanced_auth_and_functions.deployment.test.ts new file mode 100644 index 00000000000..b988eb72c22 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/deployment/advanced_auth_and_functions.deployment.test.ts @@ -0,0 +1,4 @@ +import { defineDeploymentTest } from './deployment.test.template.js'; +import { AdvancedAuthAndFunctionsTestProjectCreator } from '../../test-project-setup/advanced_auth_and_functions.js'; + +defineDeploymentTest(new AdvancedAuthAndFunctionsTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/sandbox/advanced_auth_and_functions.sandbox.test.ts b/packages/integration-tests/src/test-e2e/sandbox/advanced_auth_and_functions.sandbox.test.ts new file mode 100644 index 00000000000..16118e48ea7 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/sandbox/advanced_auth_and_functions.sandbox.test.ts @@ -0,0 +1,4 @@ +import { defineSandboxTest } from './sandbox.test.template.js'; +import { AdvancedAuthAndFunctionsTestProjectCreator } from '../../test-project-setup/advanced_auth_and_functions.js'; + +defineSandboxTest(new AdvancedAuthAndFunctionsTestProjectCreator()); diff --git a/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts b/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts index 37c7e3c456c..a08fc6cc6a8 100644 --- a/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts +++ b/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts @@ -52,11 +52,6 @@ void it('data storage auth with triggers', () => { assertExpectedLogicalIds(templates.defaultNodeFunc, 'AWS::Lambda::Function', [ 'defaultNodeFunctionlambda5C194062', 'echoFunclambdaE17DCA46', - 'funcCustomEmailSenderlambda3CCBA9A6', - 'funcNoMinifylambda91CDF3E0', - 'funcWithAwsSdklambda5F770AD7', - 'funcWithSchedulelambda0B6E4271', - 'funcWithSsmlambda6A8824A1', 'handler2lambda1B9C7EFF', 'node16Functionlambda97ECC775', 'onUploadlambdaA252C959', diff --git a/packages/integration-tests/src/test-project-setup/advanced_auth_and_functions.ts b/packages/integration-tests/src/test-project-setup/advanced_auth_and_functions.ts new file mode 100644 index 00000000000..eb8056db5a4 --- /dev/null +++ b/packages/integration-tests/src/test-project-setup/advanced_auth_and_functions.ts @@ -0,0 +1,335 @@ +import fs from 'fs/promises'; +import { BackendIdentifier } from '@aws-amplify/plugin-types'; +import { createEmptyAmplifyProject } from './create_empty_amplify_project.js'; +import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; +import { TestProjectBase } from './test_project_base.js'; +import { TestProjectCreator } from './test_project_creator.js'; +import { DeployedResourcesFinder } from '../find_deployed_resource.js'; +import assert from 'node:assert'; +import { + GetFunctionCommand, + InvokeCommand, + LambdaClient, +} from '@aws-sdk/client-lambda'; +import { AmplifyClient } from '@aws-sdk/client-amplify'; +import { + DeleteMessageCommand, + ReceiveMessageCommand, + SQSClient, +} from '@aws-sdk/client-sqs'; +import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; +import { TextWriter, ZipReader } from '@zip.js/zip.js'; +import { + AdminCreateUserCommand, + CognitoIdentityProviderClient, +} from '@aws-sdk/client-cognito-identity-provider'; + +/** + * Creates test projects with advanced use cases of auth and functions categories. + */ +export class AdvancedAuthAndFunctionsTestProjectCreator + implements TestProjectCreator +{ + readonly name = 'advanced-auth-and-functions'; + + /** + * Creates project creator. + */ + constructor( + private readonly cfnClient: CloudFormationClient = new CloudFormationClient( + e2eToolingClientConfig + ), + private readonly amplifyClient: AmplifyClient = new AmplifyClient( + e2eToolingClientConfig + ), + private readonly lambdaClient: LambdaClient = new LambdaClient( + e2eToolingClientConfig + ), + private readonly sqsClient: SQSClient = new SQSClient( + e2eToolingClientConfig + ), + private readonly resourceFinder: DeployedResourcesFinder = new DeployedResourcesFinder(), + private readonly cognitoClient: CognitoIdentityProviderClient = new CognitoIdentityProviderClient( + e2eToolingClientConfig + ) + ) {} + + createProject = async (e2eProjectDir: string): Promise => { + const { projectName, projectRoot, projectAmplifyDir } = + await createEmptyAmplifyProject(this.name, e2eProjectDir); + + const project = new AdvancedAuthAndFunctionsTestProject( + projectName, + projectRoot, + projectAmplifyDir, + this.cfnClient, + this.amplifyClient, + this.lambdaClient, + this.sqsClient, + this.resourceFinder, + this.cognitoClient + ); + await fs.cp( + project.sourceProjectAmplifyDirURL, + project.projectAmplifyDirPath, + { + recursive: true, + } + ); + + return project; + }; +} + +/** + * Test project with advanced use cases of auth and functions categories. + */ +class AdvancedAuthAndFunctionsTestProject extends TestProjectBase { + readonly sourceProjectRootPath = + '../../src/test-projects/advanced-auth-and-functions'; + + readonly sourceProjectAmplifyDirURL: URL = new URL( + `${this.sourceProjectRootPath}/amplify`, + import.meta.url + ); + + /** + * Create a test project instance. + */ + constructor( + name: string, + projectDirPath: string, + projectAmplifyDirPath: string, + cfnClient: CloudFormationClient, + amplifyClient: AmplifyClient, + private readonly lambdaClient: LambdaClient, + private readonly sqsClient: SQSClient, + private readonly resourceFinder: DeployedResourcesFinder, + private readonly cognitoClient: CognitoIdentityProviderClient + ) { + super( + name, + projectDirPath, + projectAmplifyDirPath, + cfnClient, + amplifyClient + ); + } + + override async assertPostDeployment( + backendId: BackendIdentifier + ): Promise { + await super.assertPostDeployment(backendId); + + // Check that deployed lambdas are working correctly + + // find lambda functions + const funcWithSsm = await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::Lambda::Function', + (name) => name.includes('funcWithSsm') + ); + + const funcWithAwsSdk = await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::Lambda::Function', + (name) => name.includes('funcWithAwsSdk') + ); + + const funcWithSchedule = await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::Lambda::Function', + (name) => name.includes('funcWithSchedule') + ); + + const funcNoMinify = await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::Lambda::Function', + (name) => name.includes('funcNoMinify') + ); + const funcCustomEmailSender = + await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::Lambda::Function', + (name) => name.includes('funcCustomEmailSender') + ); + + assert.equal(funcWithSsm.length, 1); + assert.equal(funcWithAwsSdk.length, 1); + assert.equal(funcWithSchedule.length, 1); + assert.equal(funcCustomEmailSender.length, 1); + + await this.checkLambdaResponse(funcWithSsm[0], 'It is working'); + + // Custom email sender assertion + await this.assertCustomEmailSenderWorks(backendId); + + await this.assertScheduleInvokesFunction(backendId); + + const expectedNoMinifyChunk = [ + 'var handler = async () => {', + ' return "No minify";', + '};', + ].join('\n'); + await this.checkLambdaCode(funcNoMinify[0], expectedNoMinifyChunk); + } + + private checkLambdaResponse = async ( + lambdaName: string, + expectedResponse: unknown + ) => { + // invoke the lambda + const response = await this.lambdaClient.send( + new InvokeCommand({ FunctionName: lambdaName }) + ); + const responsePayload = JSON.parse( + response.Payload?.transformToString() || '' + ); + + // check expected response + assert.deepStrictEqual(responsePayload, expectedResponse); + }; + + private checkLambdaCode = async ( + lambdaName: string, + expectedCode: string + ) => { + // get the lambda code + const response = await this.lambdaClient.send( + new GetFunctionCommand({ FunctionName: lambdaName }) + ); + const codeUrl = response.Code?.Location; + assert(codeUrl !== undefined); + const fetchResponse = await fetch(codeUrl); + const zipReader = new ZipReader(fetchResponse.body!); + const entries = await zipReader.getEntries(); + const entry = entries.find((entry) => entry.filename.endsWith('index.mjs')); + assert(entry !== undefined); + const sourceCode = await entry.getData!(new TextWriter()); + assert(sourceCode.includes(expectedCode)); + }; + + private assertScheduleInvokesFunction = async ( + backendId: BackendIdentifier + ) => { + const TIMEOUT_MS = 1000 * 60 * 2; // 2 minutes + const startTime = Date.now(); + let receivedMessageCount = 0; + + const queue = await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::SQS::Queue', + (name) => name.includes('testFuncQueue') + ); + + // wait for schedule to invoke the function one time for it to send a message + while (Date.now() - startTime < TIMEOUT_MS) { + const response = await this.sqsClient.send( + new ReceiveMessageCommand({ + QueueUrl: queue[0], + WaitTimeSeconds: 20, + MaxNumberOfMessages: 10, + }) + ); + + if (response.Messages) { + receivedMessageCount += response.Messages.length; + + // delete messages afterwards + for (const message of response.Messages) { + await this.sqsClient.send( + new DeleteMessageCommand({ + QueueUrl: queue[0], + ReceiptHandle: message.ReceiptHandle, + }) + ); + } + } + } + + if (receivedMessageCount === 0) { + assert.fail( + `The scheduled function failed to invoke and send a message to the queue.` + ); + } + }; + + private assertCustomEmailSenderWorks = async ( + backendId: BackendIdentifier + ) => { + const TIMEOUT_MS = 1000 * 60 * 2; // 2 minutes + const startTime = Date.now(); + const queue = await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::SQS::Queue', + (name) => name.includes('customEmailSenderQueue') + ); + + assert.strictEqual(queue.length, 1, 'Custom email sender queue not found'); + + // Trigger an email sending operation + await this.triggerEmailSending(backendId); + + // Wait for the SQS message + let messageReceived = false; + while (Date.now() - startTime < TIMEOUT_MS && !messageReceived) { + const response = await this.sqsClient.send( + new ReceiveMessageCommand({ + QueueUrl: queue[0], + WaitTimeSeconds: 20, + }) + ); + + if (response.Messages && response.Messages.length > 0) { + messageReceived = true; + // Verify the message content + const messageBody = JSON.parse(response.Messages[0].Body || '{}'); + assert.strictEqual( + messageBody.message, + 'Custom Email Sender is working', + 'Unexpected message content' + ); + + // Delete the message + await this.sqsClient.send( + new DeleteMessageCommand({ + QueueUrl: queue[0], + ReceiptHandle: response.Messages[0].ReceiptHandle!, + }) + ); + } + } + + assert.strictEqual( + messageReceived, + true, + 'Custom email sender was not triggered within the timeout period' + ); + }; + + private triggerEmailSending = async (backendId: BackendIdentifier) => { + const userPoolId = await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::Cognito::UserPool', + () => true + ); + + assert.strictEqual(userPoolId.length, 1, 'User pool not found'); + + const username = `testuser_${Date.now()}@example.com`; + const password = 'TestPassword123!'; + + await this.cognitoClient.send( + new AdminCreateUserCommand({ + UserPoolId: userPoolId[0], + Username: username, + TemporaryPassword: password, + UserAttributes: [ + { Name: 'email', Value: username }, + { Name: 'email_verified', Value: 'true' }, + ], + }) + ); + // The creation of a new user should trigger the custom email sender + }; +} diff --git a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts index 8412f6a8958..5567a5958ef 100644 --- a/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts +++ b/packages/integration-tests/src/test-project-setup/data_storage_auth_with_triggers.ts @@ -9,11 +9,7 @@ import path from 'path'; import { TestProjectCreator } from './test_project_creator.js'; import { DeployedResourcesFinder } from '../find_deployed_resource.js'; import assert from 'node:assert'; -import { - GetFunctionCommand, - InvokeCommand, - LambdaClient, -} from '@aws-sdk/client-lambda'; +import { InvokeCommand, LambdaClient } from '@aws-sdk/client-lambda'; import { amplifySharedSecretNameKey, createAmplifySharedSecretName, @@ -21,22 +17,13 @@ import { import { HeadBucketCommand, S3Client } from '@aws-sdk/client-s3'; import { GetRoleCommand, IAMClient } from '@aws-sdk/client-iam'; import { AmplifyClient } from '@aws-sdk/client-amplify'; -import { - DeleteMessageCommand, - ReceiveMessageCommand, - SQSClient, -} from '@aws-sdk/client-sqs'; + import { CloudTrailClient, LookupEventsCommand, } from '@aws-sdk/client-cloudtrail'; import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; import isMatch from 'lodash.ismatch'; -import { TextWriter, ZipReader } from '@zip.js/zip.js'; -import { - AdminCreateUserCommand, - CognitoIdentityProviderClient, -} from '@aws-sdk/client-cognito-identity-provider'; /** * Creates test projects with data, storage, and auth categories. @@ -66,16 +53,10 @@ export class DataStorageAuthWithTriggerTestProjectCreator private readonly iamClient: IAMClient = new IAMClient( e2eToolingClientConfig ), - private readonly sqsClient: SQSClient = new SQSClient( - e2eToolingClientConfig - ), private readonly cloudTrailClient: CloudTrailClient = new CloudTrailClient( e2eToolingClientConfig ), - private readonly resourceFinder: DeployedResourcesFinder = new DeployedResourcesFinder(), - private readonly cognitoClient: CognitoIdentityProviderClient = new CognitoIdentityProviderClient( - e2eToolingClientConfig - ) + private readonly resourceFinder: DeployedResourcesFinder = new DeployedResourcesFinder() ) {} createProject = async (e2eProjectDir: string): Promise => { @@ -92,10 +73,8 @@ export class DataStorageAuthWithTriggerTestProjectCreator this.lambdaClient, this.s3Client, this.iamClient, - this.sqsClient, this.cloudTrailClient, - this.resourceFinder, - this.cognitoClient + this.resourceFinder ); await fs.cp( project.sourceProjectAmplifyDirURL, @@ -114,7 +93,7 @@ export class DataStorageAuthWithTriggerTestProjectCreator */ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { // Note that this is pointing to the non-compiled project directory - // This allows us to test that we are able to deploy js, cjs, ts, etc without compiling with tsc first + // This allows us to test that we are able to deploy js, cjs, ts, etc. without compiling with tsc first readonly sourceProjectRootPath = '../../src/test-projects/data-storage-auth-with-triggers-ts'; @@ -160,10 +139,8 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { private readonly lambdaClient: LambdaClient, private readonly s3Client: S3Client, private readonly iamClient: IAMClient, - private readonly sqsClient: SQSClient, private readonly cloudTrailClient: CloudTrailClient, - private readonly resourceFinder: DeployedResourcesFinder, - private readonly cognitoClient: CognitoIdentityProviderClient + private readonly resourceFinder: DeployedResourcesFinder ) { super( name, @@ -250,42 +227,8 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { (name) => name.includes('node16Function') ); - const funcWithSsm = await this.resourceFinder.findByBackendIdentifier( - backendId, - 'AWS::Lambda::Function', - (name) => name.includes('funcWithSsm') - ); - - const funcWithAwsSdk = await this.resourceFinder.findByBackendIdentifier( - backendId, - 'AWS::Lambda::Function', - (name) => name.includes('funcWithAwsSdk') - ); - - const funcWithSchedule = await this.resourceFinder.findByBackendIdentifier( - backendId, - 'AWS::Lambda::Function', - (name) => name.includes('funcWithSchedule') - ); - - const funcNoMinify = await this.resourceFinder.findByBackendIdentifier( - backendId, - 'AWS::Lambda::Function', - (name) => name.includes('funcNoMinify') - ); - const funcCustomEmailSender = - await this.resourceFinder.findByBackendIdentifier( - backendId, - 'AWS::Lambda::Function', - (name) => name.includes('funcCustomEmailSender') - ); - assert.equal(defaultNodeLambda.length, 1); assert.equal(node16Lambda.length, 1); - assert.equal(funcWithSsm.length, 1); - assert.equal(funcWithAwsSdk.length, 1); - assert.equal(funcWithSchedule.length, 1); - assert.equal(funcCustomEmailSender.length, 1); const expectedResponse = { s3TestContent: 'this is some test content', @@ -296,19 +239,6 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { await this.checkLambdaResponse(defaultNodeLambda[0], expectedResponse); await this.checkLambdaResponse(node16Lambda[0], expectedResponse); - await this.checkLambdaResponse(funcWithSsm[0], 'It is working'); - - // Custom email sender assertion - await this.assertCustomEmailSenderWorks(backendId); - - await this.assertScheduleInvokesFunction(backendId); - - const expectedNoMinifyChunk = [ - 'var handler = async () => {', - ' return "No minify";', - '};', - ].join('\n'); - await this.checkLambdaCode(funcNoMinify[0], expectedNoMinifyChunk); const bucketName = await this.resourceFinder.findByBackendIdentifier( backendId, @@ -457,25 +387,6 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { assert.deepStrictEqual(responsePayload, expectedResponse); }; - private checkLambdaCode = async ( - lambdaName: string, - expectedCode: string - ) => { - // get the lambda code - const response = await this.lambdaClient.send( - new GetFunctionCommand({ FunctionName: lambdaName }) - ); - const codeUrl = response.Code?.Location; - assert(codeUrl !== undefined); - const fetchResponse = await fetch(codeUrl); - const zipReader = new ZipReader(fetchResponse.body!); - const entries = await zipReader.getEntries(); - const entry = entries.find((entry) => entry.filename.endsWith('index.mjs')); - assert(entry !== undefined); - const sourceCode = await entry.getData!(new TextWriter()); - assert(sourceCode.includes(expectedCode)); - }; - private assertExpectedCleanup = async () => { await this.waitForBucketDeletion(this.testBucketName); await this.assertRolesDoNotExist(this.testRoleNames); @@ -617,128 +528,4 @@ class DataStorageAuthWithTriggerTestProject extends TestProjectBase { throw err; } }; - - private assertScheduleInvokesFunction = async ( - backendId: BackendIdentifier - ) => { - const TIMEOUT_MS = 1000 * 60 * 2; // 2 minutes - const startTime = Date.now(); - let receivedMessageCount = 0; - - const queue = await this.resourceFinder.findByBackendIdentifier( - backendId, - 'AWS::SQS::Queue', - (name) => name.includes('testFuncQueue') - ); - - // wait for schedule to invoke the function one time for it to send a message - while (Date.now() - startTime < TIMEOUT_MS) { - const response = await this.sqsClient.send( - new ReceiveMessageCommand({ - QueueUrl: queue[0], - WaitTimeSeconds: 20, - MaxNumberOfMessages: 10, - }) - ); - - if (response.Messages) { - receivedMessageCount += response.Messages.length; - - // delete messages afterwards - for (const message of response.Messages) { - await this.sqsClient.send( - new DeleteMessageCommand({ - QueueUrl: queue[0], - ReceiptHandle: message.ReceiptHandle, - }) - ); - } - } - } - - if (receivedMessageCount === 0) { - assert.fail( - `The scheduled function failed to invoke and send a message to the queue.` - ); - } - }; - - private assertCustomEmailSenderWorks = async ( - backendId: BackendIdentifier - ) => { - const TIMEOUT_MS = 1000 * 60 * 2; // 2 minutes - const startTime = Date.now(); - const queue = await this.resourceFinder.findByBackendIdentifier( - backendId, - 'AWS::SQS::Queue', - (name) => name.includes('customEmailSenderQueue') - ); - - assert.strictEqual(queue.length, 1, 'Custom email sender queue not found'); - - // Trigger an email sending operation - await this.triggerEmailSending(backendId); - - // Wait for the SQS message - let messageReceived = false; - while (Date.now() - startTime < TIMEOUT_MS && !messageReceived) { - const response = await this.sqsClient.send( - new ReceiveMessageCommand({ - QueueUrl: queue[0], - WaitTimeSeconds: 20, - }) - ); - - if (response.Messages && response.Messages.length > 0) { - messageReceived = true; - // Verify the message content - const messageBody = JSON.parse(response.Messages[0].Body || '{}'); - assert.strictEqual( - messageBody.message, - 'Custom Email Sender is working', - 'Unexpected message content' - ); - - // Delete the message - await this.sqsClient.send( - new DeleteMessageCommand({ - QueueUrl: queue[0], - ReceiptHandle: response.Messages[0].ReceiptHandle!, - }) - ); - } - } - - assert.strictEqual( - messageReceived, - true, - 'Custom email sender was not triggered within the timeout period' - ); - }; - - private triggerEmailSending = async (backendId: BackendIdentifier) => { - const userPoolId = await this.resourceFinder.findByBackendIdentifier( - backendId, - 'AWS::Cognito::UserPool', - () => true - ); - - assert.strictEqual(userPoolId.length, 1, 'User pool not found'); - - const username = `testuser_${Date.now()}@example.com`; - const password = 'TestPassword123!'; - - await this.cognitoClient.send( - new AdminCreateUserCommand({ - UserPoolId: userPoolId[0], - Username: username, - TemporaryPassword: password, - UserAttributes: [ - { Name: 'email', Value: username }, - { Name: 'email_verified', Value: 'true' }, - ], - }) - ); - // The creation of a new user should trigger the custom email sender - }; } diff --git a/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/auth/resource.ts b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/auth/resource.ts new file mode 100644 index 00000000000..d4676a53901 --- /dev/null +++ b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/auth/resource.ts @@ -0,0 +1,15 @@ +import { defineAuth } from '@aws-amplify/backend'; +import { funcCustomEmailSender } from '../function.js'; + +const customEmailSenderFunction = { + handler: funcCustomEmailSender, +}; + +export const auth = defineAuth({ + loginWith: { + email: true, + }, + senders: { + email: customEmailSenderFunction, + }, +}); diff --git a/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/backend.ts b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/backend.ts new file mode 100644 index 00000000000..406c9390e83 --- /dev/null +++ b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/backend.ts @@ -0,0 +1,48 @@ +import { defineBackend } from '@aws-amplify/backend'; +import { authAndFunctions } from './test_factories.js'; +import { Queue } from 'aws-cdk-lib/aws-sqs'; +import { Role } from 'aws-cdk-lib/aws-iam'; +import { Stack } from 'aws-cdk-lib'; + +const backend = defineBackend(authAndFunctions); + +const scheduleFunctionLambda = backend.funcWithSchedule.resources.lambda; +const scheduleFunctionLambdaRole = scheduleFunctionLambda.role; +const queueStack = Stack.of(scheduleFunctionLambda); + +const queue = new Queue(queueStack, 'amplify-testFuncQueue'); + +if (scheduleFunctionLambdaRole) { + queue.grantSendMessages( + Role.fromRoleArn( + queueStack, + 'LambdaExecutionRole', + scheduleFunctionLambdaRole.roleArn + ) + ); +} +backend.funcWithSchedule.addEnvironment('SQS_QUEUE_URL', queue.queueUrl); + +// Queue setup for customEmailSender + +const customEmailSenderLambda = backend.funcCustomEmailSender.resources.lambda; +const customEmailSenderLambdaRole = customEmailSenderLambda.role; +const customEmailSenderQueueStack = Stack.of(customEmailSenderLambda); +const emailSenderQueue = new Queue( + customEmailSenderQueueStack, + 'amplify-customEmailSenderQueue' +); + +if (customEmailSenderLambdaRole) { + emailSenderQueue.grantSendMessages( + Role.fromRoleArn( + customEmailSenderQueueStack, + 'CustomEmailSenderLambdaExecutionRole', + customEmailSenderLambdaRole.roleArn + ) + ); +} +backend.funcCustomEmailSender.addEnvironment( + 'CUSTOM_EMAIL_SENDER_SQS_QUEUE_URL', + emailSenderQueue.queueUrl +); diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_custom_email_sender.ts b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_custom_email_sender.ts similarity index 100% rename from packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_custom_email_sender.ts rename to packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_custom_email_sender.ts diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_no_minify.ts b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_no_minify.ts similarity index 100% rename from packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_no_minify.ts rename to packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_no_minify.ts diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_with_aws_sdk.ts b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_with_aws_sdk.ts similarity index 100% rename from packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_with_aws_sdk.ts rename to packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_with_aws_sdk.ts diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_with_aws_sqs.ts b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_with_aws_sqs.ts similarity index 100% rename from packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_with_aws_sqs.ts rename to packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_with_aws_sqs.ts diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_with_ssm.ts b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_with_ssm.ts similarity index 100% rename from packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/handler_with_ssm.ts rename to packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_with_ssm.ts diff --git a/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/function.ts b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/function.ts new file mode 100644 index 00000000000..a4f30ef66f1 --- /dev/null +++ b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/function.ts @@ -0,0 +1,30 @@ +import { defineFunction } from '@aws-amplify/backend'; + +export const funcWithSsm = defineFunction({ + name: 'funcWithSsm', + entry: './func-src/handler_with_ssm.ts', +}); + +export const funcWithAwsSdk = defineFunction({ + name: 'funcWithAwsSdk', + entry: './func-src/handler_with_aws_sdk.ts', +}); + +export const funcWithSchedule = defineFunction({ + name: 'funcWithSchedule', + entry: './func-src/handler_with_aws_sqs.ts', + schedule: '* * * * ?', +}); + +export const funcNoMinify = defineFunction({ + name: 'funcNoMinify', + entry: './func-src/handler_no_minify.ts', + bundling: { + minify: false, + }, +}); + +export const funcCustomEmailSender = defineFunction({ + name: 'funcCustomEmailSender', + entry: './func-src/handler_custom_email_sender.ts', +}); diff --git a/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/test_factories.ts b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/test_factories.ts new file mode 100644 index 00000000000..4e72f34424b --- /dev/null +++ b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/test_factories.ts @@ -0,0 +1,17 @@ +import { + funcCustomEmailSender, + funcNoMinify, + funcWithAwsSdk, + funcWithSchedule, + funcWithSsm, +} from './function.js'; +import { auth } from './auth/resource.js'; + +export const authAndFunctions = { + auth, + funcWithSsm, + funcWithAwsSdk, + funcWithSchedule, + funcNoMinify, + funcCustomEmailSender, +}; diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts index 14c55e503ec..92733f12ec8 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/auth/resource.ts @@ -1,9 +1,5 @@ import { defineAuth, secret } from '@aws-amplify/backend'; -import { defaultNodeFunc, funcCustomEmailSender } from '../function.js'; - -const customEmailSenderFunction = { - handler: funcCustomEmailSender, -}; +import { defaultNodeFunc } from '../function.js'; export const auth = defineAuth({ loginWith: { @@ -25,9 +21,6 @@ export const auth = defineAuth({ logoutUrls: ['https://logout.com'], }, }, - senders: { - email: customEmailSenderFunction, - }, triggers: { postConfirmation: defaultNodeFunc, }, diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/backend.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/backend.ts index c47ce414bd1..8fdf38f0d28 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/backend.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/backend.ts @@ -1,52 +1,8 @@ import { defineBackend } from '@aws-amplify/backend'; import { dataStorageAuthWithTriggers } from './test_factories.js'; -import { Queue } from 'aws-cdk-lib/aws-sqs'; -import { Role } from 'aws-cdk-lib/aws-iam'; -import { Stack } from 'aws-cdk-lib'; const backend = defineBackend(dataStorageAuthWithTriggers); backend.defaultNodeFunc.addEnvironment('newKey', 'newValue'); // Change precedence of Editors group so Admins group has the lowest precedence backend.auth.resources.groups['Editors'].cfnUserGroup.precedence = 2; - -const scheduleFunctionLambda = backend.funcWithSchedule.resources.lambda; -const scheduleFunctionLambdaRole = scheduleFunctionLambda.role; -const queueStack = Stack.of(scheduleFunctionLambda); - -const queue = new Queue(queueStack, 'amplify-testFuncQueue'); - -if (scheduleFunctionLambdaRole) { - queue.grantSendMessages( - Role.fromRoleArn( - queueStack, - 'LambdaExecutionRole', - scheduleFunctionLambdaRole.roleArn - ) - ); -} -backend.funcWithSchedule.addEnvironment('SQS_QUEUE_URL', queue.queueUrl); - -// Queue setup for customEmailSender - -const customEmailSenderLambda = backend.funcCustomEmailSender.resources.lambda; -const customEmailSenderLambdaRole = customEmailSenderLambda.role; -const customEmailSenderQueueStack = Stack.of(customEmailSenderLambda); -const emailSenderQueue = new Queue( - customEmailSenderQueueStack, - 'amplify-customEmailSenderQueue' -); - -if (customEmailSenderLambdaRole) { - emailSenderQueue.grantSendMessages( - Role.fromRoleArn( - customEmailSenderQueueStack, - 'CustomEmailSenderLambdaExecutionRole', - customEmailSenderLambdaRole.roleArn - ) - ); -} -backend.funcCustomEmailSender.addEnvironment( - 'CUSTOM_EMAIL_SENDER_SQS_QUEUE_URL', - emailSenderQueue.queueUrl -); diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/function.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/function.ts index 7265fb2ac49..cfaf6da0a42 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/function.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/function.ts @@ -34,32 +34,3 @@ export const onUpload = defineFunction({ name: 'onUpload', entry: './func-src/handler.ts', }); - -export const funcWithSsm = defineFunction({ - name: 'funcWithSsm', - entry: './func-src/handler_with_ssm.ts', -}); - -export const funcWithAwsSdk = defineFunction({ - name: 'funcWithAwsSdk', - entry: './func-src/handler_with_aws_sdk.ts', -}); - -export const funcWithSchedule = defineFunction({ - name: 'funcWithSchedule', - entry: './func-src/handler_with_aws_sqs.ts', - schedule: '* * * * ?', -}); - -export const funcNoMinify = defineFunction({ - name: 'funcNoMinify', - entry: './func-src/handler_no_minify.ts', - bundling: { - minify: false, - }, -}); - -export const funcCustomEmailSender = defineFunction({ - name: 'funcCustomEmailSender', - entry: './func-src/handler_custom_email_sender.ts', -}); diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/test_factories.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/test_factories.ts index 73ee6b04798..fa024e13fc9 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/test_factories.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/test_factories.ts @@ -1,13 +1,5 @@ import { data } from './data/resource.js'; -import { - defaultNodeFunc, - funcWithSsm, - funcWithAwsSdk, - node16Func, - funcWithSchedule, - funcNoMinify, - funcCustomEmailSender, -} from './function.js'; +import { defaultNodeFunc, node16Func } from './function.js'; import { storage } from './storage/resource.js'; import { auth } from './auth/resource.js'; @@ -17,9 +9,4 @@ export const dataStorageAuthWithTriggers = { defaultNodeFunc, data, node16Func, - funcWithSsm, - funcWithAwsSdk, - funcWithSchedule, - funcNoMinify, - funcCustomEmailSender, }; diff --git a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/hotswap-update-files/function.ts b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/hotswap-update-files/function.ts index a0abf3e3021..6032d8c60f1 100644 --- a/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/hotswap-update-files/function.ts +++ b/packages/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/hotswap-update-files/function.ts @@ -36,32 +36,3 @@ export const onUpload = defineFunction({ name: 'onUpload', entry: './func-src/handler.ts', }); - -export const funcWithSsm = defineFunction({ - name: 'funcWithSsm', - entry: './func-src/handler_with_ssm.ts', -}); - -export const funcWithAwsSdk = defineFunction({ - name: 'funcWithAwsSdk', - entry: './func-src/handler_with_aws_sdk.ts', -}); - -export const funcWithSchedule = defineFunction({ - name: 'funcWithSchedule', - entry: './func-src/handler_with_aws_sqs.ts', - schedule: '* * * * ?', -}); - -export const funcNoMinify = defineFunction({ - name: 'funcNoMinify', - entry: './func-src/handler_no_minify.ts', - bundling: { - minify: false, - }, -}); - -export const funcCustomEmailSender = defineFunction({ - name: 'funcCustomEmailSender', - entry: './func-src/handler_custom_email_sender.ts', -}); From 06e4def3eded2e4127c4501a4dce669abba900db Mon Sep 17 00:00:00 2001 From: vigy02 Date: Fri, 8 Nov 2024 15:57:43 -0800 Subject: [PATCH 096/199] Added retry mechanism to catch known errors for health checks (#2209) * Added retry mechanism to catch known errors * Updated the error message to cover multiple scenarios * Added Retry Mechanism that can work for any tests * Update .changeset/nine-toes-dress.md Co-authored-by: Kamil Sobol * chore:Update API * Refined retry mechanism removed timeout and delay * chore: Update API * chore: update API * Updated error check for test case * Updated retry test case condition * updated the maxretry parameter * chore: update changeset * chore: update changeset * Updated a test case without retry * Update packages/integration-tests/src/amplify_app_pool.ts Co-authored-by: Kamil Sobol * updated retry mechanism --------- Co-authored-by: Kamil Sobol --- .changeset/nine-toes-dress.md | 2 + .eslint_dictionary.json | 1 + .../integration-tests/src/amplify_app_pool.ts | 77 +++++++++++-------- .../src/package_manager_sanity_checks.test.ts | 43 +++++++---- packages/integration-tests/src/retry.ts | 32 ++++++++ 5 files changed, 111 insertions(+), 44 deletions(-) create mode 100644 .changeset/nine-toes-dress.md create mode 100644 packages/integration-tests/src/retry.ts diff --git a/.changeset/nine-toes-dress.md b/.changeset/nine-toes-dress.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/nine-toes-dress.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index cd255e5445a..4f0f494eee8 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -136,6 +136,7 @@ "renderer", "repo", "resolvers", + "retryable", "saml", "scala", "schema", diff --git a/packages/integration-tests/src/amplify_app_pool.ts b/packages/integration-tests/src/amplify_app_pool.ts index f3cf955e502..026a4625d0a 100644 --- a/packages/integration-tests/src/amplify_app_pool.ts +++ b/packages/integration-tests/src/amplify_app_pool.ts @@ -13,6 +13,7 @@ import { } from '@aws-sdk/client-amplify'; import { shortUuid } from './short_uuid.js'; import { e2eToolingClientConfig } from './e2e_tooling_client_config.js'; +import { runWithRetry } from './retry.js'; export type TestBranch = { readonly appId: string; @@ -46,42 +47,46 @@ class DefaultAmplifyAppPool implements AmplifyAppPool { } fetchTestBranchDetails = async (testBranch: TestBranch): Promise => { - const branch = ( - await this.amplifyClient.send( - new GetBranchCommand({ - appId: testBranch.appId, - branchName: testBranch.branchName, - }) - ) - ).branch; - if (!branch) { - throw new Error( - `Failed to retrieve ${testBranch.branchName} branch of app ${testBranch.appId}` - ); - } - return branch; + return this.retryableOperation(async () => { + const branch = ( + await this.amplifyClient.send( + new GetBranchCommand({ + appId: testBranch.appId, + branchName: testBranch.branchName, + }) + ) + ).branch; + if (!branch) { + throw new Error( + `Failed to retrieve ${testBranch.branchName} branch of app ${testBranch.appId}` + ); + } + return branch; + }); }; createTestBranch = async (): Promise => { - const app = await this.getAppWithCapacity(); - const branch = ( - await this.amplifyClient.send( - new CreateBranchCommand({ - branchName: `${this.testBranchPrefix}${shortUuid()}`, + return this.retryableOperation(async () => { + const app = await this.getAppWithCapacity(); + const branch = ( + await this.amplifyClient.send( + new CreateBranchCommand({ + branchName: `${this.testBranchPrefix}${shortUuid()}`, + appId: app.appId, + }) + ) + ).branch; + if (app.appId && branch?.branchName) { + const testBranch: TestBranch = { appId: app.appId, - }) - ) - ).branch; - if (app.appId && branch?.branchName) { - const testBranch: TestBranch = { - appId: app.appId, - branchName: branch.branchName, - }; - this.branchesCreated.push(testBranch); - return testBranch; - } + branchName: branch.branchName, + }; + this.branchesCreated.push(testBranch); + return testBranch; + } - throw new Error('Unable to create branch'); + throw new Error('Unable to create branch'); + }); }; private listAllTestAmplifyApps = async (): Promise> => { @@ -173,6 +178,16 @@ class DefaultAmplifyAppPool implements AmplifyAppPool { } } }; + + private retryableOperation = (operation: () => Promise) => { + return runWithRetry(operation, (error) => { + // Add specific error conditions here that warrant a retry + return ( + error.message.includes('Unexpected token') || + error.message.includes('Bad control character') + ); + }); + }; } export const amplifyAppPool: AmplifyAppPool = new DefaultAmplifyAppPool( diff --git a/packages/integration-tests/src/package_manager_sanity_checks.test.ts b/packages/integration-tests/src/package_manager_sanity_checks.test.ts index cc90bcd05d0..cafc707117e 100644 --- a/packages/integration-tests/src/package_manager_sanity_checks.test.ts +++ b/packages/integration-tests/src/package_manager_sanity_checks.test.ts @@ -26,6 +26,7 @@ import { runWithPackageManager, } from './process-controller/process_controller.js'; import { amplifyAtTag } from './constants.js'; +import { runWithRetry } from './retry.js'; void describe('getting started happy path', async () => { let branchBackendIdentifier: BackendIdentifier; @@ -81,19 +82,35 @@ void describe('getting started happy path', async () => { if (packageManager === 'pnpm' && process.platform === 'win32') { return; } - if (packageManager === 'yarn-classic') { - await execa('yarn', ['add', 'create-amplify'], { cwd: tempDir }); - await execaCommand('./node_modules/.bin/create-amplify --yes --debug', { - cwd: tempDir, - env: { npm_config_user_agent: 'yarn/1.22.21' }, - }); - } else { - await runPackageManager( - packageManager, - ['create', amplifyAtTag, '--yes'], - tempDir - ).run(); - } + + await runWithRetry( + async () => { + if (packageManager === 'yarn-classic') { + await execa('yarn', ['add', 'create-amplify'], { cwd: tempDir }); + await execaCommand( + './node_modules/.bin/create-amplify --yes --debug', + { + cwd: tempDir, + env: { npm_config_user_agent: 'yarn/1.22.21' }, + } + ); + } else { + await runPackageManager( + packageManager, + ['create', amplifyAtTag, '--yes'], + tempDir + ).run(); + } + }, + (error) => { + // Retry on network-related errors or command failures + return ( + error.message.includes('exit code 1') && + (error.message.includes('aws-amplify') || + error.message.includes('Command failed with exit code 1: yarn add')) + ); + } + ); const pathPrefix = path.join(tempDir, 'amplify'); diff --git a/packages/integration-tests/src/retry.ts b/packages/integration-tests/src/retry.ts new file mode 100644 index 00000000000..b7804c115b5 --- /dev/null +++ b/packages/integration-tests/src/retry.ts @@ -0,0 +1,32 @@ +/** + * Executes an asynchronous operation with retry logic. + * This function attempts to execute the provided callable function multiple times + * based on the specified retry conditions. It's useful for handling transient + * errors or temporary service unavailability. + */ +export const runWithRetry = async ( + callable: () => Promise, + retryPredicate: (error: Error) => boolean, + maxAttempts = 3 +): Promise => { + const collectedErrors: Error[] = []; + + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + try { + const result = await callable(); + return result; + } catch (error) { + if (error instanceof Error) { + collectedErrors.push(error); + if (!retryPredicate(error)) { + throw error; + } + } + } + } + + throw new AggregateError( + collectedErrors, + `All ${maxAttempts} attempts failed` + ); +}; From 443e2ffae505fc920fc93757f1d490b40e91219f Mon Sep 17 00:00:00 2001 From: Ian Saultz <52051793+atierian@users.noreply.github.com> Date: Mon, 11 Nov 2024 11:21:23 -0500 Subject: [PATCH 097/199] bump graphql-generator dependency version for model-generator (#2215) * bump graphql-generator dependency version for model-generator * update api * fix api --------- Co-authored-by: Kamil Sobol --- .changeset/cool-points-kneel.md | 5 + package-lock.json | 229 ++++++++++++++++---------- packages/model-generator/package.json | 2 +- 3 files changed, 149 insertions(+), 87 deletions(-) create mode 100644 .changeset/cool-points-kneel.md diff --git a/.changeset/cool-points-kneel.md b/.changeset/cool-points-kneel.md new file mode 100644 index 00000000000..766c97d2ee5 --- /dev/null +++ b/.changeset/cool-points-kneel.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/model-generator': patch +--- + +bump graphql-generator dependency version to 0.5.1 diff --git a/package-lock.json b/package-lock.json index fb897043263..672a2ec6f5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -481,12 +481,12 @@ } }, "node_modules/@aws-amplify/appsync-modelgen-plugin": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/@aws-amplify/appsync-modelgen-plugin/-/appsync-modelgen-plugin-2.13.0.tgz", - "integrity": "sha512-j74lEO53Sf5u9o6ZqmH6JqiUBD8VjqYSp4Rb4G+RNdLX8zt6eaEUKlO4wTQ9ejSrKgCDxzbb+2YldZWCMsWUFQ==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@aws-amplify/appsync-modelgen-plugin/-/appsync-modelgen-plugin-2.15.0.tgz", + "integrity": "sha512-k3hU3ZPXcxQgUB1I8mQ7+5zCTU2KCL43U4R/LbNAdGlXzDy0T2tppWJxobxRE+9K3+wtiYBeivtGzc7EmrveWw==", "license": "Apache-2.0", "dependencies": { - "@graphql-codegen/plugin-helpers": "^1.18.8", + "@graphql-codegen/plugin-helpers": "^3.1.1", "@graphql-codegen/visitor-plugin-common": "^1.22.0", "@graphql-tools/utils": "^6.0.18", "chalk": "^3.0.0", @@ -501,6 +501,60 @@ "graphql": "^15.5.0" } }, + "node_modules/@aws-amplify/appsync-modelgen-plugin/node_modules/@graphql-codegen/plugin-helpers": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-3.1.2.tgz", + "integrity": "sha512-emOQiHyIliVOIjKVKdsI5MXj312zmRDwmHpyUTZMjfpvxq/UVAHUJIVdVf+lnjjrI+LXBTgMlTWTgHQfmICxjg==", + "license": "MIT", + "dependencies": { + "@graphql-tools/utils": "^9.0.0", + "change-case-all": "1.0.15", + "common-tags": "1.8.2", + "import-from": "4.0.0", + "lodash": "~4.17.0", + "tslib": "~2.4.0" + }, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/@aws-amplify/appsync-modelgen-plugin/node_modules/@graphql-codegen/plugin-helpers/node_modules/@graphql-tools/utils": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz", + "integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@aws-amplify/appsync-modelgen-plugin/node_modules/change-case-all": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.15.tgz", + "integrity": "sha512-3+GIFhk3sNuvFAJKU46o26OdzudQlPNBCu1ZQi3cMeMHhty1bhDxu2WrEilVNYaGvqUtR1VSigFcJOiS13dRhQ==", + "license": "MIT", + "dependencies": { + "change-case": "^4.1.2", + "is-lower-case": "^2.0.2", + "is-upper-case": "^2.0.2", + "lower-case": "^2.0.2", + "lower-case-first": "^2.0.2", + "sponge-case": "^1.0.1", + "swap-case": "^2.0.2", + "title-case": "^3.0.3", + "upper-case": "^2.0.2", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/@aws-amplify/appsync-modelgen-plugin/node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "license": "0BSD" + }, "node_modules/@aws-amplify/auth": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/@aws-amplify/auth/-/auth-6.4.0.tgz", @@ -4945,17 +4999,20 @@ } }, "node_modules/@aws-amplify/graphql-generator": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/@aws-amplify/graphql-generator/-/graphql-generator-0.4.5.tgz", - "integrity": "sha512-yxAxb9KJjUEtgW32nBy2ZzR4bFVe1Em8oR+w63WSnoEmpsW3D0SAa7H2oDocaePPZCVVYC6AsqH5Ne6Q3i608Q==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@aws-amplify/graphql-generator/-/graphql-generator-0.5.1.tgz", + "integrity": "sha512-30t/1QvK6klDHL30IJ8/S6nGkfZNC4s534U0y6rbYGhMSpKtmWy63HozxAwxz5HBUzkom+HmWIMHdLW+UVgQeA==", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/appsync-modelgen-plugin": "2.13.0", + "@aws-amplify/appsync-modelgen-plugin": "2.15.0", "@aws-amplify/graphql-directives": "^1.0.1", "@aws-amplify/graphql-docs-generator": "4.2.1", "@aws-amplify/graphql-types-generator": "3.6.0", "@graphql-codegen/core": "^2.6.6", + "@graphql-codegen/plugin-helpers": "^3.1.1", "@graphql-tools/apollo-engine-loader": "^8.0.0", + "@graphql-tools/schema": "^9.0.0", + "@graphql-tools/utils": "^9.2.1", "graphql": "^15.5.0", "prettier": "^1.19.1" } @@ -4975,7 +5032,7 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, - "node_modules/@aws-amplify/graphql-generator/node_modules/@graphql-codegen/core/node_modules/@graphql-codegen/plugin-helpers": { + "node_modules/@aws-amplify/graphql-generator/node_modules/@graphql-codegen/plugin-helpers": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-3.1.2.tgz", "integrity": "sha512-emOQiHyIliVOIjKVKdsI5MXj312zmRDwmHpyUTZMjfpvxq/UVAHUJIVdVf+lnjjrI+LXBTgMlTWTgHQfmICxjg==", @@ -4992,7 +5049,7 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, - "node_modules/@aws-amplify/graphql-generator/node_modules/@graphql-codegen/core/node_modules/@graphql-tools/schema": { + "node_modules/@aws-amplify/graphql-generator/node_modules/@graphql-tools/schema": { "version": "9.0.19", "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.19.tgz", "integrity": "sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==", @@ -5007,7 +5064,7 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@aws-amplify/graphql-generator/node_modules/@graphql-codegen/core/node_modules/@graphql-tools/schema/node_modules/@graphql-tools/merge": { + "node_modules/@aws-amplify/graphql-generator/node_modules/@graphql-tools/schema/node_modules/@graphql-tools/merge": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.4.2.tgz", "integrity": "sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw==", @@ -5020,7 +5077,7 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@aws-amplify/graphql-generator/node_modules/@graphql-codegen/core/node_modules/@graphql-tools/utils": { + "node_modules/@aws-amplify/graphql-generator/node_modules/@graphql-tools/utils": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz", "integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==", @@ -31758,18 +31815,18 @@ }, "packages/ai-constructs": { "name": "@aws-amplify/ai-constructs", - "version": "0.7.0", + "version": "0.8.1", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.0.1", + "@aws-amplify/platform-core": "^1.2.0", + "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/client-bedrock-runtime": "^3.622.0", "@smithy/types": "^3.3.0", "json-schema-to-ts": "^3.1.1" }, "devDependencies": { - "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/backend-output-storage": "^1.1.3", "typescript": "^5.0.0" }, "peerDependencies": { @@ -31783,12 +31840,12 @@ }, "packages/auth-construct": { "name": "@aws-amplify/auth-construct", - "version": "1.3.2", + "version": "1.4.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/util-arn-parser": "^3.568.0" }, "peerDependencies": { @@ -31798,20 +31855,20 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.5.2", + "version": "1.6.2", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-auth": "^1.2.0", - "@aws-amplify/backend-data": "^1.1.5", - "@aws-amplify/backend-function": "^1.7.2", + "@aws-amplify/backend-auth": "^1.3.0", + "@aws-amplify/backend-data": "^1.1.7", + "@aws-amplify/backend-function": "^1.7.4", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/backend-secret": "^1.1.4", - "@aws-amplify/backend-storage": "^1.2.1", - "@aws-amplify/client-config": "^1.5.0", + "@aws-amplify/backend-storage": "^1.2.2", + "@aws-amplify/client-config": "^1.5.2", "@aws-amplify/data-schema": "^1.0.0", - "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.3.0", + "@aws-amplify/platform-core": "^1.2.0", + "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/client-amplify": "^3.624.0", "lodash.snakecase": "^4.1.1" }, @@ -31827,15 +31884,15 @@ }, "packages/backend-ai": { "name": "@aws-amplify/backend-ai", - "version": "0.3.4", + "version": "0.3.5", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.7.0", + "@aws-amplify/ai-constructs": "^0.8.0", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.0.2", + "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.0.1" + "@aws-amplify/plugin-types": "^1.3.1" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0", @@ -31844,15 +31901,15 @@ }, "packages/backend-auth": { "name": "@aws-amplify/backend-auth", - "version": "1.2.0", + "version": "1.3.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/plugin-types": "^1.3.0" + "@aws-amplify/auth-construct": "^1.4.0", + "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/plugin-types": "^1.3.1" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.5", + "@aws-amplify/backend-platform-test-stubs": "^0.3.6", "@aws-amplify/platform-core": "^1.0.6" }, "peerDependencies": { @@ -31862,19 +31919,19 @@ }, "packages/backend-data": { "name": "@aws-amplify/backend-data", - "version": "1.1.5", + "version": "1.1.7", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.2", + "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/data-construct": "^1.10.1", "@aws-amplify/data-schema-types": "^1.2.0", - "@aws-amplify/plugin-types": "^1.2.2" + "@aws-amplify/plugin-types": "^1.3.1" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.5", + "@aws-amplify/backend-platform-test-stubs": "^0.3.6", "@aws-amplify/data-schema": "^1.0.0", - "@aws-amplify/platform-core": "^1.0.7" + "@aws-amplify/platform-core": "^1.2.0" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0", @@ -31883,11 +31940,11 @@ }, "packages/backend-deployer": { "name": "@aws-amplify/backend-deployer", - "version": "1.1.5", + "version": "1.1.8", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/platform-core": "^1.2.0", + "@aws-amplify/plugin-types": "^1.3.1", "execa": "^8.0.1", "tsx": "^4.6.1" }, @@ -31898,16 +31955,16 @@ }, "packages/backend-function": { "name": "@aws-amplify/backend-function", - "version": "1.7.2", + "version": "1.7.4", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/plugin-types": "^1.3.0", + "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/plugin-types": "^1.3.1", "execa": "^8.0.1" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.5", + "@aws-amplify/backend-platform-test-stubs": "^0.3.6", "@aws-amplify/platform-core": "^1.1.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", @@ -31945,12 +32002,12 @@ }, "packages/backend-output-storage": { "name": "@aws-amplify/backend-output-storage", - "version": "1.1.2", + "version": "1.1.3", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.2.2" + "@aws-amplify/plugin-types": "^1.3.1" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0" @@ -31958,17 +32015,17 @@ }, "packages/backend-platform-test-stubs": { "name": "@aws-amplify/backend-platform-test-stubs", - "version": "0.3.5", + "version": "0.3.6", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/plugin-types": "^1.3.1", "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } }, "packages/backend-secret": { "name": "@aws-amplify/backend-secret", - "version": "1.1.4", + "version": "1.1.5", "license": "Apache-2.0", "dependencies": { "@aws-amplify/platform-core": "^1.0.5", @@ -31981,15 +32038,15 @@ }, "packages/backend-storage": { "name": "@aws-amplify/backend-storage", - "version": "1.2.1", + "version": "1.2.2", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.1", - "@aws-amplify/backend-output-storage": "^1.1.2", - "@aws-amplify/plugin-types": "^1.3.0" + "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/plugin-types": "^1.3.1" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.5", + "@aws-amplify/backend-platform-test-stubs": "^0.3.6", "@aws-amplify/platform-core": "^1.0.6" }, "peerDependencies": { @@ -31999,21 +32056,21 @@ }, "packages/cli": { "name": "@aws-amplify/backend-cli", - "version": "1.3.0", + "version": "1.4.1", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.3", + "@aws-amplify/backend-deployer": "^1.1.8", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/cli-core": "^1.1.3", - "@aws-amplify/client-config": "^1.5.0", + "@aws-amplify/cli-core": "^1.2.0", + "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/form-generator": "^1.0.3", "@aws-amplify/model-generator": "^1.0.8", - "@aws-amplify/platform-core": "^1.0.5", - "@aws-amplify/plugin-types": "^1.3.0", - "@aws-amplify/sandbox": "^1.2.2", - "@aws-amplify/schema-generator": "^1.2.4", + "@aws-amplify/platform-core": "^1.2.0", + "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/sandbox": "^1.2.5", + "@aws-amplify/schema-generator": "^1.2.5", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", @@ -32046,7 +32103,7 @@ }, "packages/cli-core": { "name": "@aws-amplify/cli-core", - "version": "1.1.3", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/platform-core": "^1.0.5", @@ -32153,14 +32210,14 @@ }, "packages/client-config": { "name": "@aws-amplify/client-config", - "version": "1.5.0", + "version": "1.5.2", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/model-generator": "^1.0.7", "@aws-amplify/platform-core": "^1.0.7", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/plugin-types": "^1.3.1", "zod": "^3.22.2" }, "devDependencies": { @@ -32338,20 +32395,20 @@ }, "packages/integration-tests": { "name": "@aws-amplify/integration-tests", - "version": "0.5.10", + "version": "0.6.0", "license": "Apache-2.0", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.7.0", - "@aws-amplify/auth-construct": "^1.3.1", - "@aws-amplify/backend": "^1.5.2", - "@aws-amplify/backend-ai": "^0.3.4", + "@aws-amplify/ai-constructs": "^0.8.0", + "@aws-amplify/auth-construct": "^1.4.0", + "@aws-amplify/backend": "^1.6.0", + "@aws-amplify/backend-ai": "^0.3.5", "@aws-amplify/backend-secret": "^1.1.4", - "@aws-amplify/client-config": "^1.4.0", + "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/client-accessanalyzer": "^3.624.0", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-bedrock-runtime": "^3.622.0", @@ -32404,7 +32461,7 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/graphql-generator": "^0.4.0", + "@aws-amplify/graphql-generator": "^0.5.1", "@aws-amplify/graphql-types-generator": "^3.6.0", "@aws-amplify/platform-core": "^1.0.5", "@aws-amplify/plugin-types": "^1.3.0", @@ -32421,7 +32478,7 @@ }, "packages/platform-core": { "name": "@aws-amplify/platform-core", - "version": "1.1.0", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/plugin-types": "^1.2.1", @@ -32453,7 +32510,7 @@ }, "packages/plugin-types": { "name": "@aws-amplify/plugin-types", - "version": "1.3.0", + "version": "1.3.1", "license": "Apache-2.0", "devDependencies": { "execa": "^5.1.1" @@ -32582,16 +32639,16 @@ }, "packages/sandbox": { "name": "@aws-amplify/sandbox", - "version": "1.2.3", + "version": "1.2.5", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.3", + "@aws-amplify/backend-deployer": "^1.1.8", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/cli-core": "^1.1.3", - "@aws-amplify/client-config": "^1.3.1", + "@aws-amplify/cli-core": "^1.2.0", + "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/platform-core": "^1.2.0", + "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", @@ -32613,7 +32670,7 @@ }, "packages/schema-generator": { "name": "@aws-amplify/schema-generator", - "version": "1.2.4", + "version": "1.2.5", "license": "Apache-2.0", "dependencies": { "@aws-amplify/graphql-schema-generator": "^0.11.0", diff --git a/packages/model-generator/package.json b/packages/model-generator/package.json index b45b00a62a4..c6dfc4971eb 100644 --- a/packages/model-generator/package.json +++ b/packages/model-generator/package.json @@ -21,7 +21,7 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/graphql-generator": "^0.4.0", + "@aws-amplify/graphql-generator": "^0.5.1", "@aws-amplify/graphql-types-generator": "^3.6.0", "@aws-amplify/platform-core": "^1.0.5", "@aws-amplify/plugin-types": "^1.3.0", From 12cf2092872293638ac9b78032e1ebc42e62e7fc Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Mon, 11 Nov 2024 12:54:49 -0800 Subject: [PATCH 098/199] update error mapping to catch when Lambda layer ARN regions do not match function region (#2216) * Revert "add validation if layer arn region does not match function region (#2188)" This reverts commit 4e9738933b15d6d4d4518f5284cf2b851b8f3cf5. * update error mapping to catch when Lambda layer ARN regions do not match function region * update changeset --- .changeset/violet-tools-clap.md | 7 +++ .../src/cdk_error_mapper.test.ts | 6 +++ .../backend-deployer/src/cdk_error_mapper.ts | 12 ++++- packages/backend-function/src/factory.test.ts | 33 ------------- packages/backend-function/src/factory.ts | 49 +++++++------------ .../backend-function/src/layer_parser.test.ts | 2 +- 6 files changed, 44 insertions(+), 65 deletions(-) create mode 100644 .changeset/violet-tools-clap.md diff --git a/.changeset/violet-tools-clap.md b/.changeset/violet-tools-clap.md new file mode 100644 index 00000000000..0837e670526 --- /dev/null +++ b/.changeset/violet-tools-clap.md @@ -0,0 +1,7 @@ +--- +'@aws-amplify/backend': patch +'@aws-amplify/backend-deployer': patch +'@aws-amplify/backend-function': patch +--- + +update error mapping to catch when Lambda layer ARN regions do not match function region diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 20f01df933f..f9e8524aaba 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -380,6 +380,12 @@ const testErrorMappings = [ errorName: 'MissingDefineBackendError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: `User: is not authorized to perform: lambda:GetLayerVersion on resource: because no resource-based policy allows the lambda:GetLayerVersion action`, + expectedTopLevelErrorMessage: 'Unable to get Lambda layer version', + errorName: 'GetLambdaLayerVersionError', + expectedDownstreamErrorMessage: undefined, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 994791f4e3f..9f109318d59 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -196,6 +196,15 @@ export class CdkErrorMapper { errorName: 'MultipleSandboxInstancesError', classification: 'ERROR', }, + { + errorRegex: + /User:(.*) is not authorized to perform: lambda:GetLayerVersion on resource:(.*) because no resource-based policy allows the lambda:GetLayerVersion action/, + humanReadableErrorMessage: 'Unable to get Lambda layer version', + resolutionMessage: + 'Make sure layer ARNs are correct and layer regions match function region', + errorName: 'GetLambdaLayerVersionError', + classification: 'ERROR', + }, { // Also extracts the first line in the stack where the error happened errorRegex: new RegExp( @@ -360,4 +369,5 @@ export type CDKDeploymentError = | 'FileConventionError' | 'ModuleNotFoundError' | 'SecretNotSetError' - | 'SyntaxError'; + | 'SyntaxError' + | 'GetLambdaLayerVersionError'; diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index 872e91b5bf9..127c10c33ce 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -17,7 +17,6 @@ import { NodeVersion, defineFunction } from './factory.js'; import { lambdaWithDependencies } from './test-assets/lambda-with-dependencies/resource.js'; import { Runtime } from 'aws-cdk-lib/aws-lambda'; import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; -import { AmplifyUserError } from '@aws-amplify/platform-core'; const createStackAndSetContext = (): Stack => { const app = new App(); @@ -412,38 +411,6 @@ void describe('AmplifyFunctionFactory', () => { }); }); - void describe('layers property', () => { - void it('defaults to no layers', () => { - const lambda = defineFunction({ - entry: './test-assets/default-lambda/handler.ts', - }).getInstance(getInstanceProps); - const template = Template.fromStack(lambda.stack); - - template.resourceCountIs('AWS::Lambda::LayerVersion', 0); - }); - - void it('throws if layer arn region is not the same as function region', () => { - assert.throws( - () => - defineFunction({ - entry: './test-assets/default-lambda/handler.ts', - layers: { - layer1: - 'arn:aws:lambda:some-region:123456789012:layer:my-layer-1:1', - }, - }).getInstance(getInstanceProps), - (error: AmplifyUserError) => { - assert.strictEqual( - error.message, - 'Region in ARN does not match function region for layer: layer1' - ); - assert.ok(error.resolution); - return true; - } - ); - }); - }); - void describe('minify property', () => { void it('sets minify to false', () => { const lambda = defineFunction({ diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 511bf320e6f..5d5a6938e26 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -23,11 +23,16 @@ import { SsmEnvironmentEntry, StackProvider, } from '@aws-amplify/plugin-types'; -import { Duration, Lazy, Stack, Tags, Token } from 'aws-cdk-lib'; +import { Duration, Stack, Tags } from 'aws-cdk-lib'; import { Rule } from 'aws-cdk-lib/aws-events'; import * as targets from 'aws-cdk-lib/aws-events-targets'; import { Policy } from 'aws-cdk-lib/aws-iam'; -import { CfnFunction, LayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { + CfnFunction, + ILayerVersion, + LayerVersion, + Runtime, +} from 'aws-cdk-lib/aws-lambda'; import { NodejsFunction, OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs'; import { Construct } from 'constructs'; import { readFileSync } from 'fs'; @@ -345,10 +350,19 @@ class FunctionGenerator implements ConstructContainerEntryGenerator { scope, backendSecretResolver, }: GenerateContainerEntryProps) => { + // resolve layers to LayerVersion objects for the NodejsFunction constructor using the scope. + const resolvedLayers = Object.entries(this.props.layers).map(([key, arn]) => + LayerVersion.fromLayerVersionArn( + scope, + `${this.props.name}-${key}-layer`, + arn + ) + ); + return new AmplifyFunction( scope, this.props.name, - this.props, + { ...this.props, resolvedLayers }, backendSecretResolver, this.outputStorageStrategy ); @@ -368,7 +382,7 @@ class AmplifyFunction constructor( scope: Construct, id: string, - props: HydratedFunctionProps, + props: HydratedFunctionProps & { resolvedLayers: ILayerVersion[] }, backendSecretResolver: BackendSecretResolver, outputStorageStrategy: BackendOutputStorageStrategy ) { @@ -376,31 +390,6 @@ class AmplifyFunction this.stack = Stack.of(scope); - // resolve layers to LayerVersion objects for the NodejsFunction constructor using the scope. - const resolvedLayers = Object.entries(props.layers).map(([key, arn]) => { - const layerRegion = arn.split(':')[3]; - // If region is an unresolved token, use lazy to get region - const region = Token.isUnresolved(this.stack.region) - ? Lazy.string({ - produce: () => this.stack.region, - }) - : this.stack.region; - - if (layerRegion !== region) { - throw new AmplifyUserError('InvalidLayerArnRegionError', { - message: `Region in ARN does not match function region for layer: ${key}`, - resolution: - 'Update the layer ARN with the same region as the function', - }); - } - - return LayerVersion.fromLayerVersionArn( - scope, - `${props.name}-${key}-layer`, - arn - ); - }); - const runtime = nodeVersionMap[props.runtime]; const require = createRequire(import.meta.url); @@ -443,7 +432,7 @@ class AmplifyFunction timeout: Duration.seconds(props.timeoutSeconds), memorySize: props.memoryMB, runtime: nodeVersionMap[props.runtime], - layers: resolvedLayers, + layers: props.resolvedLayers, bundling: { ...props.bundling, banner: bannerCode, diff --git a/packages/backend-function/src/layer_parser.test.ts b/packages/backend-function/src/layer_parser.test.ts index a00fb306243..4d1e8ea5cc9 100644 --- a/packages/backend-function/src/layer_parser.test.ts +++ b/packages/backend-function/src/layer_parser.test.ts @@ -20,7 +20,7 @@ const createStackAndSetContext = (): Stack => { app.node.setContext('amplify-backend-name', 'testEnvName'); app.node.setContext('amplify-backend-namespace', 'testBackendId'); app.node.setContext('amplify-backend-type', 'branch'); - const stack = new Stack(app, 'Stack', { env: { region: 'us-east-1' } }); + const stack = new Stack(app); return stack; }; From 90a7c499ea4175d64f4bdc182fd2d1f2fff4a420 Mon Sep 17 00:00:00 2001 From: awsluja <110861985+awsluja@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:11:45 -0800 Subject: [PATCH 099/199] Reference Auth (#2118) * feat: reference auth basic setup * chore: factory basic tests * chore: update api * chore: add ref auth package as dependency to backend-auth * chore: lint * chore: add tests for construct * chore: cleanup tsconfig * chore: add changeset * chore: fix tests inputs * chore: update tests * fix: update resource provider types * chore: update api * feat: reference auth outputs * chore: add tests * chore: fix test * chore: cleanup reused variables * chore: changeset * chore: cleanup changeset * chore: cleanup * chore: cleanup changesets, lockfile, and api * chore: fix mismatched output structure * chore: refactor and add tests * chore: add more tests for identity pool errors * chore: cleanup * chore: fix test * chore: add role tests * chore: add tests for user pool client * chore: cleanup * chore: refactor * chore: fix api * chore: undo changes to concurrent workspace script * chore: add missing roles permission * chore: update expected IAM policy permissions for identity pool * fix: make sure to throw on errors when using Provider framework * chore: refactor * chore: cleanup * chore: more cleanup * chore: check for alias attributes and fix tests * chore: add support for validating group roles exist for user pool * chore: update package-lock file * chore: add checks for oauth validation * chore: fix typo * chore: eliminate forcing updates on any change * chore: remove commented out code * chore: merge factory count into single count for all auth factories * chore: move sample data and npmignore it * chore: cleanup * chore: fix path * chore: update package lock * chore: update package-lock * chore: move construct into backend-auth * chore: update api * chore: update changeset * chore: cleanup * chore: move props type to factory * chore: add working setup for e2e resources in ref auth (#2122) * chore: add working setup for e2e resources in ref auth * feed pr base sha and ref into envs before scripts (#2168) * feed pr base sha and ref into envs before scripts * removing empty file * chore: update names to use test prefix * chore: remove extra hyphen * chore: fix cleanup and add sandbox test * chore: make sure to throw if error describing stack is unknown --------- Co-authored-by: Roshane Pascual * chore: add bsd-3-clause-clear license to allow list * chore: cleanup * chore: make lambda deps dev dependencies * chore: revert license changes * chore: remove tag mechanism as not needed for cleanup --------- Co-authored-by: Roshane Pascual --- .changeset/good-pugs-rescue.md | 9 + .changeset/spicy-rules-speak.md | 2 + CONTRIBUTING.md | 2 + package-lock.json | 7 +- packages/auth-construct/src/construct.ts | 1 + packages/backend-auth/.npmignore | 1 + packages/backend-auth/API.md | 27 + packages/backend-auth/package.json | 7 +- packages/backend-auth/src/factory.test.ts | 4 +- packages/backend-auth/src/factory.ts | 4 +- packages/backend-auth/src/index.ts | 6 + .../backend-auth/src/lambda/.eslintrc.json | 6 + .../lambda/reference_auth_initializer.test.ts | 558 ++++++++++++++++++ .../src/lambda/reference_auth_initializer.ts | 544 +++++++++++++++++ .../src/reference_construct.test.ts | 184 ++++++ .../backend-auth/src/reference_construct.ts | 224 +++++++ .../src/reference_factory.test.ts | 279 +++++++++ .../backend-auth/src/reference_factory.ts | 239 ++++++++ .../src/test-resources/sample_data.ts | 448 ++++++++++++++ packages/backend-auth/tsconfig.json | 1 + .../src/convert_authorization_modes.test.ts | 1 + .../src/convert_authorization_modes.ts | 9 +- packages/backend-data/src/factory.test.ts | 1 + packages/backend-data/src/factory.ts | 7 +- packages/backend/API.md | 3 + packages/backend/src/index.ts | 2 +- .../auth_resource_creator.ts | 372 ++++++++++++ .../reference_auth_project.deployment.test.ts | 4 + .../reference_auth_project.sandbox.test.ts | 4 + .../reference_auth_project.ts | 340 +++++++++++ .../test-project-setup/test_project_base.ts | 69 ++- .../reference-auth/amplify/auth/resource.ts | 14 + .../reference-auth/amplify/backend.ts | 10 + .../amplify/data/add-user-to-group/handler.ts | 3 + .../data/add-user-to-group/resource.ts | 5 + .../reference-auth/amplify/data/resource.ts | 24 + .../amplify/storage/resource.ts | 15 + .../test-types/env/add-user-to-group.ts | 10 + packages/plugin-types/API.md | 15 + packages/plugin-types/src/auth_resources.ts | 41 ++ 40 files changed, 3485 insertions(+), 17 deletions(-) create mode 100644 .changeset/good-pugs-rescue.md create mode 100644 .changeset/spicy-rules-speak.md create mode 100644 packages/backend-auth/src/lambda/.eslintrc.json create mode 100644 packages/backend-auth/src/lambda/reference_auth_initializer.test.ts create mode 100644 packages/backend-auth/src/lambda/reference_auth_initializer.ts create mode 100644 packages/backend-auth/src/reference_construct.test.ts create mode 100644 packages/backend-auth/src/reference_construct.ts create mode 100644 packages/backend-auth/src/reference_factory.test.ts create mode 100644 packages/backend-auth/src/reference_factory.ts create mode 100644 packages/backend-auth/src/test-resources/sample_data.ts create mode 100644 packages/integration-tests/src/resource-creation/auth_resource_creator.ts create mode 100644 packages/integration-tests/src/test-e2e/deployment/reference_auth_project.deployment.test.ts create mode 100644 packages/integration-tests/src/test-e2e/sandbox/reference_auth_project.sandbox.test.ts create mode 100644 packages/integration-tests/src/test-project-setup/reference_auth_project.ts create mode 100644 packages/integration-tests/src/test-projects/reference-auth/amplify/auth/resource.ts create mode 100644 packages/integration-tests/src/test-projects/reference-auth/amplify/backend.ts create mode 100644 packages/integration-tests/src/test-projects/reference-auth/amplify/data/add-user-to-group/handler.ts create mode 100644 packages/integration-tests/src/test-projects/reference-auth/amplify/data/add-user-to-group/resource.ts create mode 100644 packages/integration-tests/src/test-projects/reference-auth/amplify/data/resource.ts create mode 100644 packages/integration-tests/src/test-projects/reference-auth/amplify/storage/resource.ts create mode 100644 packages/integration-tests/src/test-projects/reference-auth/test-types/env/add-user-to-group.ts diff --git a/.changeset/good-pugs-rescue.md b/.changeset/good-pugs-rescue.md new file mode 100644 index 00000000000..7f4a210605d --- /dev/null +++ b/.changeset/good-pugs-rescue.md @@ -0,0 +1,9 @@ +--- +'@aws-amplify/auth-construct': minor +'@aws-amplify/backend-auth': minor +'@aws-amplify/backend-data': minor +'@aws-amplify/plugin-types': minor +'@aws-amplify/backend': minor +--- + +Add support for referenceAuth. diff --git a/.changeset/spicy-rules-speak.md b/.changeset/spicy-rules-speak.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/spicy-rules-speak.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 424876cb2fb..d76eea6cdf6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -82,6 +82,8 @@ For local testing we recommend writing unit tests that exercise the code you are npm run test:dir packages//lib/.test.ts ``` +> Note: If your test depends on \_\_dirname or import.meta.url paths, you may see errors resolving paths if you specify the entire path to the test file. You should specify just the `packages/` portion of the test you are running. + > Note: You must rebuild using `npm run build` for tests to pick up your changes. Sometimes it's nice to have a test project to use as a testing environment for local changes. You can create test projects in the `local-testing` directory using diff --git a/package-lock.json b/package-lock.json index 672a2ec6f5a..1ce770cf49c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31905,12 +31905,17 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/auth-construct": "^1.4.0", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/plugin-types": "^1.3.1" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.0.6" + "@aws-amplify/platform-core": "^1.0.6", + "@aws-sdk/client-cognito-identity": "^3.624.0", + "@aws-sdk/client-cognito-identity-provider": "^3.624.0", + "@types/aws-lambda": "^8.10.119", + "aws-lambda": "^1.0.7" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0", diff --git a/packages/auth-construct/src/construct.ts b/packages/auth-construct/src/construct.ts index 84f9455d7b9..7c94f9ad004 100644 --- a/packages/auth-construct/src/construct.ts +++ b/packages/auth-construct/src/construct.ts @@ -223,6 +223,7 @@ export class AmplifyAuth userPoolClient, authenticatedUserIamRole: auth, unauthenticatedUserIamRole: unAuth, + identityPoolId: identityPool.ref, cfnResources: { cfnUserPool, cfnUserPoolClient, diff --git a/packages/backend-auth/.npmignore b/packages/backend-auth/.npmignore index dbde1fb5dbc..78143c71132 100644 --- a/packages/backend-auth/.npmignore +++ b/packages/backend-auth/.npmignore @@ -10,5 +10,6 @@ # Then ignore test js and ts declaration files *.test.js *.test.d.ts +**/test-resources/** # This leaves us with including only js and ts declaration files of functional code diff --git a/packages/backend-auth/API.md b/packages/backend-auth/API.md index a1c07703d79..6663c93e786 100644 --- a/packages/backend-auth/API.md +++ b/packages/backend-auth/API.md @@ -7,9 +7,11 @@ import { AmazonProviderProps } from '@aws-amplify/auth-construct'; import { AmplifyFunction } from '@aws-amplify/plugin-types'; import { AppleProviderProps } from '@aws-amplify/auth-construct'; +import { AuthOutput } from '@aws-amplify/backend-output-schemas'; import { AuthProps } from '@aws-amplify/auth-construct'; import { AuthResources } from '@aws-amplify/plugin-types'; import { AuthRoleName } from '@aws-amplify/plugin-types'; +import { BackendOutputStorageStrategy } from '@aws-amplify/plugin-types'; import { BackendSecret } from '@aws-amplify/plugin-types'; import { ConstructFactory } from '@aws-amplify/plugin-types'; import { ConstructFactoryGetInstanceProps } from '@aws-amplify/plugin-types'; @@ -19,6 +21,7 @@ import { FunctionResources } from '@aws-amplify/plugin-types'; import { GoogleProviderProps } from '@aws-amplify/auth-construct'; import { IFunction } from 'aws-cdk-lib/aws-lambda'; import { OidcProviderProps } from '@aws-amplify/auth-construct'; +import { ReferenceAuthResources } from '@aws-amplify/plugin-types'; import { ResourceAccessAcceptor } from '@aws-amplify/plugin-types'; import { ResourceAccessAcceptorFactory } from '@aws-amplify/plugin-types'; import { ResourceProvider } from '@aws-amplify/plugin-types'; @@ -48,6 +51,11 @@ export type AmplifyAuthProps = Expand; +// @public (undocumented) +export type AmplifyReferenceAuthProps = Expand & { + access?: AuthAccessGenerator; +}>; + // @public export type AppleProviderFactoryProps = Omit & { clientId: BackendSecret; @@ -86,6 +94,9 @@ export type AuthLoginWithFactoryProps = Omit & ResourceAccessAcceptorFactory & StackProvider; +// @public (undocumented) +export type BackendReferenceAuth = ResourceProvider & ResourceAccessAcceptorFactory & StackProvider; + // @public export type CustomEmailSender = { handler: ConstructFactory | IFunction; @@ -130,6 +141,22 @@ export type OidcProviderFactoryProps = Omit ConstructFactory; + +// @public (undocumented) +export type ReferenceAuthProps = { + outputStorageStrategy?: BackendOutputStorageStrategy; + userPoolId: string; + identityPoolId: string; + userPoolClientId: string; + authRoleArn: string; + unauthRoleArn: string; + groups?: { + [groupName: string]: string; + }; +}; + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/backend-auth/package.json b/packages/backend-auth/package.json index dd5dab4e33d..4c3a4aed8a9 100644 --- a/packages/backend-auth/package.json +++ b/packages/backend-auth/package.json @@ -20,12 +20,17 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/auth-construct": "^1.4.0", + "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/plugin-types": "^1.3.1" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.0.6" + "@aws-amplify/platform-core": "^1.0.6", + "@aws-sdk/client-cognito-identity-provider": "^3.624.0", + "@aws-sdk/client-cognito-identity": "^3.624.0", + "@types/aws-lambda": "^8.10.119", + "aws-lambda": "^1.0.7" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0", diff --git a/packages/backend-auth/src/factory.test.ts b/packages/backend-auth/src/factory.test.ts index a01c5f59352..eb7ed5336e3 100644 --- a/packages/backend-auth/src/factory.test.ts +++ b/packages/backend-auth/src/factory.test.ts @@ -153,8 +153,8 @@ void describe('AmplifyAuthFactory', () => { }, new AmplifyUserError('MultipleSingletonResourcesError', { message: - 'Multiple `defineAuth` calls are not allowed within an Amplify backend', - resolution: 'Remove all but one `defineAuth` call', + 'Multiple `defineAuth` or `referenceAuth` calls are not allowed within an Amplify backend', + resolution: 'Remove all but one `defineAuth` or `referenceAuth` call', }) ); }); diff --git a/packages/backend-auth/src/factory.ts b/packages/backend-auth/src/factory.ts index 48d411f07d3..767e08430f6 100644 --- a/packages/backend-auth/src/factory.ts +++ b/packages/backend-auth/src/factory.ts @@ -103,8 +103,8 @@ export class AmplifyAuthFactory implements ConstructFactory { if (AmplifyAuthFactory.factoryCount > 0) { throw new AmplifyUserError('MultipleSingletonResourcesError', { message: - 'Multiple `defineAuth` calls are not allowed within an Amplify backend', - resolution: 'Remove all but one `defineAuth` call', + 'Multiple `defineAuth` or `referenceAuth` calls are not allowed within an Amplify backend', + resolution: 'Remove all but one `defineAuth` or `referenceAuth` call', }); } AmplifyAuthFactory.factoryCount++; diff --git a/packages/backend-auth/src/index.ts b/packages/backend-auth/src/index.ts index 90ce00ddc31..8a53f0225dd 100644 --- a/packages/backend-auth/src/index.ts +++ b/packages/backend-auth/src/index.ts @@ -1,2 +1,8 @@ export { BackendAuth, AmplifyAuthProps, defineAuth } from './factory.js'; +export { + BackendReferenceAuth, + AmplifyReferenceAuthProps, + referenceAuth, + ReferenceAuthProps, +} from './reference_factory.js'; export * from './types.js'; diff --git a/packages/backend-auth/src/lambda/.eslintrc.json b/packages/backend-auth/src/lambda/.eslintrc.json new file mode 100644 index 00000000000..fa0db4e4223 --- /dev/null +++ b/packages/backend-auth/src/lambda/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "rules": { + "no-console": "off", + "amplify-backend-rules/prefer-amplify-errors": "off" + } +} diff --git a/packages/backend-auth/src/lambda/reference_auth_initializer.test.ts b/packages/backend-auth/src/lambda/reference_auth_initializer.test.ts new file mode 100644 index 00000000000..81c50484aaf --- /dev/null +++ b/packages/backend-auth/src/lambda/reference_auth_initializer.test.ts @@ -0,0 +1,558 @@ +import { beforeEach, describe, it, mock } from 'node:test'; +import { ReferenceAuthInitializer } from './reference_auth_initializer.js'; +import { CloudFormationCustomResourceEvent } from 'aws-lambda'; +import assert from 'node:assert'; +import { + CognitoIdentityProviderClient, + DescribeUserPoolClientCommand, + DescribeUserPoolClientCommandOutput, + DescribeUserPoolCommand, + DescribeUserPoolCommandOutput, + GetUserPoolMfaConfigCommand, + GetUserPoolMfaConfigCommandOutput, + ListGroupsCommand, + ListGroupsCommandOutput, + ListIdentityProvidersCommand, + ListIdentityProvidersCommandOutput, +} from '@aws-sdk/client-cognito-identity-provider'; +import { + CognitoIdentityClient, + DescribeIdentityPoolCommand, + DescribeIdentityPoolCommandOutput, + GetIdentityPoolRolesCommand, + GetIdentityPoolRolesCommandOutput, +} from '@aws-sdk/client-cognito-identity'; +import { + IdentityPool, + IdentityPoolRoles, + IdentityProviders, + MFAResponse, + SampleInputProperties, + UserPool, + UserPoolClient, + UserPoolGroups, +} from '../test-resources/sample_data.js'; + +const customResourceEventCommon: Omit< + CloudFormationCustomResourceEvent, + 'RequestType' +> = { + ServiceToken: 'mockServiceToken', + ResponseURL: 'mockPreSignedS3Url', + StackId: 'mockStackId', + RequestId: '123', + LogicalResourceId: 'logicalId', + ResourceType: 'AWS::CloudFormation::CustomResource', + ResourceProperties: { + ...SampleInputProperties, + ServiceToken: 'token', + }, +}; +const createCfnEvent: CloudFormationCustomResourceEvent = { + RequestType: 'Create', + ...customResourceEventCommon, +}; + +const updateCfnEvent: CloudFormationCustomResourceEvent = { + RequestType: 'Update', + PhysicalResourceId: 'physicalId', + OldResourceProperties: { + ...SampleInputProperties, + ServiceToken: 'token', + }, + ...customResourceEventCommon, +}; + +const deleteCfnEvent: CloudFormationCustomResourceEvent = { + RequestType: 'Delete', + PhysicalResourceId: 'physicalId', + ...customResourceEventCommon, +}; +const httpError = { + $metadata: { + httpStatusCode: 500, + }, +}; +const httpSuccess = { + $metadata: { + httpStatusCode: 200, + }, +}; +const groupName = 'ADMINS'; +const groupRoleARN = 'arn:aws:iam::000000000000:role/sample-group-role'; +const groupRoleARNNotOnUserPool = + 'arn:aws:iam::000000000000:role/sample-bad-group-role'; +// aws sdk will throw with error message for any non 200 status so we don't need to re-package it +const awsSDKErrorMessageMock = new Error('this message comes from the aws sdk'); +const uuidMock = () => '00000000-0000-0000-0000-000000000000'; +const identityProviderClient = new CognitoIdentityProviderClient(); +const identityClient = new CognitoIdentityClient(); +const expectedData = { + userPoolId: SampleInputProperties.userPoolId, + webClientId: SampleInputProperties.userPoolClientId, + identityPoolId: SampleInputProperties.identityPoolId, + signupAttributes: '["sub","email"]', + usernameAttributes: '["email"]', + verificationMechanisms: '["email"]', + passwordPolicyMinLength: '10', + passwordPolicyRequirements: + '["REQUIRES_NUMBERS","REQUIRES_LOWERCASE","REQUIRES_UPPERCASE"]', + mfaConfiguration: 'ON', + mfaTypes: '["TOTP"]', + socialProviders: '["FACEBOOK","GOOGLE","LOGIN_WITH_AMAZON"]', + oauthCognitoDomain: 'ref-auth-userpool-1.auth.us-east-1.amazoncognito.com', + allowUnauthenticatedIdentities: 'true', + oauthScope: '["email","openid","phone"]', + oauthRedirectSignIn: 'https://redirect.com,https://redirect2.com', + oauthRedirectSignOut: 'https://anotherlogouturl.com,https://logouturl.com', + oauthResponseType: 'code', + oauthClientId: SampleInputProperties.userPoolClientId, +}; + +void describe('ReferenceAuthInitializer', () => { + let handler: ReferenceAuthInitializer; + let describeUserPoolResponse: DescribeUserPoolCommandOutput; + let getUserPoolMfaConfigResponse: GetUserPoolMfaConfigCommandOutput; + let listIdentityProvidersResponse: ListIdentityProvidersCommandOutput; + let describeUserPoolClientResponse: DescribeUserPoolClientCommandOutput; + let describeIdentityPoolResponse: DescribeIdentityPoolCommandOutput; + let getIdentityPoolRolesResponse: GetIdentityPoolRolesCommandOutput; + let listGroupsResponse: ListGroupsCommandOutput; + const rejectsAndMatchError = async ( + fn: Promise, + expectedErrorMessage: string + ): Promise => { + await assert.rejects(fn, (error: Error) => { + assert.strictEqual(error.message, expectedErrorMessage); + return true; + }); + }; + beforeEach(() => { + handler = new ReferenceAuthInitializer( + identityClient, + identityProviderClient, + uuidMock + ); + describeUserPoolResponse = { + ...httpSuccess, + UserPool: UserPool, + }; + getUserPoolMfaConfigResponse = { + ...httpSuccess, + ...MFAResponse, + }; + listIdentityProvidersResponse = { + ...httpSuccess, + Providers: [...IdentityProviders], + }; + describeUserPoolClientResponse = { + ...httpSuccess, + UserPoolClient: UserPoolClient, + }; + describeIdentityPoolResponse = { + ...httpSuccess, + ...IdentityPool, + }; + getIdentityPoolRolesResponse = { + ...httpSuccess, + ...IdentityPoolRoles, + }; + listGroupsResponse = { + ...httpSuccess, + ...UserPoolGroups, + }; + mock.method( + identityProviderClient, + 'send', + async ( + request: + | DescribeUserPoolCommand + | GetUserPoolMfaConfigCommand + | ListIdentityProvidersCommand + | DescribeUserPoolClientCommand + | ListGroupsCommand + ) => { + if (request instanceof DescribeUserPoolCommand) { + if (describeUserPoolResponse.$metadata.httpStatusCode !== 200) { + throw awsSDKErrorMessageMock; + } + return describeUserPoolResponse; + } + if (request instanceof GetUserPoolMfaConfigCommand) { + if (getUserPoolMfaConfigResponse.$metadata.httpStatusCode !== 200) { + throw awsSDKErrorMessageMock; + } + return getUserPoolMfaConfigResponse; + } + if (request instanceof ListIdentityProvidersCommand) { + if (listIdentityProvidersResponse.$metadata.httpStatusCode !== 200) { + throw awsSDKErrorMessageMock; + } + return listIdentityProvidersResponse; + } + if (request instanceof DescribeUserPoolClientCommand) { + if (describeUserPoolClientResponse.$metadata.httpStatusCode !== 200) { + throw awsSDKErrorMessageMock; + } + return describeUserPoolClientResponse; + } + if (request instanceof ListGroupsCommand) { + if (listGroupsResponse.$metadata.httpStatusCode !== 200) { + throw awsSDKErrorMessageMock; + } + return listGroupsResponse; + } + return undefined; + } + ); + mock.method( + identityClient, + 'send', + async ( + request: DescribeIdentityPoolCommand | GetIdentityPoolRolesCommand + ) => { + if (request instanceof DescribeIdentityPoolCommand) { + if (describeIdentityPoolResponse.$metadata.httpStatusCode !== 200) { + throw awsSDKErrorMessageMock; + } + return describeIdentityPoolResponse; + } + if (request instanceof GetIdentityPoolRolesCommand) { + if (getIdentityPoolRolesResponse.$metadata.httpStatusCode !== 200) { + throw awsSDKErrorMessageMock; + } + return getIdentityPoolRolesResponse; + } + return undefined; + } + ); + }); + void it('handles create events', async () => { + const result = await handler.handleEvent(createCfnEvent); + assert.deepEqual(result.Status, 'SUCCESS'); + assert.equal( + result.PhysicalResourceId, + '00000000-0000-0000-0000-000000000000' + ); + assert.deepEqual(result.Data, expectedData); + }); + + void it('handles update events', async () => { + const result = await handler.handleEvent(updateCfnEvent); + assert.deepEqual(result.Status, 'SUCCESS'); + assert.deepEqual(result.Data, expectedData); + }); + + void it('handles delete events', async () => { + const result = await handler.handleEvent(deleteCfnEvent); + assert.deepEqual(result.Status, 'SUCCESS'); + }); + + void it('throws if fetching user pool fails', async () => { + describeUserPoolResponse = httpError; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + awsSDKErrorMessageMock.message + ); + }); + + void it('throws if fetching user pool fails', async () => { + describeUserPoolResponse = { + ...httpSuccess, + UserPool: undefined, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'Failed to retrieve the specified UserPool.' + ); + }); + + void it('throws if user pool has no password policy', async () => { + describeUserPoolResponse = { + ...httpSuccess, + UserPool: { + ...UserPool, + Policies: undefined, + }, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'Failed to retrieve password policy.' + ); + }); + + void it('throws if user pool uses alias attributes', async () => { + describeUserPoolResponse = { + ...httpSuccess, + UserPool: { + ...UserPool, + UsernameAttributes: [], + AliasAttributes: ['email', 'phone_number'], + }, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'The specified user pool is configured with alias attributes which are not currently supported.' + ); + }); + + void it('throws if user pool does not have a domain configured and external login providers are enabled', async () => { + describeUserPoolResponse = { + ...httpSuccess, + UserPool: { + ...UserPool, + Domain: undefined, + CustomDomain: undefined, + }, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'You must configure a domain for your UserPool if external login providers are enabled.' + ); + }); + + void it('throws if user pool group is not found', async () => { + listGroupsResponse = { + ...httpSuccess, + Groups: [ + { + GroupName: 'OTHERGROUP', + RoleArn: groupRoleARNNotOnUserPool, + }, + ], + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + `The group '${groupName}' with role '${groupRoleARN}' does not match any group for the specified user pool.` + ); + }); + + void it('throws if user pool groups request fails', async () => { + listGroupsResponse = { + ...httpError, + Groups: undefined, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + awsSDKErrorMessageMock.message + ); + }); + + void it('throws if user pool groups response is undefined', async () => { + listGroupsResponse = { + ...httpSuccess, + Groups: undefined, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'An error occurred while retrieving the groups for the user pool.' + ); + }); + + void it('throws if fetching user pool MFA config fails', async () => { + getUserPoolMfaConfigResponse = httpError; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + awsSDKErrorMessageMock.message + ); + }); + + void it('throws if fetching user pool providers fails', async () => { + listIdentityProvidersResponse = { + $metadata: { + httpStatusCode: 500, + }, + Providers: [], + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + awsSDKErrorMessageMock.message + ); + }); + + void it('throws if fetching user pool providers returns undefined', async () => { + listIdentityProvidersResponse = { + ...httpSuccess, + Providers: undefined, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'An error occurred while retrieving identity providers for the user pool.' + ); + }); + + void it('throws if fetching user pool client fails', async () => { + describeUserPoolClientResponse = httpError; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + awsSDKErrorMessageMock.message + ); + }); + void it('throws if fetching user pool client returns undefined', async () => { + describeUserPoolClientResponse = { + ...httpSuccess, + UserPoolClient: undefined, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'An error occurred while retrieving the user pool client details.' + ); + }); + void it('throws if user pool client does not have sign-out / logout URLs configured and external login providers are enabled', async () => { + describeUserPoolClientResponse = { + ...httpSuccess, + UserPoolClient: { + ...UserPoolClient, + LogoutURLs: [], + }, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'Your UserPool client must have "Allowed sign-out URLs" configured if external login providers are enabled.' + ); + }); + void it('throws if user pool client does not have callback URLs configured and external login providers are enabled', async () => { + describeUserPoolClientResponse = { + ...httpSuccess, + UserPoolClient: { + ...UserPoolClient, + CallbackURLs: [], + }, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'Your UserPool client must have "Allowed callback URLs" configured if external login providers are enabled.' + ); + }); + + void it('throws if fetching identity pool fails', async () => { + describeIdentityPoolResponse = { + $metadata: { + httpStatusCode: 500, + }, + IdentityPoolId: undefined, + IdentityPoolName: undefined, + AllowUnauthenticatedIdentities: undefined, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + awsSDKErrorMessageMock.message + ); + }); + void it('throws if fetching identity pool returns undefined', async () => { + describeIdentityPoolResponse = { + ...httpSuccess, + IdentityPoolId: undefined, + IdentityPoolName: undefined, + AllowUnauthenticatedIdentities: undefined, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'An error occurred while retrieving the identity pool details.' + ); + }); + + void it('throws if fetching identity pool roles fails', async () => { + getIdentityPoolRolesResponse = httpError; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + awsSDKErrorMessageMock.message + ); + }); + void it('throws if fetching identity pool roles return undefined', async () => { + getIdentityPoolRolesResponse = { + ...httpSuccess, + Roles: undefined, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'An error occurred while retrieving the roles for the identity pool.' + ); + }); + // throws if userPool or client doesn't match identity pool + void it('throws there is not matching userPool for the identity pool', async () => { + describeIdentityPoolResponse = { + ...describeIdentityPoolResponse, + CognitoIdentityProviders: [ + { + ProviderName: + 'cognito-idp.us-east-1.amazonaws.com/us-east-1_wrongUserPool', + ClientId: 'sampleUserPoolClientId', + ServerSideTokenCheck: false, + }, + ], + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'The user pool and user pool client pair do not match any cognito identity providers for the specified identity pool.' + ); + }); + void it('throws if identity pool does not have cognito identity providers configured', async () => { + describeIdentityPoolResponse = { + ...describeIdentityPoolResponse, + CognitoIdentityProviders: [], + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'The specified identity pool does not have any cognito identity providers.' + ); + }); + void it('throws if the client id does not match any cognito provider on the identity pool', async () => { + describeIdentityPoolResponse = { + ...describeIdentityPoolResponse, + CognitoIdentityProviders: [ + { + ProviderName: + 'cognito-idp.us-east-1.amazonaws.com/us-east-1_userpoolTest', + ClientId: 'wrongClientId', + ServerSideTokenCheck: false, + }, + ], + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'The user pool and user pool client pair do not match any cognito identity providers for the specified identity pool.' + ); + }); + void it('throws if auth role ARN does not match', async () => { + getIdentityPoolRolesResponse = { + ...httpSuccess, + IdentityPoolId: SampleInputProperties.identityPoolId, + Roles: { + authenticated: 'wrongAuthRole', + unauthenticated: SampleInputProperties.unauthRoleArn, + }, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'The provided authRoleArn does not match the authenticated role for the specified identity pool.' + ); + }); + void it('throws if unauth role ARN does not match', async () => { + getIdentityPoolRolesResponse = { + ...httpSuccess, + IdentityPoolId: SampleInputProperties.identityPoolId, + Roles: { + authenticated: SampleInputProperties.authRoleArn, + unauthenticated: 'wrongUnauthRole', + }, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'The provided unauthRoleArn does not match the unauthenticated role for the specified identity pool.' + ); + }); + void it('throws if user pool client is not a web client', async () => { + describeUserPoolClientResponse = { + ...httpSuccess, + UserPoolClient: { + ...UserPoolClient, + ClientSecret: 'sample', + }, + }; + await rejectsAndMatchError( + handler.handleEvent(createCfnEvent), + 'The specified user pool client is not configured as a web client.' + ); + }); +}); diff --git a/packages/backend-auth/src/lambda/reference_auth_initializer.ts b/packages/backend-auth/src/lambda/reference_auth_initializer.ts new file mode 100644 index 00000000000..9f31a7302b8 --- /dev/null +++ b/packages/backend-auth/src/lambda/reference_auth_initializer.ts @@ -0,0 +1,544 @@ +import { + CloudFormationCustomResourceEvent, + CloudFormationCustomResourceResponse, + CloudFormationCustomResourceSuccessResponse, +} from 'aws-lambda'; +import { + CognitoIdentityProviderClient, + DescribeUserPoolClientCommand, + DescribeUserPoolCommand, + GetUserPoolMfaConfigCommand, + GetUserPoolMfaConfigCommandOutput, + GroupType, + ListGroupsCommand, + ListIdentityProvidersCommand, + PasswordPolicyType, + ProviderDescription, + UserPoolClientType, + UserPoolType, +} from '@aws-sdk/client-cognito-identity-provider'; +import { + CognitoIdentityClient, + DescribeIdentityPoolCommand, + DescribeIdentityPoolCommandOutput, + GetIdentityPoolRolesCommand, +} from '@aws-sdk/client-cognito-identity'; +import { randomUUID } from 'node:crypto'; +import { AuthOutput } from '@aws-amplify/backend-output-schemas'; +export type ReferenceAuthInitializerProps = { + userPoolId: string; + identityPoolId: string; + authRoleArn: string; + unauthRoleArn: string; + userPoolClientId: string; + groups: Record; + region: string; +}; + +/** + * Initializer that fetches and process auth resources. + */ +export class ReferenceAuthInitializer { + /** + * Create a new initializer + * @param cognitoIdentityClient identity client + * @param cognitoIdentityProviderClient identity provider client + */ + constructor( + private cognitoIdentityClient: CognitoIdentityClient, + private cognitoIdentityProviderClient: CognitoIdentityProviderClient, + private uuidGenerator: () => string + ) {} + + /** + * Handles custom resource events + * @param event event to process + * @returns custom resource response + */ + public handleEvent = async (event: CloudFormationCustomResourceEvent) => { + console.info(`Received '${event.RequestType}' event`); + // physical id is only generated on create, otherwise it must stay the same + const physicalId = + event.RequestType === 'Create' + ? this.uuidGenerator() + : event.PhysicalResourceId; + + // on delete, just respond with success since we don't need to do anything + if (event.RequestType === 'Delete') { + return { + RequestId: event.RequestId, + LogicalResourceId: event.LogicalResourceId, + PhysicalResourceId: physicalId, + StackId: event.StackId, + NoEcho: true, + Status: 'SUCCESS', + } as CloudFormationCustomResourceSuccessResponse; + } + // for create or update events, we will fetch and validate resource properties + const props = + event.ResourceProperties as unknown as ReferenceAuthInitializerProps; + const { + userPool, + userPoolPasswordPolicy, + userPoolMFA, + userPoolGroups, + userPoolProviders, + userPoolClient, + identityPool, + roles, + } = await this.getResourceDetails( + props.userPoolId, + props.identityPoolId, + props.userPoolClientId + ); + + this.validateResources( + userPool, + userPoolProviders, + userPoolGroups, + userPoolClient, + identityPool, + roles, + props + ); + + const userPoolOutputs = await this.getUserPoolOutputs( + userPool, + userPoolPasswordPolicy, + userPoolProviders, + userPoolMFA, + props.region + ); + const identityPoolOutputs = await this.getIdentityPoolOutputs(identityPool); + const userPoolClientOutputs = await this.getUserPoolClientOutputs( + userPoolClient + ); + const data: Omit = { + userPoolId: props.userPoolId, + webClientId: props.userPoolClientId, + identityPoolId: props.identityPoolId, + ...userPoolOutputs, + ...identityPoolOutputs, + ...userPoolClientOutputs, + }; + return { + RequestId: event.RequestId, + LogicalResourceId: event.LogicalResourceId, + PhysicalResourceId: physicalId, + StackId: event.StackId, + NoEcho: true, + Data: data, + Status: 'SUCCESS', + } as CloudFormationCustomResourceSuccessResponse; + }; + + private getUserPool = async (userPoolId: string) => { + const userPoolCommand = new DescribeUserPoolCommand({ + UserPoolId: userPoolId, + }); + const userPoolResponse = await this.cognitoIdentityProviderClient.send( + userPoolCommand + ); + if (!userPoolResponse.UserPool) { + throw new Error('Failed to retrieve the specified UserPool.'); + } + const userPool = userPoolResponse.UserPool; + const policy = userPool.Policies?.PasswordPolicy; + if (!policy) { + throw new Error('Failed to retrieve password policy.'); + } + return { + userPool: userPoolResponse.UserPool, + userPoolPasswordPolicy: policy, + }; + }; + + private getUserPoolMFASettings = async (userPoolId: string) => { + // mfa types + const mfaCommand = new GetUserPoolMfaConfigCommand({ + UserPoolId: userPoolId, + }); + const mfaResponse = await this.cognitoIdentityProviderClient.send( + mfaCommand + ); + return mfaResponse; + }; + + private getUserPoolGroups = async (userPoolId: string) => { + let nextToken: string | undefined; + const groups: GroupType[] = []; + do { + const listGroupsResponse = await this.cognitoIdentityProviderClient.send( + new ListGroupsCommand({ + UserPoolId: userPoolId, + NextToken: nextToken, + }) + ); + if (!listGroupsResponse.Groups) { + throw new Error( + 'An error occurred while retrieving the groups for the user pool.' + ); + } + groups.push(...listGroupsResponse.Groups); + nextToken = listGroupsResponse.NextToken; + } while (nextToken); + return groups; + }; + + private getUserPoolProviders = async (userPoolId: string) => { + const providers: ProviderDescription[] = []; + let nextToken: string | undefined; + do { + const providersResponse = await this.cognitoIdentityProviderClient.send( + new ListIdentityProvidersCommand({ + UserPoolId: userPoolId, + NextToken: nextToken, + }) + ); + if (providersResponse.Providers === undefined) { + throw new Error( + 'An error occurred while retrieving identity providers for the user pool.' + ); + } + providers.push(...providersResponse.Providers); + nextToken = providersResponse.NextToken; + } while (nextToken); + return providers; + }; + + private getIdentityPool = async (identityPoolId: string) => { + const idpResponse = await this.cognitoIdentityClient.send( + new DescribeIdentityPoolCommand({ + IdentityPoolId: identityPoolId, + }) + ); + if (!idpResponse.IdentityPoolId) { + throw new Error( + 'An error occurred while retrieving the identity pool details.' + ); + } + return idpResponse; + }; + + private getIdentityPoolRoles = async (identityPoolId: string) => { + const rolesCommand = new GetIdentityPoolRolesCommand({ + IdentityPoolId: identityPoolId, + }); + const rolesResponse = await this.cognitoIdentityClient.send(rolesCommand); + if (!rolesResponse.Roles) { + throw new Error( + 'An error occurred while retrieving the roles for the identity pool.' + ); + } + return rolesResponse.Roles; + }; + + private getUserPoolClient = async ( + userPoolId: string, + userPoolClientId: string + ) => { + const userPoolClientCommand = new DescribeUserPoolClientCommand({ + UserPoolId: userPoolId, + ClientId: userPoolClientId, + }); + const userPoolClientResponse = + await this.cognitoIdentityProviderClient.send(userPoolClientCommand); + if (!userPoolClientResponse.UserPoolClient) { + throw new Error( + 'An error occurred while retrieving the user pool client details.' + ); + } + return userPoolClientResponse.UserPoolClient; + }; + + /** + * Retrieves all of the resource data that is necessary for validation and output generation. + * @param userPoolId userPoolId + * @param identityPoolId identityPoolId + * @param userPoolClientId userPoolClientId + * @returns all necessary resource data + */ + private getResourceDetails = async ( + userPoolId: string, + identityPoolId: string, + userPoolClientId: string + ) => { + const { userPool, userPoolPasswordPolicy } = await this.getUserPool( + userPoolId + ); + const userPoolMFA = await this.getUserPoolMFASettings(userPoolId); + const userPoolProviders = await this.getUserPoolProviders(userPoolId); + const userPoolGroups = await this.getUserPoolGroups(userPoolId); + const userPoolClient = await this.getUserPoolClient( + userPoolId, + userPoolClientId + ); + const identityPool = await this.getIdentityPool(identityPoolId); + const roles = await this.getIdentityPoolRoles(identityPoolId); + return { + userPool, + userPoolPasswordPolicy, + userPoolMFA, + userPoolProviders, + userPoolGroups, + userPoolClient, + identityPool, + roles, + }; + }; + + /** + * Validate the resource associations. + * 1. make sure the user pool & user pool client pair are a cognito provider for the identity pool + * 2. make sure the provided auth/unauth role ARNs match the roles for the identity pool + * 3. make sure the user pool client is a web client + * @param userPool userPool + * @param userPoolProviders the user pool providers + * @param userPoolGroups the existing groups for the userPool + * @param userPoolClient userPoolClient + * @param identityPool identityPool + * @param identityPoolRoles identityPool roles + * @param props props that include the roles which we compare with the actual roles for the identity pool + */ + private validateResources = ( + userPool: UserPoolType, + userPoolProviders: ProviderDescription[], + userPoolGroups: GroupType[], + userPoolClient: UserPoolClientType, + identityPool: DescribeIdentityPoolCommandOutput, + identityPoolRoles: Record, + props: ReferenceAuthInitializerProps + ) => { + // verify the user pool is a cognito provider for this identity pool + if ( + !identityPool.CognitoIdentityProviders || + identityPool.CognitoIdentityProviders.length === 0 + ) { + throw new Error( + 'The specified identity pool does not have any cognito identity providers.' + ); + } + // check for alias attributes, since we don't support this yet + if (userPool.AliasAttributes && userPool.AliasAttributes.length > 0) { + throw new Error( + 'The specified user pool is configured with alias attributes which are not currently supported.' + ); + } + + // check OAuth settings + if (userPoolProviders.length > 0) { + // validate user pool + const domainSpecified = userPool.Domain || userPool.CustomDomain; + if (!domainSpecified) { + throw new Error( + 'You must configure a domain for your UserPool if external login providers are enabled.' + ); + } + + // validate user pool client + const hasLogoutUrls = + userPoolClient.LogoutURLs && userPoolClient.LogoutURLs.length > 0; + const hasCallbackUrls = + userPoolClient.CallbackURLs && userPoolClient.CallbackURLs.length > 0; + if (!hasLogoutUrls) { + throw new Error( + 'Your UserPool client must have "Allowed sign-out URLs" configured if external login providers are enabled.' + ); + } + if (!hasCallbackUrls) { + throw new Error( + 'Your UserPool client must have "Allowed callback URLs" configured if external login providers are enabled.' + ); + } + } + + // make sure props groups Roles actually exist for the user pool + const groupEntries = Object.entries(props.groups); + for (const [groupName, groupRoleARN] of groupEntries) { + const match = userPoolGroups.find((g) => g.RoleArn === groupRoleARN); + if (match === undefined) { + throw new Error( + `The group '${groupName}' with role '${groupRoleARN}' does not match any group for the specified user pool.` + ); + } + } + // verify that the user pool + user pool client pair are configured with the identity pool + const matchingProvider = identityPool.CognitoIdentityProviders.find((p) => { + const matchingUserPool: boolean = + p.ProviderName === + `cognito-idp.${props.region}.amazonaws.com/${userPool.Id}`; + const matchingUserPoolClient: boolean = + p.ClientId === userPoolClient.ClientId; + return matchingUserPool && matchingUserPoolClient; + }); + if (!matchingProvider) { + throw new Error( + 'The user pool and user pool client pair do not match any cognito identity providers for the specified identity pool.' + ); + } + // verify the auth / unauth roles from the props match the identity pool roles that we retrieved + const authRoleArn = identityPoolRoles['authenticated']; + const unauthRoleArn = identityPoolRoles['unauthenticated']; + if (authRoleArn !== props.authRoleArn) { + throw new Error( + 'The provided authRoleArn does not match the authenticated role for the specified identity pool.' + ); + } + if (unauthRoleArn !== props.unauthRoleArn) { + throw new Error( + 'The provided unauthRoleArn does not match the unauthenticated role for the specified identity pool.' + ); + } + + // make sure the client is a web client here (web clients shouldn't have client secrets) + if (userPoolClient?.ClientSecret) { + throw new Error( + 'The specified user pool client is not configured as a web client.' + ); + } + }; + + /** + * Transform the userPool data into outputs. + * @param userPool user pool + * @param userPoolPasswordPolicy user pool password policy + * @param userPoolProviders user pool providers + * @param userPoolMFA user pool MFA settings + * @returns formatted outputs + */ + private getUserPoolOutputs = ( + userPool: UserPoolType, + userPoolPasswordPolicy: PasswordPolicyType, + userPoolProviders: ProviderDescription[], + userPoolMFA: GetUserPoolMfaConfigCommandOutput, + region: string + ) => { + // password policy requirements + const requirements: string[] = []; + userPoolPasswordPolicy.RequireNumbers && + requirements.push('REQUIRES_NUMBERS'); + userPoolPasswordPolicy.RequireLowercase && + requirements.push('REQUIRES_LOWERCASE'); + userPoolPasswordPolicy.RequireUppercase && + requirements.push('REQUIRES_UPPERCASE'); + userPoolPasswordPolicy.RequireSymbols && + requirements.push('REQUIRES_SYMBOLS'); + // mfa types + const mfaTypes: string[] = []; + if ( + userPoolMFA.SmsMfaConfiguration && + userPoolMFA.SmsMfaConfiguration.SmsConfiguration + ) { + mfaTypes.push('SMS_MFA'); + } + if (userPoolMFA.SoftwareTokenMfaConfiguration?.Enabled) { + mfaTypes.push('TOTP'); + } + // social providers + const socialProviders: string[] = []; + if (userPoolProviders) { + for (const provider of userPoolProviders) { + const providerType = provider.ProviderType; + const providerName = provider.ProviderName; + if (providerType === 'Google') { + socialProviders.push('GOOGLE'); + } + if (providerType === 'Facebook') { + socialProviders.push('FACEBOOK'); + } + if (providerType === 'SignInWithApple') { + socialProviders.push('SIGN_IN_WITH_APPLE'); + } + if (providerType === 'LoginWithAmazon') { + socialProviders.push('LOGIN_WITH_AMAZON'); + } + if (providerType === 'OIDC' && providerName) { + socialProviders.push(providerName); + } + if (providerType === 'SAML' && providerName) { + socialProviders.push(providerName); + } + } + } + + // domain + const oauthDomain = userPool.CustomDomain ?? userPool.Domain ?? ''; + const fullDomainPath = `${oauthDomain}.auth.${region}.amazoncognito.com`; + const data = { + signupAttributes: JSON.stringify( + userPool.SchemaAttributes?.filter( + (attribute) => attribute.Required && attribute.Name + ).map((attribute) => attribute.Name?.toLowerCase()) || [] + ), + usernameAttributes: JSON.stringify( + userPool.UsernameAttributes?.map((attribute) => + attribute.toLowerCase() + ) || [] + ), + verificationMechanisms: JSON.stringify( + userPool.AutoVerifiedAttributes ?? [] + ), + passwordPolicyMinLength: + userPoolPasswordPolicy.MinimumLength === undefined + ? '' + : userPoolPasswordPolicy.MinimumLength.toString(), + passwordPolicyRequirements: JSON.stringify(requirements), + mfaConfiguration: userPool.MfaConfiguration ?? 'OFF', + mfaTypes: JSON.stringify(mfaTypes), + socialProviders: JSON.stringify(socialProviders), + oauthCognitoDomain: fullDomainPath, + }; + return data; + }; + + /** + * Transforms identityPool info into outputs. + * @param identityPool identity pool data + * @returns formatted outputs + */ + private getIdentityPoolOutputs = ( + identityPool: DescribeIdentityPoolCommandOutput + ) => { + const data = { + allowUnauthenticatedIdentities: + identityPool.AllowUnauthenticatedIdentities === true ? 'true' : 'false', + }; + return data; + }; + + /** + * Transforms userPoolClient info into outputs. + * @param userPoolClient userPoolClient data + * @returns formatted outputs + */ + private getUserPoolClientOutputs = (userPoolClient: UserPoolClientType) => { + const data = { + oauthScope: JSON.stringify(userPoolClient.AllowedOAuthScopes ?? []), + oauthRedirectSignIn: userPoolClient.CallbackURLs + ? userPoolClient.CallbackURLs.join(',') + : '', + oauthRedirectSignOut: userPoolClient.LogoutURLs + ? userPoolClient.LogoutURLs.join(',') + : '', + oauthResponseType: userPoolClient.AllowedOAuthFlows + ? userPoolClient.AllowedOAuthFlows.join(',') + : '', + oauthClientId: userPoolClient.ClientId, + }; + return data; + }; +} + +/** + * Entry point for the lambda-backend custom resource to retrieve auth outputs. + */ +export const handler = async ( + event: CloudFormationCustomResourceEvent +): Promise => { + const initializer = new ReferenceAuthInitializer( + new CognitoIdentityClient(), + new CognitoIdentityProviderClient(), + randomUUID + ); + return initializer.handleEvent(event); +}; diff --git a/packages/backend-auth/src/reference_construct.test.ts b/packages/backend-auth/src/reference_construct.test.ts new file mode 100644 index 00000000000..8c852add5e7 --- /dev/null +++ b/packages/backend-auth/src/reference_construct.test.ts @@ -0,0 +1,184 @@ +import { beforeEach, describe, it, mock } from 'node:test'; +import assert from 'assert'; +import { + AmplifyReferenceAuth, + OUTPUT_PROPERTIES_PROVIDED_BY_AUTH_CUSTOM_RESOURCE, + authOutputKey, +} from './reference_construct.js'; +import { + BackendOutputEntry, + BackendOutputStorageStrategy, +} from '@aws-amplify/plugin-types'; +import { Template } from 'aws-cdk-lib/assertions'; +import { App, Stack } from 'aws-cdk-lib'; +import { ReferenceAuthProps } from './reference_factory.js'; +const refAuthProps: ReferenceAuthProps = { + authRoleArn: 'arn:aws:iam::000000000000:role/amplify-sample-auth-role-name', + unauthRoleArn: + 'arn:aws:iam::000000000000:role/amplify-sample-unauth-role-name', + identityPoolId: 'us-east-1:identityPoolId', + userPoolClientId: 'userPoolClientId', + userPoolId: 'us-east-1_userPoolId', +}; + +void describe('AmplifyConstruct', () => { + void it('creates custom resource initializer', () => { + const app = new App(); + const stack = new Stack(app); + new AmplifyReferenceAuth(stack, 'test', refAuthProps); + const template = Template.fromStack(stack); + // check that custom resource is created with properties + template.hasResourceProperties('Custom::AmplifyRefAuth', { + identityPoolId: refAuthProps.identityPoolId, + userPoolId: refAuthProps.userPoolId, + userPoolClientId: refAuthProps.userPoolClientId, + }); + }); + + void it('creates policy documents for custom resource', () => { + const app = new App(); + const stack = new Stack(app); + new AmplifyReferenceAuth(stack, 'test', refAuthProps); + const template = Template.fromStack(stack); + const policyStatements = [ + { + Action: [ + 'cognito-idp:DescribeUserPool', + 'cognito-idp:GetUserPoolMfaConfig', + 'cognito-idp:ListIdentityProviders', + 'cognito-idp:ListGroups', + 'cognito-idp:DescribeUserPoolClient', + ], + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':cognito-idp:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + `:userpool/${refAuthProps.userPoolId}`, + ], + ], + }, + }, + { + Action: [ + 'cognito-identity:DescribeIdentityPool', + 'cognito-identity:GetIdentityPoolRoles', + ], + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:cognito-identity:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + `:identitypool/${refAuthProps.identityPoolId}`, + ], + ], + }, + }, + ]; + template.hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: policyStatements, + Version: '2012-10-17', + }, + }); + }); + + void it('generates the correct output values', () => { + const app = new App(); + const stack = new Stack(app); + new AmplifyReferenceAuth(stack, 'test', refAuthProps); + const template = Template.fromStack(stack); + // check that outputs reference custom resource attributes + const outputs = template.findOutputs('*'); + for (const property of OUTPUT_PROPERTIES_PROVIDED_BY_AUTH_CUSTOM_RESOURCE) { + const expectedValue = { + 'Fn::GetAtt': ['AmplifyRefAuthCustomResource', `${property}`], + }; + assert.ok(outputs[property]); + const actualValue = outputs[property]['Value']; + assert.deepEqual(actualValue, expectedValue); + } + }); + + void describe('storeOutput strategy', () => { + let app: App; + let stack: Stack; + const storeOutputMock = mock.fn(); + const stubBackendOutputStorageStrategy: BackendOutputStorageStrategy = + { + addBackendOutputEntry: storeOutputMock, + appendToBackendOutputList: storeOutputMock, + }; + + void beforeEach(() => { + app = new App(); + stack = new Stack(app); + storeOutputMock.mock.resetCalls(); + }); + + void it('stores output using custom strategy and basic props', () => { + const authConstruct = new AmplifyReferenceAuth(stack, 'test', { + ...refAuthProps, + outputStorageStrategy: stubBackendOutputStorageStrategy, + }); + + const expectedUserPoolId = authConstruct.resources.userPool.userPoolId; + const expectedIdentityPoolId = authConstruct.resources.identityPoolId; + const expectedWebClientId = + authConstruct.resources.userPoolClient.userPoolClientId; + const expectedRegion = Stack.of(authConstruct).region; + + const storeOutputArgs = storeOutputMock.mock.calls[0].arguments; + assert.equal(storeOutputArgs.length, 2); + assert.equal(storeOutputArgs[0], authOutputKey); + assert.equal(storeOutputArgs[1]['version'], '1'); + const payload = storeOutputArgs[1]['payload']; + assert.equal(payload['userPoolId'], expectedUserPoolId); + assert.equal(payload['identityPoolId'], expectedIdentityPoolId); + assert.equal(payload['webClientId'], expectedWebClientId); + assert.equal(payload['authRegion'], expectedRegion); + }); + + void it('stores output when no storage strategy is injected', () => { + const app = new App(); + const stack = new Stack(app); + new AmplifyReferenceAuth(stack, 'test', refAuthProps); + + const template = Template.fromStack(stack); + template.templateMatches({ + Metadata: { + [authOutputKey]: { + version: '1', + stackOutputs: [ + 'userPoolId', + 'webClientId', + 'identityPoolId', + 'authRegion', + ...OUTPUT_PROPERTIES_PROVIDED_BY_AUTH_CUSTOM_RESOURCE, + ], + }, + }, + }); + }); + }); +}); diff --git a/packages/backend-auth/src/reference_construct.ts b/packages/backend-auth/src/reference_construct.ts new file mode 100644 index 00000000000..939319a26f4 --- /dev/null +++ b/packages/backend-auth/src/reference_construct.ts @@ -0,0 +1,224 @@ +import { Construct } from 'constructs'; +import { + CustomResource, + Duration, + Stack, + aws_cognito, + aws_iam, +} from 'aws-cdk-lib'; +import { + BackendOutputStorageStrategy, + ReferenceAuthResources, + ResourceProvider, +} from '@aws-amplify/plugin-types'; +import { + AttributionMetadataStorage, + StackMetadataBackendOutputStorageStrategy, +} from '@aws-amplify/backend-output-storage'; +import { AuthOutput } from '@aws-amplify/backend-output-schemas'; +import * as path from 'path'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { Runtime } from 'aws-cdk-lib/aws-lambda'; +import { Provider } from 'aws-cdk-lib/custom-resources'; +import { Role } from 'aws-cdk-lib/aws-iam'; +import { ReferenceAuthInitializerProps } from './lambda/reference_auth_initializer.js'; +import { fileURLToPath } from 'node:url'; +import { ReferenceAuthProps } from './reference_factory.js'; + +/** + * Expected key that auth output is stored under - must match backend-output-schemas's authOutputKey + */ +export const authOutputKey = 'AWS::Amplify::Auth'; + +const REFERENCE_AUTH_CUSTOM_RESOURCE_PROVIDER_ID = + 'AmplifyRefAuthCustomResourceProvider'; +const REFERENCE_AUTH_CUSTOM_RESOURCE_ID = 'AmplifyRefAuthCustomResource'; +const RESOURCE_TYPE = 'Custom::AmplifyRefAuth'; + +const filename = fileURLToPath(import.meta.url); +const dirname = path.dirname(filename); +const resourcesRoot = path.normalize(path.join(dirname, 'lambda')); +const refAuthLambdaFilePath = path.join( + resourcesRoot, + 'reference_auth_initializer.js' +); + +const authStackType = 'auth-Cognito'; + +/** + * These properties are fetched by the custom resource and must be accounted for + * in the final AuthOutput payload. + */ +export const OUTPUT_PROPERTIES_PROVIDED_BY_AUTH_CUSTOM_RESOURCE: (keyof AuthOutput['payload'])[] = + [ + 'allowUnauthenticatedIdentities', + 'signupAttributes', + 'usernameAttributes', + 'verificationMechanisms', + 'passwordPolicyMinLength', + 'passwordPolicyRequirements', + 'mfaConfiguration', + 'mfaTypes', + 'socialProviders', + 'oauthCognitoDomain', + 'oauthScope', + 'oauthRedirectSignIn', + 'oauthRedirectSignOut', + 'oauthResponseType', + 'oauthClientId', + ]; +/** + * Reference Auth construct for using external auth resources + */ +export class AmplifyReferenceAuth + extends Construct + implements ResourceProvider +{ + resources: ReferenceAuthResources; + + private configurationCustomResource: CustomResource; + + /** + * Create a new AmplifyConstruct + */ + constructor(scope: Construct, id: string, props: ReferenceAuthProps) { + super(scope, id); + + this.resources = { + userPool: aws_cognito.UserPool.fromUserPoolId( + this, + 'UserPool', + props.userPoolId + ), + userPoolClient: aws_cognito.UserPoolClient.fromUserPoolClientId( + this, + 'UserPoolClient', + props.userPoolClientId + ), + authenticatedUserIamRole: aws_iam.Role.fromRoleArn( + this, + 'authenticatedUserRole', + props.authRoleArn + ), + unauthenticatedUserIamRole: aws_iam.Role.fromRoleArn( + this, + 'unauthenticatedUserRole', + props.unauthRoleArn + ), + identityPoolId: props.identityPoolId, + groups: {}, + }; + + // mapping of existing group roles + if (props.groups) { + Object.entries(props.groups).forEach(([groupName, roleArn]) => { + this.resources.groups[groupName] = { + role: Role.fromRoleArn(this, `${groupName}GroupRole`, roleArn), + }; + }); + } + + // custom resource lambda + const refAuthLambda = new NodejsFunction( + scope, + `${REFERENCE_AUTH_CUSTOM_RESOURCE_PROVIDER_ID}Lambda`, + { + runtime: Runtime.NODEJS_18_X, + timeout: Duration.seconds(10), + entry: refAuthLambdaFilePath, + handler: 'handler', + } + ); + // UserPool & UserPoolClient specific permissions + refAuthLambda.grantPrincipal.addToPrincipalPolicy( + new aws_iam.PolicyStatement({ + effect: aws_iam.Effect.ALLOW, + actions: [ + 'cognito-idp:DescribeUserPool', + 'cognito-idp:GetUserPoolMfaConfig', + 'cognito-idp:ListIdentityProviders', + 'cognito-idp:ListGroups', + 'cognito-idp:DescribeUserPoolClient', + ], + resources: [this.resources.userPool.userPoolArn], + }) + ); + // IdentityPool specific permissions + const stack = Stack.of(this); + refAuthLambda.grantPrincipal.addToPrincipalPolicy( + new aws_iam.PolicyStatement({ + effect: aws_iam.Effect.ALLOW, + actions: [ + 'cognito-identity:DescribeIdentityPool', + 'cognito-identity:GetIdentityPoolRoles', + ], + resources: [ + `arn:aws:cognito-identity:${stack.region}:${stack.account}:identitypool/${this.resources.identityPoolId}`, + ], + }) + ); + const provider = new Provider( + scope, + REFERENCE_AUTH_CUSTOM_RESOURCE_PROVIDER_ID, + { + onEventHandler: refAuthLambda, + } + ); + const initializerProps: ReferenceAuthInitializerProps = { + userPoolId: props.userPoolId, + identityPoolId: props.identityPoolId, + userPoolClientId: props.userPoolClientId, + authRoleArn: props.authRoleArn, + unauthRoleArn: props.unauthRoleArn, + groups: props.groups ?? {}, + region: Stack.of(this).region, + }; + // custom resource + this.configurationCustomResource = new CustomResource( + scope, + REFERENCE_AUTH_CUSTOM_RESOURCE_ID, + { + serviceToken: provider.serviceToken, + properties: { + ...initializerProps, + }, + resourceType: RESOURCE_TYPE, + } + ); + + this.storeOutput(props.outputStorageStrategy); + new AttributionMetadataStorage().storeAttributionMetadata( + Stack.of(this), + authStackType, + fileURLToPath(new URL('../package.json', import.meta.url)) + ); + } + + /** + * Stores auth output using the provided strategy + */ + private storeOutput = ( + outputStorageStrategy: BackendOutputStorageStrategy = new StackMetadataBackendOutputStorageStrategy( + Stack.of(this) + ) + ): void => { + // these properties cannot be overwritten + const output: AuthOutput['payload'] = { + userPoolId: this.resources.userPool.userPoolId, + webClientId: this.resources.userPoolClient.userPoolClientId, + identityPoolId: this.resources.identityPoolId, + authRegion: Stack.of(this).region, + }; + + // assign cdk tokens which will be resolved during deployment + for (const property of OUTPUT_PROPERTIES_PROVIDED_BY_AUTH_CUSTOM_RESOURCE) { + output[property] = + this.configurationCustomResource.getAttString(property); + } + + outputStorageStrategy.addBackendOutputEntry(authOutputKey, { + version: '1', + payload: output, + }); + }; +} diff --git a/packages/backend-auth/src/reference_factory.test.ts b/packages/backend-auth/src/reference_factory.test.ts new file mode 100644 index 00000000000..ee16e7317e0 --- /dev/null +++ b/packages/backend-auth/src/reference_factory.test.ts @@ -0,0 +1,279 @@ +import { beforeEach, describe, it, mock } from 'node:test'; +import { App, Stack } from 'aws-cdk-lib'; +import assert from 'node:assert'; +import { Template } from 'aws-cdk-lib/assertions'; +import { + BackendOutputEntry, + BackendOutputStorageStrategy, + ConstructContainer, + ConstructFactory, + ConstructFactoryGetInstanceProps, + ImportPathVerifier, + ResourceAccessAcceptorFactory, + ResourceNameValidator, + ResourceProvider, +} from '@aws-amplify/plugin-types'; +import { StackMetadataBackendOutputStorageStrategy } from '@aws-amplify/backend-output-storage'; +import { + ConstructContainerStub, + ImportPathVerifierStub, + ResourceNameValidatorStub, + StackResolverStub, +} from '@aws-amplify/backend-platform-test-stubs'; +import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; +import { + AmplifyReferenceAuthProps, + BackendReferenceAuth, + referenceAuth, +} from './reference_factory.js'; +import { AmplifyAuthFactory } from './factory.js'; + +const defaultReferenceAuthProps: AmplifyReferenceAuthProps = { + authRoleArn: 'arn:aws:iam::000000000000:role/amplify-sample-auth-role-name', + unauthRoleArn: + 'arn:aws:iam::000000000000:role/amplify-sample-unauth-role-name', + identityPoolId: 'us-east-1:identityPoolId', + userPoolClientId: 'userPoolClientId', + userPoolId: 'us-east-1_userPoolId', +}; + +const createStackAndSetContext = (): Stack => { + const app = new App(); + app.node.setContext('amplify-backend-name', 'testEnvName'); + app.node.setContext('amplify-backend-namespace', 'testBackendId'); + app.node.setContext('amplify-backend-type', 'branch'); + const stack = new Stack(app); + return stack; +}; + +void describe('AmplifyReferenceAuthFactory', () => { + let authFactory: ConstructFactory; + let constructContainer: ConstructContainer; + let outputStorageStrategy: BackendOutputStorageStrategy; + let importPathVerifier: ImportPathVerifier; + let getInstanceProps: ConstructFactoryGetInstanceProps; + let resourceNameValidator: ResourceNameValidator; + let stack: Stack; + beforeEach(() => { + resetFactoryCount(); + authFactory = referenceAuth(defaultReferenceAuthProps); + + stack = createStackAndSetContext(); + + constructContainer = new ConstructContainerStub( + new StackResolverStub(stack) + ); + + outputStorageStrategy = new StackMetadataBackendOutputStorageStrategy( + stack + ); + + importPathVerifier = new ImportPathVerifierStub(); + + resourceNameValidator = new ResourceNameValidatorStub(); + + getInstanceProps = { + constructContainer, + outputStorageStrategy, + importPathVerifier, + resourceNameValidator, + }; + }); + + void it('returns singleton instance', () => { + const instance1 = authFactory.getInstance(getInstanceProps); + const instance2 = authFactory.getInstance(getInstanceProps); + + assert.strictEqual(instance1, instance2); + }); + + void it('verifies stack property exists and is equivalent to auth stack', () => { + const backendAuth = authFactory.getInstance(getInstanceProps); + assert.equal(backendAuth.stack, Stack.of(backendAuth.resources.userPool)); + }); + + void it('adds construct to stack', () => { + const backendAuth = authFactory.getInstance(getInstanceProps); + + const template = Template.fromStack(backendAuth.stack); + + template.resourceCountIs('Custom::AmplifyRefAuth', 1); + }); + + void it('verifies constructor import path', () => { + const importPathVerifier = { + verify: mock.fn(), + }; + + authFactory.getInstance({ ...getInstanceProps, importPathVerifier }); + + assert.ok( + (importPathVerifier.verify.mock.calls[0].arguments[0] as string).includes( + 'referenceAuth' + ) + ); + }); + + void it('should throw TooManyAmplifyAuthFactoryError when referenceAuth is called multiple times', () => { + assert.throws( + () => { + referenceAuth({ + ...defaultReferenceAuthProps, + }); + referenceAuth({ + ...defaultReferenceAuthProps, + }); + }, + new AmplifyUserError('MultipleSingletonResourcesError', { + message: + 'Multiple `defineAuth` or `referenceAuth` calls are not allowed within an Amplify backend', + resolution: 'Remove all but one `defineAuth` or `referenceAuth` call', + }) + ); + }); + + void it('if access is defined, it should attach valid policy to the resource', () => { + const mockAcceptResourceAccess = mock.fn(); + const lambdaResourceStub = { + getInstance: () => ({ + getResourceAccessAcceptor: () => ({ + acceptResourceAccess: mockAcceptResourceAccess, + }), + }), + } as unknown as ConstructFactory< + ResourceProvider & ResourceAccessAcceptorFactory + >; + + resetFactoryCount(); + + authFactory = referenceAuth({ + ...defaultReferenceAuthProps, + access: (allow) => [ + allow.resource(lambdaResourceStub).to(['managePasswordRecovery']), + allow.resource(lambdaResourceStub).to(['createUser']), + ], + }); + + const backendAuth = authFactory.getInstance(getInstanceProps); + + assert.equal(mockAcceptResourceAccess.mock.callCount(), 2); + assert.ok( + mockAcceptResourceAccess.mock.calls[0].arguments[0] instanceof Policy + ); + assert.deepStrictEqual( + mockAcceptResourceAccess.mock.calls[0].arguments[0].document.toJSON(), + { + Statement: [ + { + Action: [ + 'cognito-idp:AdminResetUserPassword', + 'cognito-idp:AdminSetUserPassword', + ], + Effect: 'Allow', + Resource: backendAuth.resources.userPool.userPoolArn, + }, + ], + Version: '2012-10-17', + } + ); + assert.ok( + mockAcceptResourceAccess.mock.calls[1].arguments[0] instanceof Policy + ); + assert.deepStrictEqual( + mockAcceptResourceAccess.mock.calls[1].arguments[0].document.toJSON(), + { + Statement: [ + { + Action: 'cognito-idp:AdminCreateUser', + Effect: 'Allow', + Resource: backendAuth.resources.userPool.userPoolArn, + }, + ], + Version: '2012-10-17', + } + ); + }); + + void describe('getResourceAccessAcceptor', () => { + void it('attaches policies to the authenticated role', () => { + const backendAuth = authFactory.getInstance(getInstanceProps); + const testPolicy = new Policy(stack, 'testPolicy', { + statements: [ + new PolicyStatement({ + actions: ['s3:GetObject'], + resources: ['testBucket/testObject/*'], + }), + ], + }); + const resourceAccessAcceptor = backendAuth.getResourceAccessAcceptor( + 'authenticatedUserIamRole' + ); + + assert.equal( + resourceAccessAcceptor.identifier, + 'authenticatedUserIamRoleResourceAccessAcceptor' + ); + + resourceAccessAcceptor.acceptResourceAccess(testPolicy, [ + { name: 'test', path: 'test' }, + ]); + const template = Template.fromStack(stack); + template.resourceCountIs('AWS::IAM::Policy', 1); + template.hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 's3:GetObject', + Effect: 'Allow', + Resource: 'testBucket/testObject/*', + }, + ], + }, + Roles: [backendAuth.resources.authenticatedUserIamRole.roleName], + }); + }); + + void it('attaches policies to the unauthenticated role', () => { + const backendAuth = authFactory.getInstance(getInstanceProps); + const testPolicy = new Policy(stack, 'testPolicy', { + statements: [ + new PolicyStatement({ + actions: ['s3:GetObject'], + resources: ['testBucket/testObject/*'], + }), + ], + }); + const resourceAccessAcceptor = backendAuth.getResourceAccessAcceptor( + 'unauthenticatedUserIamRole' + ); + + assert.equal( + resourceAccessAcceptor.identifier, + 'unauthenticatedUserIamRoleResourceAccessAcceptor' + ); + + resourceAccessAcceptor.acceptResourceAccess(testPolicy, [ + { name: 'test', path: 'test' }, + ]); + const template = Template.fromStack(stack); + template.resourceCountIs('AWS::IAM::Policy', 1); + template.hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 's3:GetObject', + Effect: 'Allow', + Resource: 'testBucket/testObject/*', + }, + ], + }, + Roles: [backendAuth.resources.unauthenticatedUserIamRole.roleName], + }); + }); + }); +}); + +const resetFactoryCount = () => { + AmplifyAuthFactory.factoryCount = 0; +}; diff --git a/packages/backend-auth/src/reference_factory.ts b/packages/backend-auth/src/reference_factory.ts new file mode 100644 index 00000000000..926db833c94 --- /dev/null +++ b/packages/backend-auth/src/reference_factory.ts @@ -0,0 +1,239 @@ +import { + AuthRoleName, + BackendOutputStorageStrategy, + ConstructContainerEntryGenerator, + ConstructFactory, + ConstructFactoryGetInstanceProps, + GenerateContainerEntryProps, + ReferenceAuthResources, + ResourceAccessAcceptor, + ResourceAccessAcceptorFactory, + ResourceProvider, + StackProvider, +} from '@aws-amplify/plugin-types'; +import { AuthAccessGenerator, Expand } from './types.js'; +import { authAccessBuilder as _authAccessBuilder } from './access_builder.js'; +import path from 'path'; +import { AmplifyUserError, TagName } from '@aws-amplify/platform-core'; +import { AuthAccessPolicyArbiterFactory } from './auth_access_policy_arbiter.js'; +import { Stack, Tags } from 'aws-cdk-lib'; +import { Policy } from 'aws-cdk-lib/aws-iam'; +import { UserPoolAccessPolicyFactory } from './userpool_access_policy_factory.js'; +import { AmplifyAuthFactory } from './factory.js'; +import { AmplifyReferenceAuth } from './reference_construct.js'; +import { AuthOutput } from '@aws-amplify/backend-output-schemas'; + +export type ReferenceAuthProps = { + /** + * @internal + */ + outputStorageStrategy?: BackendOutputStorageStrategy; + /** + * Existing UserPool Id + */ + userPoolId: string; + /** + * Existing IdentityPool Id + */ + identityPoolId: string; + /** + * Existing UserPoolClient Id + */ + userPoolClientId: string; + /** + * Existing AuthRole ARN + */ + authRoleArn: string; + /** + * Existing UnauthRole ARN + */ + unauthRoleArn: string; + /** + * A mapping of existing group names and their associated role ARNs + * which can be used for group permissions. + */ + groups?: { + [groupName: string]: string; + }; +}; + +export type BackendReferenceAuth = ResourceProvider & + ResourceAccessAcceptorFactory & + StackProvider; + +export type AmplifyReferenceAuthProps = Expand< + Omit & { + /** + * Configure access to auth for other Amplify resources + * @see https://docs.amplify.aws/react/build-a-backend/auth/grant-access-to-auth-resources/ + * @example + * access: (allow) => [allow.resource(postConfirmation).to(["addUserToGroup"])] + * @example + * access: (allow) => [allow.resource(groupManager).to(["manageGroups"])] + */ + access?: AuthAccessGenerator; + } +>; +/** + * Singleton factory for AmplifyReferenceAuth that can be used in Amplify project files. + * + * Exported for testing purpose only & should NOT be exported out of the package. + */ +export class AmplifyReferenceAuthFactory + implements ConstructFactory +{ + readonly provides = 'AuthResources'; + + private generator: ConstructContainerEntryGenerator; + + /** + * Set the properties that will be used to initialize AmplifyReferenceAuth + */ + constructor( + private readonly props: AmplifyReferenceAuthProps, + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors + private readonly importStack = new Error().stack + ) { + if (AmplifyAuthFactory.factoryCount > 0) { + throw new AmplifyUserError('MultipleSingletonResourcesError', { + message: + 'Multiple `defineAuth` or `referenceAuth` calls are not allowed within an Amplify backend', + resolution: 'Remove all but one `defineAuth` or `referenceAuth` call', + }); + } + AmplifyAuthFactory.factoryCount++; + } + /** + * Get a singleton instance of AmplifyReferenceAuth + */ + getInstance = ( + getInstanceProps: ConstructFactoryGetInstanceProps + ): BackendReferenceAuth => { + const { constructContainer, importPathVerifier } = getInstanceProps; + importPathVerifier?.verify( + this.importStack, + path.join('amplify', 'auth', 'resource'), + 'Amplify Auth must be defined in amplify/auth/resource.ts' + ); + if (!this.generator) { + this.generator = new AmplifyReferenceAuthGenerator( + this.props, + getInstanceProps + ); + } + return constructContainer.getOrCompute( + this.generator + ) as BackendReferenceAuth; + }; +} +class AmplifyReferenceAuthGenerator + implements ConstructContainerEntryGenerator +{ + readonly resourceGroupName = 'auth'; + private readonly name: string; + + constructor( + private readonly props: AmplifyReferenceAuthProps, + private readonly getInstanceProps: ConstructFactoryGetInstanceProps, + private readonly authAccessBuilder = _authAccessBuilder, + private readonly authAccessPolicyArbiterFactory = new AuthAccessPolicyArbiterFactory() + ) { + this.name = 'amplifyAuth'; + } + + generateContainerEntry = ({ + scope, + ssmEnvironmentEntriesGenerator, + }: GenerateContainerEntryProps) => { + const authProps: ReferenceAuthProps = { + ...this.props, + outputStorageStrategy: this.getInstanceProps.outputStorageStrategy, + }; + + let authConstruct: AmplifyReferenceAuth; + try { + authConstruct = new AmplifyReferenceAuth(scope, this.name, authProps); + } catch (error) { + throw new AmplifyUserError( + 'AmplifyReferenceAuthConstructInitializationError', + { + message: 'Failed to instantiate reference auth construct', + resolution: 'See the underlying error message for more details.', + }, + error as Error + ); + } + + Tags.of(authConstruct).add(TagName.FRIENDLY_NAME, this.name); + + const authConstructMixin: BackendReferenceAuth = { + ...authConstruct, + /** + * Returns a resourceAccessAcceptor for the given role + * @param roleIdentifier Either the auth or unauth role name or the name of a UserPool group + */ + getResourceAccessAcceptor: ( + roleIdentifier: AuthRoleName | string + ): ResourceAccessAcceptor => ({ + identifier: `${roleIdentifier}ResourceAccessAcceptor`, + acceptResourceAccess: (policy: Policy) => { + const role = roleNameIsAuthRoleName(roleIdentifier) + ? authConstruct.resources[roleIdentifier] + : authConstruct.resources.groups?.[roleIdentifier]?.role; + if (!role) { + throw new AmplifyUserError('InvalidResourceAccessConfigError', { + message: `No auth IAM role found for "${roleIdentifier}".`, + resolution: `If you are trying to configure UserPool group access, ensure that the group name is specified correctly.`, + }); + } + policy.attachToRole(role); + }, + }), + stack: Stack.of(authConstruct), + }; + if (!this.props.access) { + return authConstructMixin; + } + // props.access is the access callback defined by the customer + // here we inject the authAccessBuilder into the callback and run it + // this produces the access definition that will be used to create the auth access policies + const accessDefinition = this.props.access(this.authAccessBuilder); + + const ssmEnvironmentEntries = + ssmEnvironmentEntriesGenerator.generateSsmEnvironmentEntries({ + [`${this.name}_USERPOOL_ID`]: + authConstructMixin.resources.userPool.userPoolId, + }); + + const authPolicyArbiter = this.authAccessPolicyArbiterFactory.getInstance( + accessDefinition, + this.getInstanceProps, + ssmEnvironmentEntries, + new UserPoolAccessPolicyFactory(authConstruct.resources.userPool) + ); + + authPolicyArbiter.arbitratePolicies(); + + return authConstructMixin; + }; +} + +const roleNameIsAuthRoleName = (roleName: string): roleName is AuthRoleName => { + return ( + roleName === 'authenticatedUserIamRole' || + roleName === 'unauthenticatedUserIamRole' + ); +}; + +/** + * Provide references to existing auth resources. + */ +export const referenceAuth = ( + props: AmplifyReferenceAuthProps +): ConstructFactory => { + return new AmplifyReferenceAuthFactory( + props, + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors + new Error().stack + ); +}; diff --git a/packages/backend-auth/src/test-resources/sample_data.ts b/packages/backend-auth/src/test-resources/sample_data.ts new file mode 100644 index 00000000000..1ac099f65b8 --- /dev/null +++ b/packages/backend-auth/src/test-resources/sample_data.ts @@ -0,0 +1,448 @@ +import { IdentityPool as IdentityPoolType } from '@aws-sdk/client-cognito-identity'; +import { + GetUserPoolMfaConfigCommandOutput, + ListGroupsResponse, + ProviderDescription, + UserPoolClientType, + UserPoolType, +} from '@aws-sdk/client-cognito-identity-provider'; +import { ReferenceAuthInitializerProps } from '../lambda/reference_auth_initializer.js'; +/** + * Sample referenceAuth properties + */ +export const SampleInputProperties: ReferenceAuthInitializerProps = { + authRoleArn: 'arn:aws:iam::000000000000:role/service-role/ref-auth-role-1', + unauthRoleArn: 'arn:aws:iam::000000000000:role/service-role/ref-unauth-role1', + identityPoolId: 'us-east-1:sample-identity-pool-id', + userPoolClientId: 'sampleUserPoolClientId', + userPoolId: 'us-east-1_userpoolTest', + groups: { + ADMINS: 'arn:aws:iam::000000000000:role/sample-group-role', + }, + region: 'us-east-1', +}; +/** + * Sample response from describe user pool command + */ +export const UserPool: Readonly = { + Id: SampleInputProperties.userPoolId, + Name: 'ref-auth-userpool-1', + Policies: { + PasswordPolicy: { + MinimumLength: 10, + RequireUppercase: true, + RequireLowercase: true, + RequireNumbers: true, + RequireSymbols: false, + TemporaryPasswordValidityDays: 7, + }, + }, + DeletionProtection: 'ACTIVE', + LambdaConfig: {}, + SchemaAttributes: [ + { + Name: 'profile', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + Name: 'address', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + Name: 'birthdate', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '10', + MaxLength: '10', + }, + }, + { + Name: 'gender', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + Name: 'preferred_username', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + Name: 'updated_at', + AttributeDataType: 'Number', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + NumberAttributeConstraints: { + MinValue: '0', + }, + }, + { + Name: 'website', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + Name: 'picture', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + Name: 'identities', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: {}, + }, + { + Name: 'sub', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: false, + Required: true, + StringAttributeConstraints: { + MinLength: '1', + MaxLength: '2048', + }, + }, + { + Name: 'phone_number', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + Name: 'phone_number_verified', + AttributeDataType: 'Boolean', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + }, + { + Name: 'zoneinfo', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + // eslint-disable-next-line spellcheck/spell-checker + Name: 'custom:duplicateemail', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: {}, + }, + { + Name: 'locale', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + Name: 'email', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: true, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + Name: 'email_verified', + AttributeDataType: 'Boolean', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + }, + { + Name: 'given_name', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + Name: 'family_name', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + Name: 'middle_name', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + Name: 'name', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + { + Name: 'nickname', + AttributeDataType: 'String', + DeveloperOnlyAttribute: false, + Mutable: true, + Required: false, + StringAttributeConstraints: { + MinLength: '0', + MaxLength: '2048', + }, + }, + ], + AutoVerifiedAttributes: ['email'], + UsernameAttributes: ['email'], + VerificationMessageTemplate: { + DefaultEmailOption: 'CONFIRM_WITH_CODE', + }, + UserAttributeUpdateSettings: { + AttributesRequireVerificationBeforeUpdate: ['email'], + }, + MfaConfiguration: 'ON', + EstimatedNumberOfUsers: 0, + EmailConfiguration: { + EmailSendingAccount: 'COGNITO_DEFAULT', + }, + UserPoolTags: {}, + Domain: 'ref-auth-userpool-1', + AdminCreateUserConfig: { + AllowAdminCreateUserOnly: false, + UnusedAccountValidityDays: 7, + }, + UsernameConfiguration: { + CaseSensitive: false, + }, + Arn: `arn:aws:cognito-idp:us-east-1:000000000000:userpool/${SampleInputProperties.userPoolId}`, + AccountRecoverySetting: { + RecoveryMechanisms: [ + { + Priority: 1, + Name: 'verified_email', + }, + ], + }, +}; + +export const UserPoolGroups: Readonly = { + Groups: [ + { + GroupName: 'sample-group-name', + RoleArn: 'arn:aws:iam::000000000000:role/sample-group-role', + }, + ], +}; + +/** + * Sample data from get user pool mfa config + */ +export const MFAResponse: Readonly< + Omit +> = { + SoftwareTokenMfaConfiguration: { + Enabled: true, + }, + MfaConfiguration: 'ON', +}; + +/** + * Sample data from list identity providers + */ +export const IdentityProviders: Readonly = [ + { + ProviderName: 'Facebook', + ProviderType: 'Facebook', + }, + { + ProviderName: 'Google', + ProviderType: 'Google', + }, + { + ProviderName: 'LoginWithAmazon', + ProviderType: 'LoginWithAmazon', + }, +]; + +/** + * Sample data for describe identity pool + */ +export const IdentityPool: Readonly = { + IdentityPoolId: SampleInputProperties.identityPoolId, + IdentityPoolName: 'sample-identity-pool-name', + AllowUnauthenticatedIdentities: true, + AllowClassicFlow: false, + CognitoIdentityProviders: [ + { + ProviderName: `cognito-idp.us-east-1.amazonaws.com/${SampleInputProperties.userPoolId}`, + ClientId: SampleInputProperties.userPoolClientId, + ServerSideTokenCheck: false, + }, + ], + IdentityPoolTags: {}, +}; + +/** + * Sample data for get identity pool roles + */ +export const IdentityPoolRoles = { + IdentityPoolId: SampleInputProperties.identityPoolId, + Roles: { + authenticated: SampleInputProperties.authRoleArn, + unauthenticated: SampleInputProperties.unauthRoleArn, + }, +}; + +/** + * Sample data from describe user pool client + */ +export const UserPoolClient: Readonly = { + UserPoolId: SampleInputProperties.userPoolId, + ClientName: 'ref-auth-app-client-1', + ClientId: SampleInputProperties.userPoolClientId, + RefreshTokenValidity: 30, + AccessTokenValidity: 60, + IdTokenValidity: 60, + TokenValidityUnits: { + AccessToken: 'minutes', + IdToken: 'minutes', + RefreshToken: 'days', + }, + ReadAttributes: [ + 'address', + 'birthdate', + // eslint-disable-next-line spellcheck/spell-checker + 'custom:duplicateemail', + 'email', + 'email_verified', + 'family_name', + 'gender', + 'given_name', + 'locale', + 'middle_name', + 'name', + 'nickname', + 'phone_number', + 'phone_number_verified', + 'picture', + 'preferred_username', + 'profile', + 'updated_at', + 'website', + 'zoneinfo', + ], + WriteAttributes: [ + 'address', + 'birthdate', + // eslint-disable-next-line spellcheck/spell-checker + 'custom:duplicateemail', + 'email', + 'family_name', + 'gender', + 'given_name', + 'locale', + 'middle_name', + 'name', + 'nickname', + 'phone_number', + 'picture', + 'preferred_username', + 'profile', + 'updated_at', + 'website', + 'zoneinfo', + ], + ExplicitAuthFlows: ['ALLOW_REFRESH_TOKEN_AUTH', 'ALLOW_USER_SRP_AUTH'], + SupportedIdentityProviders: [ + 'COGNITO', + 'Facebook', + 'Google', + 'LoginWithAmazon', + ], + CallbackURLs: ['https://redirect.com', 'https://redirect2.com'], + LogoutURLs: ['https://anotherlogouturl.com', 'https://logouturl.com'], + AllowedOAuthFlows: ['code'], + AllowedOAuthScopes: ['email', 'openid', 'phone'], + AllowedOAuthFlowsUserPoolClient: true, + PreventUserExistenceErrors: 'ENABLED', + EnableTokenRevocation: true, + EnablePropagateAdditionalUserContextData: false, + AuthSessionValidity: 3, +}; diff --git a/packages/backend-auth/tsconfig.json b/packages/backend-auth/tsconfig.json index b98614a8127..42a487d8e77 100644 --- a/packages/backend-auth/tsconfig.json +++ b/packages/backend-auth/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "rootDir": "src", "outDir": "lib" }, "references": [ { "path": "../auth-construct" }, + { "path": "../backend-output-schemas" }, { "path": "../backend-output-storage" }, { "path": "../plugin-types" }, { "path": "../backend-platform-test-stubs" }, diff --git a/packages/backend-data/src/convert_authorization_modes.test.ts b/packages/backend-data/src/convert_authorization_modes.test.ts index f6854144491..f728c7bd72d 100644 --- a/packages/backend-data/src/convert_authorization_modes.test.ts +++ b/packages/backend-data/src/convert_authorization_modes.test.ts @@ -36,6 +36,7 @@ void describe('buildConstructFactoryProvidedAuthConfig', () => { userPool: 'ThisIsAUserPool', authenticatedUserIamRole: 'ThisIsAnAuthenticatedUserIamRole', unauthenticatedUserIamRole: 'ThisIsAnUnauthenticatedUserIamRole', + identityPoolId: 'us-fake-1:123123-123123', cfnResources: { cfnIdentityPool: { logicalId: 'IdentityPoolLogicalId', diff --git a/packages/backend-data/src/convert_authorization_modes.ts b/packages/backend-data/src/convert_authorization_modes.ts index fadfec4fbb5..02df9d4a044 100644 --- a/packages/backend-data/src/convert_authorization_modes.ts +++ b/packages/backend-data/src/convert_authorization_modes.ts @@ -20,6 +20,7 @@ import { import { AuthResources, ConstructFactoryGetInstanceProps, + ReferenceAuthResources, ResourceProvider, } from '@aws-amplify/plugin-types'; import { AmplifyUserError } from '@aws-amplify/platform-core'; @@ -38,14 +39,14 @@ export type ProvidedAuthConfig = { * Function instance provider which uses the */ export const buildConstructFactoryProvidedAuthConfig = ( - authResourceProvider: ResourceProvider | undefined + authResourceProvider: + | ResourceProvider + | undefined ): ProvidedAuthConfig | undefined => { if (!authResourceProvider) return; - return { userPool: authResourceProvider.resources.userPool, - identityPoolId: - authResourceProvider.resources.cfnResources.cfnIdentityPool.ref, + identityPoolId: authResourceProvider.resources.identityPoolId, authenticatedUserRole: authResourceProvider.resources.authenticatedUserIamRole, unauthenticatedUserRole: diff --git a/packages/backend-data/src/factory.test.ts b/packages/backend-data/src/factory.test.ts index c4cc45b9adb..51e2aabf458 100644 --- a/packages/backend-data/src/factory.test.ts +++ b/packages/backend-data/src/factory.test.ts @@ -85,6 +85,7 @@ const createConstructContainerWithUserPoolAuthRegistered = ( authenticatedUserIamRole: new Role(stack, 'testAuthRole', { assumedBy: new ServicePrincipal('test.amazon.com'), }), + identityPoolId: 'identityPoolId', cfnResources: { cfnUserPool: new CfnUserPool(stack, 'CfnUserPool', {}), cfnUserPoolClient: new CfnUserPoolClient(stack, 'CfnUserPoolClient', { diff --git a/packages/backend-data/src/factory.ts b/packages/backend-data/src/factory.ts index ebe3d1b0835..578d0cbcbc7 100644 --- a/packages/backend-data/src/factory.ts +++ b/packages/backend-data/src/factory.ts @@ -7,6 +7,7 @@ import { ConstructFactory, ConstructFactoryGetInstanceProps, GenerateContainerEntryProps, + ReferenceAuthResources, ResourceProvider, } from '@aws-amplify/plugin-types'; import { @@ -97,9 +98,9 @@ export class DataFactory implements ConstructFactory { this.props, buildConstructFactoryProvidedAuthConfig( props.constructContainer - .getConstructFactory>( - 'AuthResources' - ) + .getConstructFactory< + ResourceProvider + >('AuthResources') ?.getInstance(props) ), props, diff --git a/packages/backend/API.md b/packages/backend/API.md index ba53fec1756..c9d790427f0 100644 --- a/packages/backend/API.md +++ b/packages/backend/API.md @@ -26,6 +26,7 @@ import { defineStorage } from '@aws-amplify/backend-storage'; import { FunctionResources } from '@aws-amplify/plugin-types'; import { GenerateContainerEntryProps } from '@aws-amplify/plugin-types'; import { ImportPathVerifier } from '@aws-amplify/plugin-types'; +import { referenceAuth } from '@aws-amplify/backend-auth'; import { ResourceAccessAcceptorFactory } from '@aws-amplify/plugin-types'; import { ResourceProvider } from '@aws-amplify/plugin-types'; import { SsmEnvironmentEntriesGenerator } from '@aws-amplify/plugin-types'; @@ -90,6 +91,8 @@ export { GenerateContainerEntryProps } export { ImportPathVerifier } +export { referenceAuth } + export { ResourceProvider } // @public diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 46adc6515e8..5150f93a8e2 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -17,7 +17,7 @@ export { defineData } from '@aws-amplify/backend-data'; export { type ClientSchema, a } from '@aws-amplify/data-schema'; // auth -export { defineAuth } from '@aws-amplify/backend-auth'; +export { defineAuth, referenceAuth } from '@aws-amplify/backend-auth'; // storage export { defineStorage } from '@aws-amplify/backend-storage'; diff --git a/packages/integration-tests/src/resource-creation/auth_resource_creator.ts b/packages/integration-tests/src/resource-creation/auth_resource_creator.ts new file mode 100644 index 00000000000..da405b6bbe2 --- /dev/null +++ b/packages/integration-tests/src/resource-creation/auth_resource_creator.ts @@ -0,0 +1,372 @@ +import { + CognitoIdentityProviderClient, + CreateGroupCommand, + CreateGroupCommandInput, + CreateIdentityProviderCommand, + CreateIdentityProviderCommandInput, + CreateUserPoolClientCommand, + CreateUserPoolClientCommandInput, + CreateUserPoolCommand, + CreateUserPoolCommandInput, + CreateUserPoolDomainCommand, + CreateUserPoolDomainCommandInput, + DeleteGroupCommand, + DeleteIdentityProviderCommand, + DeleteUserPoolClientCommand, + DeleteUserPoolCommand, + DeleteUserPoolDomainCommand, +} from '@aws-sdk/client-cognito-identity-provider'; +import { + CreateRoleCommand, + CreateRoleCommandInput, + DeleteRoleCommand, + IAMClient, +} from '@aws-sdk/client-iam'; +import { + CognitoIdentityClient, + CreateIdentityPoolCommand, + CreateIdentityPoolCommandInput, + DeleteIdentityPoolCommand, + SetIdentityPoolRolesCommand, +} from '@aws-sdk/client-cognito-identity'; +import { shortUuid } from '../short_uuid.js'; +import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; +const TEST_AMPLIFY_RESOURCE_PREFIX = 'amplify-'; + +type CleanupTask = { + run: () => Promise; + arn?: string | undefined; + id?: string | undefined; +}; +/** + * Provides a way to create auth resources using aws sdk + */ +export class AuthResourceCreator { + private cleanup: CleanupTask[] = []; + + /** + * Setup a new auth resource creator + * @param cognitoIdentityProviderClient client + * @param cognitoIdentityClient client + * @param iamClient client + */ + constructor( + private cognitoIdentityProviderClient: CognitoIdentityProviderClient = new CognitoIdentityProviderClient( + e2eToolingClientConfig + ), + private cognitoIdentityClient: CognitoIdentityClient = new CognitoIdentityClient( + e2eToolingClientConfig + ), + private iamClient: IAMClient = new IAMClient(e2eToolingClientConfig), + private createResourceNameSuffix: () => string = shortUuid + ) {} + + cleanupResources = async () => { + // delete in reverse order + const list = this.cleanup.map((t) => t.arn ?? t.id); + console.log( + `Attempting to delete a total of ${this.cleanup.length} resources` + ); + console.log('Resource descriptions/ARNs/IDs:', list); + const failedTasks: CleanupTask[] = []; + for (let i = this.cleanup.length - 1; i >= 0; i--) { + const task = this.cleanup[i]; + try { + await task.run(); + console.log(`Deleted: ${task.arn ?? task.id}`); + } catch (e) { + failedTasks.push(task); + console.error(`Failed to delete resource: ${task.arn ?? task.id}`, e); + } + } + console.error( + 'Failed tasks:', + failedTasks.map((t) => t.arn ?? t.id) + ); + }; + + createUserPoolBase = async (props: CreateUserPoolCommandInput) => { + const result = await this.cognitoIdentityProviderClient.send( + new CreateUserPoolCommand({ + ...props, + PoolName: `${TEST_AMPLIFY_RESOURCE_PREFIX}${ + props.PoolName + }-${this.createResourceNameSuffix()}`, + }) + ); + const userPool = result.UserPool; + if (!userPool) { + throw new Error('Failed to create user pool.'); + } + this.cleanup.push({ + run: async () => { + await this.cognitoIdentityProviderClient.send( + new DeleteUserPoolCommand({ UserPoolId: userPool.Id }) + ); + }, + arn: userPool.Arn, + }); + return userPool; + }; + + createUserPoolClientBase = async ( + props: CreateUserPoolClientCommandInput + ) => { + const result = await this.cognitoIdentityProviderClient.send( + new CreateUserPoolClientCommand({ + ...props, + ClientName: `${TEST_AMPLIFY_RESOURCE_PREFIX}${ + props.ClientName + }-${this.createResourceNameSuffix()}`, + }) + ); + const client = result.UserPoolClient; + if (!client) { + throw new Error('Failed to create user pool client.'); + } + this.cleanup.push({ + run: async () => { + await this.cognitoIdentityProviderClient.send( + new DeleteUserPoolClientCommand({ + ClientId: client.ClientId, + UserPoolId: client.UserPoolId, + }) + ); + }, + id: `UserPoolClientId: ${client.ClientId}`, + }); + return client; + }; + + createUserPoolDomainBase = async ( + props: CreateUserPoolDomainCommandInput + ) => { + const domain = `${TEST_AMPLIFY_RESOURCE_PREFIX}${ + props.Domain + }-${this.createResourceNameSuffix()}`; + await this.cognitoIdentityProviderClient.send( + new CreateUserPoolDomainCommand({ + ...props, + Domain: domain, + }) + ); + // if it didn't throw, domain was created. + this.cleanup.push({ + run: async () => { + await this.cognitoIdentityProviderClient.send( + new DeleteUserPoolDomainCommand({ + Domain: domain, + UserPoolId: props.UserPoolId, + }) + ); + }, + id: `Domain: ${domain}`, + }); + return domain; + }; + + createIdentityProviderBase = async ( + props: CreateIdentityProviderCommandInput + ) => { + const result = await this.cognitoIdentityProviderClient.send( + new CreateIdentityProviderCommand({ + ...props, + }) + ); + const provider = result.IdentityProvider; + if (!provider) { + throw new Error( + `An error occurred while creating the identity provider ${props.ProviderName}` + ); + } + this.cleanup.push({ + run: async () => { + await this.cognitoIdentityProviderClient.send( + new DeleteIdentityProviderCommand({ + UserPoolId: props.UserPoolId, + ProviderName: provider.ProviderName, + }) + ); + }, + id: `Provider: ${provider.ProviderName}`, + }); + return provider; + }; + + createIdentityPoolBase = async (props: CreateIdentityPoolCommandInput) => { + const identityPoolResponse = await this.cognitoIdentityClient.send( + new CreateIdentityPoolCommand({ + ...props, + IdentityPoolName: `${TEST_AMPLIFY_RESOURCE_PREFIX}${ + props.IdentityPoolName + }-${this.createResourceNameSuffix()}`, + }) + ); + const identityPoolId = identityPoolResponse.IdentityPoolId; + if (!identityPoolId) { + throw new Error('An error occurred while creating the identity pool'); + } + this.cleanup.push({ + run: async () => { + await this.cognitoIdentityClient.send( + new DeleteIdentityPoolCommand({ IdentityPoolId: identityPoolId }) + ); + }, + id: `IdentityPool: ${identityPoolResponse.IdentityPoolId}`, + }); + return { + ...identityPoolResponse, + // the line below ensures that the type engine sees IdentityPoolId as string, not string | undefined. + IdentityPoolId: identityPoolId, + }; + }; + + createRoleBase = async (props: CreateRoleCommandInput) => { + const result = await this.iamClient.send( + new CreateRoleCommand({ + ...props, + RoleName: `${TEST_AMPLIFY_RESOURCE_PREFIX}${ + props.RoleName + }-${this.createResourceNameSuffix()}`, + }) + ); + const role = result.Role; + if (!role) { + throw new Error( + `An error occurred while creating the role: ${props.RoleName}` + ); + } + this.cleanup.push({ + run: async () => { + await this.iamClient.send( + new DeleteRoleCommand({ RoleName: role.RoleName }) + ); + }, + arn: role.Arn, + }); + return role; + }; + + createUserPoolGroupBase = async (props: CreateGroupCommandInput) => { + const result = await this.cognitoIdentityProviderClient.send( + new CreateGroupCommand({ + ...props, + GroupName: `${TEST_AMPLIFY_RESOURCE_PREFIX}${ + props.GroupName + }-${this.createResourceNameSuffix()}`, + }) + ); + const group = result.Group; + if (!group || !group.GroupName) { + throw new Error(`Error creating group with name: ${props.GroupName}`); + } + this.cleanup.push({ + run: async () => { + await this.cognitoIdentityProviderClient.send( + new DeleteGroupCommand({ + UserPoolId: props.UserPoolId, + GroupName: group.GroupName, + }) + ); + }, + id: `Group: ${group.GroupName}`, + }); + return group; + }; + + setupUserPoolGroup = async ( + groupName: string, + userPoolId: string, + identityPoolId: string + ) => { + const groupRole = await this.createRoleBase({ + RoleName: 'ref-auth-group-role', + AssumeRolePolicyDocument: this.getIdentityPoolAssumeRolePolicyDocument( + identityPoolId, + 'authenticated' + ), + }); + const group = await this.createUserPoolGroupBase({ + GroupName: groupName, + UserPoolId: userPoolId, + RoleArn: groupRole.Arn, + }); + return group; + }; + + /** + * Setup standard auth and unauth roles for an identity pool + * @param userPoolId user pool id + * @param userPoolClientId user pool client id + * @param identityPoolId identity pool id + * @returns auth and unauth roles + */ + setupIdentityPoolRoles = async ( + userPoolId: string, + userPoolClientId: string, + identityPoolId: string + ) => { + const authRole = await this.createRoleBase({ + RoleName: `ref-auth-role`, + AssumeRolePolicyDocument: this.getIdentityPoolAssumeRolePolicyDocument( + identityPoolId, + 'authenticated' + ), + }); + const unauthRole = await this.createRoleBase({ + RoleName: `ref-unauth-role`, + AssumeRolePolicyDocument: this.getIdentityPoolAssumeRolePolicyDocument( + identityPoolId, + 'unauthenticated' + ), + }); + const region = await this.cognitoIdentityClient.config.region(); + await this.cognitoIdentityClient.send( + new SetIdentityPoolRolesCommand({ + IdentityPoolId: identityPoolId, + Roles: { + unauthenticated: unauthRole.Arn!, + authenticated: authRole.Arn!, + }, + RoleMappings: { + [`cognito-idp.${region}.amazonaws.com/${userPoolId}:${userPoolClientId}`]: + { + Type: 'Token', + AmbiguousRoleResolution: 'AuthenticatedRole', + }, + }, + }) + ); + + return { + authRole, + unauthRole, + }; + }; + + private getIdentityPoolAssumeRolePolicyDocument = ( + identityPoolId: string, + roleType: 'authenticated' | 'unauthenticated' + ) => { + return `{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "cognito-identity.amazonaws.com" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + "cognito-identity.amazonaws.com:aud": "${identityPoolId}" + }, + "ForAnyValue:StringLike": { + "cognito-identity.amazonaws.com:amr": "${roleType}" + } + } + } + ] + }`; + }; +} diff --git a/packages/integration-tests/src/test-e2e/deployment/reference_auth_project.deployment.test.ts b/packages/integration-tests/src/test-e2e/deployment/reference_auth_project.deployment.test.ts new file mode 100644 index 00000000000..2a30ffa54e3 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/deployment/reference_auth_project.deployment.test.ts @@ -0,0 +1,4 @@ +import { ReferenceAuthTestProjectCreator } from '../../test-project-setup/reference_auth_project.js'; +import { defineDeploymentTest } from './deployment.test.template.js'; + +defineDeploymentTest(new ReferenceAuthTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/sandbox/reference_auth_project.sandbox.test.ts b/packages/integration-tests/src/test-e2e/sandbox/reference_auth_project.sandbox.test.ts new file mode 100644 index 00000000000..c7c19bdcf02 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/sandbox/reference_auth_project.sandbox.test.ts @@ -0,0 +1,4 @@ +import { ReferenceAuthTestProjectCreator } from '../../test-project-setup/reference_auth_project.js'; +import { defineSandboxTest } from './sandbox.test.template.js'; + +defineSandboxTest(new ReferenceAuthTestProjectCreator()); diff --git a/packages/integration-tests/src/test-project-setup/reference_auth_project.ts b/packages/integration-tests/src/test-project-setup/reference_auth_project.ts new file mode 100644 index 00000000000..66c893c9c16 --- /dev/null +++ b/packages/integration-tests/src/test-project-setup/reference_auth_project.ts @@ -0,0 +1,340 @@ +import { TestProjectBase } from './test_project_base.js'; +import fsp from 'fs/promises'; +import { createEmptyAmplifyProject } from './create_empty_amplify_project.js'; +import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; +import { TestProjectCreator } from './test_project_creator.js'; +import { AmplifyClient } from '@aws-sdk/client-amplify'; +import { AuthResourceCreator } from '../resource-creation/auth_resource_creator.js'; +import { CognitoIdentityProviderClient } from '@aws-sdk/client-cognito-identity-provider'; +import { CognitoIdentityClient } from '@aws-sdk/client-cognito-identity'; +import { IAMClient } from '@aws-sdk/client-iam'; +import { BackendIdentifier } from '@aws-amplify/plugin-types'; +import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; + +/** + * Creates a reference auth project + */ +export class ReferenceAuthTestProjectCreator implements TestProjectCreator { + readonly name = 'reference-auth'; + + /** + * Creates project creator. + */ + constructor( + private readonly cfnClient: CloudFormationClient = new CloudFormationClient( + e2eToolingClientConfig + ), + private readonly amplifyClient: AmplifyClient = new AmplifyClient( + e2eToolingClientConfig + ), + private readonly cognitoIdentityProviderClient: CognitoIdentityProviderClient = new CognitoIdentityProviderClient( + e2eToolingClientConfig + ), + private readonly cognitoIdentityClient: CognitoIdentityClient = new CognitoIdentityClient( + e2eToolingClientConfig + ), + private readonly iamClient: IAMClient = new IAMClient( + e2eToolingClientConfig + ) + ) {} + + createProject = async (e2eProjectDir: string): Promise => { + const { projectName, projectRoot, projectAmplifyDir } = + await createEmptyAmplifyProject(this.name, e2eProjectDir); + + const project = new ReferenceAuthTestProject( + projectName, + projectRoot, + projectAmplifyDir, + this.cfnClient, + this.amplifyClient, + this.cognitoIdentityProviderClient, + this.cognitoIdentityClient, + this.iamClient + ); + + await fsp.cp( + project.sourceProjectAmplifyDirURL, + project.projectAmplifyDirPath, + { + recursive: true, + } + ); + + // generate resources + const { + userPool, + userPoolClient, + identityPool, + authRole, + unauthRole, + adminGroup, + } = await project.setupTestResources(); + // copy generated resource ids into project's auth/resource.ts file + const authResourceFilePath = `${project.projectAmplifyDirPath}/auth/resource.ts`; + await fsp.writeFile( + authResourceFilePath, + `import { referenceAuth } from '@aws-amplify/backend'; + import { addUserToGroup } from "../data/add-user-to-group/resource.js"; + + export const auth = referenceAuth({ + identityPoolId: "${identityPool.IdentityPoolId}", + authRoleArn: "${authRole.Arn}", + unauthRoleArn: "${unauthRole.Arn}", + userPoolId: "${userPool.Id}", + userPoolClientId: "${userPoolClient.ClientId}", + groups: { + "ADMINS": '${adminGroup.RoleArn}', + }, + access: (allow) => [ + allow.resource(addUserToGroup).to(["addUserToGroup"]) + ], + })` + ); + return project; + }; +} + +/** + * The minimal test with typescript idioms. + */ +class ReferenceAuthTestProject extends TestProjectBase { + readonly sourceProjectDirPath = '../../src/test-projects/reference-auth'; + + readonly sourceProjectAmplifyDirSuffix = `${this.sourceProjectDirPath}/amplify`; + + readonly sourceProjectAmplifyDirURL: URL = new URL( + this.sourceProjectAmplifyDirSuffix, + import.meta.url + ); + + authResourceCreator: AuthResourceCreator; + + /** + * Create a test project instance. + */ + constructor( + name: string, + projectDirPath: string, + projectAmplifyDirPath: string, + cfnClient: CloudFormationClient, + amplifyClient: AmplifyClient, + cognitoIdentityProviderClient: CognitoIdentityProviderClient, + private cognitoIdentityClient: CognitoIdentityClient, + iamClient: IAMClient + ) { + super( + name, + projectDirPath, + projectAmplifyDirPath, + cfnClient, + amplifyClient + ); + this.authResourceCreator = new AuthResourceCreator( + cognitoIdentityProviderClient, + cognitoIdentityClient, + iamClient + ); + } + + setupTestResources = async () => { + try { + const userPool = await this.authResourceCreator.createUserPoolBase({ + PoolName: `RefUserPool`, + AccountRecoverySetting: { + RecoveryMechanisms: [ + { + Name: 'verified_email', + Priority: 1, + }, + ], + }, + AdminCreateUserConfig: { + AllowAdminCreateUserOnly: false, + }, + AutoVerifiedAttributes: ['email'], + UserAttributeUpdateSettings: { + AttributesRequireVerificationBeforeUpdate: ['email'], + }, + EmailConfiguration: { + EmailSendingAccount: 'COGNITO_DEFAULT', + }, + Schema: [ + { + Name: 'email', + Required: true, + }, + ], + Policies: { + PasswordPolicy: { + MinimumLength: 8, + RequireUppercase: true, + RequireLowercase: true, + RequireNumbers: true, + RequireSymbols: true, + TemporaryPasswordValidityDays: 7, + }, + }, + UsernameAttributes: ['email'], + UsernameConfiguration: { + CaseSensitive: false, + }, + MfaConfiguration: 'OFF', + DeletionProtection: 'INACTIVE', + }); + + const domain = await this.authResourceCreator.createUserPoolDomainBase({ + UserPoolId: userPool.Id, + Domain: `ref-auth`, + }); + + await this.authResourceCreator.createIdentityProviderBase({ + UserPoolId: userPool.Id, + ProviderType: 'Facebook', + ProviderDetails: { + client_id: 'clientId', + client_secret: 'clientSecret', + authorize_scopes: 'openid,email', + api_version: 'v17.0', + }, + AttributeMapping: { + email: 'email', + }, + ProviderName: 'Facebook', + }); + + await this.authResourceCreator.createIdentityProviderBase({ + UserPoolId: userPool.Id, + ProviderType: 'Google', + ProviderDetails: { + client_id: 'clientId', + client_secret: 'clientSecret', + authorize_scopes: 'openid,email', + }, + AttributeMapping: { + email: 'email', + }, + ProviderName: 'Google', + }); + + const userPoolClient = + await this.authResourceCreator.createUserPoolClientBase({ + ClientName: `ref-auth-client`, + UserPoolId: userPool.Id, + ExplicitAuthFlows: [ + 'ALLOW_REFRESH_TOKEN_AUTH', + 'ALLOW_USER_SRP_AUTH', + ], + AuthSessionValidity: 3, + RefreshTokenValidity: 30, + AccessTokenValidity: 60, + IdTokenValidity: 60, + TokenValidityUnits: { + RefreshToken: 'days', + AccessToken: 'minutes', + IdToken: 'minutes', + }, + EnableTokenRevocation: true, + PreventUserExistenceErrors: 'ENABLED', + AllowedOAuthFlows: ['code'], + AllowedOAuthScopes: ['openid', 'phone', 'email'], + SupportedIdentityProviders: ['COGNITO', 'Facebook', 'Google'], + CallbackURLs: ['https://callback.com'], + LogoutURLs: ['https://logout.com'], + AllowedOAuthFlowsUserPoolClient: true, + GenerateSecret: false, + ReadAttributes: [ + 'address', + 'birthdate', + 'email', + 'email_verified', + 'family_name', + 'gender', + 'given_name', + 'locale', + 'middle_name', + 'name', + 'nickname', + 'phone_number', + 'phone_number_verified', + 'picture', + 'preferred_username', + 'profile', + 'updated_at', + 'website', + 'zoneinfo', + ], + WriteAttributes: [ + 'address', + 'birthdate', + 'email', + 'family_name', + 'gender', + 'given_name', + 'locale', + 'middle_name', + 'name', + 'nickname', + 'phone_number', + 'picture', + 'preferred_username', + 'profile', + 'updated_at', + 'website', + 'zoneinfo', + ], + }); + + const region = await this.cognitoIdentityClient.config.region(); + const identityPool = + await this.authResourceCreator.createIdentityPoolBase({ + AllowUnauthenticatedIdentities: true, + IdentityPoolName: `ref-auth-ip`, + AllowClassicFlow: false, + CognitoIdentityProviders: [ + { + ClientId: userPoolClient.ClientId, + ProviderName: `cognito-idp.${region}.amazonaws.com/${userPool.Id}`, + ServerSideTokenCheck: false, + }, + ], + SupportedLoginProviders: { + 'graph.facebook.com': 'clientId', + 'accounts.google.com': 'clientId', + }, + }); + + const roles = await this.authResourceCreator.setupIdentityPoolRoles( + userPool.Id!, + userPoolClient.ClientId!, + identityPool.IdentityPoolId + ); + + const adminGroup = await this.authResourceCreator.setupUserPoolGroup( + 'ADMINS', + userPool.Id!, + identityPool.IdentityPoolId + ); + return { + userPool, + userPoolClient, + domain, + identityPool, + authRole: roles.authRole, + unauthRole: roles.unauthRole, + adminGroup, + }; + } catch (e) { + await this.authResourceCreator.cleanupResources(); + throw e; + } + }; + + /** + * @inheritdoc + */ + override async tearDown(backendIdentifier: BackendIdentifier) { + await super.tearDown(backendIdentifier, true); + await this.authResourceCreator.cleanupResources(); + } +} diff --git a/packages/integration-tests/src/test-project-setup/test_project_base.ts b/packages/integration-tests/src/test-project-setup/test_project_base.ts index 1b1650a5b38..706d68114c6 100644 --- a/packages/integration-tests/src/test-project-setup/test_project_base.ts +++ b/packages/integration-tests/src/test-project-setup/test_project_base.ts @@ -14,7 +14,9 @@ import { import { CloudFormationClient, + CloudFormationServiceException, DeleteStackCommand, + DescribeStacksCommand, } from '@aws-sdk/client-cloudformation'; import fsp from 'fs/promises'; import assert from 'node:assert'; @@ -100,19 +102,80 @@ export abstract class TestProjectBase { /** * Tear down the project. */ - async tearDown(backendIdentifier: BackendIdentifier) { + async tearDown( + backendIdentifier: BackendIdentifier, + waitForStackDeletion: boolean = false + ) { if (backendIdentifier.type === 'sandbox') { await ampxCli(['sandbox', 'delete'], this.projectDirPath) .do(confirmDeleteSandbox()) .run(); } else { + const stackName = + BackendIdentifierConversions.toStackName(backendIdentifier); await this.cfnClient.send( new DeleteStackCommand({ - StackName: - BackendIdentifierConversions.toStackName(backendIdentifier), + StackName: stackName, }) ); + if (waitForStackDeletion) { + await this.waitForStackDeletion(stackName); + } + } + } + + /** + * Wait for a stack to be deleted, returns true if deleted within allotted time. + * @param stackName name of the stack + * @returns true if delete completes within allotted time (3 minutes) + */ + async waitForStackDeletion( + stackName: string, + timeoutInMS: number = 3 * 60 * 1000 + ): Promise { + let attempts = 0; + let totalTimeWaitedMs = 0; + const maxIntervalMs = 32 * 1000; + while (totalTimeWaitedMs < timeoutInMS) { + attempts++; + const intervalMs = Math.min(Math.pow(2, attempts) * 1000, maxIntervalMs); + console.log(`waiting: ${intervalMs} milliseconds`); + await new Promise((resolve) => setTimeout(resolve, intervalMs)); + totalTimeWaitedMs += intervalMs; + try { + const status = await this.cfnClient.send( + new DescribeStacksCommand({ + StackName: stackName, + }) + ); + console.log( + JSON.stringify(status.Stacks?.map((s) => s.StackName) ?? []) + ); + if (!status.Stacks || status.Stacks.length == 0) { + console.log(`Stack ${stackName} was deleted successfully.`); + return true; + } + } catch (e) { + if ( + e instanceof CloudFormationServiceException && + e.message.includes('does not exist') + ) { + console.log(`Stack ${stackName} was deleted successfully.`); + return true; + } + console.error( + `Could not describe stack ${stackName} while waiting for deletion.`, + e + ); + throw e; + } } + console.error( + `Stack ${stackName} did not delete within ${ + timeoutInMS / 1000 + } seconds, continuing.` + ); + return false; } /** diff --git a/packages/integration-tests/src/test-projects/reference-auth/amplify/auth/resource.ts b/packages/integration-tests/src/test-projects/reference-auth/amplify/auth/resource.ts new file mode 100644 index 00000000000..bb04424328f --- /dev/null +++ b/packages/integration-tests/src/test-projects/reference-auth/amplify/auth/resource.ts @@ -0,0 +1,14 @@ +import { referenceAuth } from '@aws-amplify/backend'; +import { addUserToGroup } from '../data/add-user-to-group/resource.js'; + +export const auth = referenceAuth({ + identityPoolId: '', + authRoleArn: '', + unauthRoleArn: '', + userPoolId: '', + userPoolClientId: '', + groups: { + ADMINS: '', + }, + access: (allow) => [allow.resource(addUserToGroup).to(['addUserToGroup'])], +}); diff --git a/packages/integration-tests/src/test-projects/reference-auth/amplify/backend.ts b/packages/integration-tests/src/test-projects/reference-auth/amplify/backend.ts new file mode 100644 index 00000000000..8aac23b5436 --- /dev/null +++ b/packages/integration-tests/src/test-projects/reference-auth/amplify/backend.ts @@ -0,0 +1,10 @@ +import { defineBackend } from '@aws-amplify/backend'; +import { auth } from './auth/resource.js'; +import { data } from './data/resource.js'; +import { storage } from './storage/resource.js'; + +defineBackend({ + auth, + data, + storage, +}); diff --git a/packages/integration-tests/src/test-projects/reference-auth/amplify/data/add-user-to-group/handler.ts b/packages/integration-tests/src/test-projects/reference-auth/amplify/data/add-user-to-group/handler.ts new file mode 100644 index 00000000000..48a0db7cb61 --- /dev/null +++ b/packages/integration-tests/src/test-projects/reference-auth/amplify/data/add-user-to-group/handler.ts @@ -0,0 +1,3 @@ +export const handler = async (event: any) => { + return 'Hello world!'; +}; diff --git a/packages/integration-tests/src/test-projects/reference-auth/amplify/data/add-user-to-group/resource.ts b/packages/integration-tests/src/test-projects/reference-auth/amplify/data/add-user-to-group/resource.ts new file mode 100644 index 00000000000..dd1d930b074 --- /dev/null +++ b/packages/integration-tests/src/test-projects/reference-auth/amplify/data/add-user-to-group/resource.ts @@ -0,0 +1,5 @@ +import { defineFunction } from '@aws-amplify/backend'; + +export const addUserToGroup = defineFunction({ + name: 'add-user-to-group', +}); diff --git a/packages/integration-tests/src/test-projects/reference-auth/amplify/data/resource.ts b/packages/integration-tests/src/test-projects/reference-auth/amplify/data/resource.ts new file mode 100644 index 00000000000..d6842ab5d0f --- /dev/null +++ b/packages/integration-tests/src/test-projects/reference-auth/amplify/data/resource.ts @@ -0,0 +1,24 @@ +import { type ClientSchema, a, defineData } from '@aws-amplify/backend'; +import { addUserToGroup } from './add-user-to-group/resource.js'; + +const schema = a.schema({ + Todo: a + .model({ + name: a.string(), + description: a.string(), + }) + .authorization((allow) => allow.group('ADMINS')), + addUserToGroup: a + .mutation() + .arguments({ + userId: a.string().required(), + groupName: a.string().required(), + }) + .authorization((allow) => [allow.group('ADMINS')]) + .handler(a.handler.function(addUserToGroup)) + .returns(a.json()), +}) as never; + +export type Schema = ClientSchema; + +export const data = defineData({ schema }); diff --git a/packages/integration-tests/src/test-projects/reference-auth/amplify/storage/resource.ts b/packages/integration-tests/src/test-projects/reference-auth/amplify/storage/resource.ts new file mode 100644 index 00000000000..9404344b2bf --- /dev/null +++ b/packages/integration-tests/src/test-projects/reference-auth/amplify/storage/resource.ts @@ -0,0 +1,15 @@ +import { defineStorage } from '@aws-amplify/backend'; +export const storage = defineStorage({ + name: 'amplifyTeamDrive', + access: (allow) => ({ + 'profile-pictures/{entity_id}/*': [ + allow.guest.to(['read']), + allow.groups(['ADMINS']).to(['read']), + allow.entity('identity').to(['read', 'write', 'delete']), + ], + 'picture-submissions/*': [ + allow.authenticated.to(['read', 'write']), + allow.guest.to(['read', 'write']), + ], + }), +}); diff --git a/packages/integration-tests/src/test-projects/reference-auth/test-types/env/add-user-to-group.ts b/packages/integration-tests/src/test-projects/reference-auth/test-types/env/add-user-to-group.ts new file mode 100644 index 00000000000..f58ac10994f --- /dev/null +++ b/packages/integration-tests/src/test-projects/reference-auth/test-types/env/add-user-to-group.ts @@ -0,0 +1,10 @@ +export const env = process.env as { + TEST_NAME_BUCKET_NAME: string; + AWS_REGION: string; + AWS_ACCESS_KEY_ID: string; + AWS_SECRET_ACCESS_KEY: string; + AWS_SESSION_TOKEN: string; + TEST_SECRET: string; + TEST_SHARED_SECRET: string; + AMPLIFY_AUTH_USERPOOL_ID: string; +}; diff --git a/packages/plugin-types/API.md b/packages/plugin-types/API.md index 97bf48886ae..5633bd63c65 100644 --- a/packages/plugin-types/API.md +++ b/packages/plugin-types/API.md @@ -43,6 +43,7 @@ export type AuthResources = { userPoolClient: IUserPoolClient; authenticatedUserIamRole: IRole; unauthenticatedUserIamRole: IRole; + identityPoolId: string; cfnResources: AuthCfnResources; groups: { [groupName: string]: { @@ -192,6 +193,20 @@ export type PackageManagerController = { // @public (undocumented) export type ProjectName = string; +// @public +export type ReferenceAuthResources = { + userPool: IUserPool; + userPoolClient: IUserPoolClient; + authenticatedUserIamRole: IRole; + unauthenticatedUserIamRole: IRole; + identityPoolId: string; + groups: { + [groupName: string]: { + role: IRole; + }; + }; +}; + // @public (undocumented) export type ResolvePathResult = { branchSecretPath: string; diff --git a/packages/plugin-types/src/auth_resources.ts b/packages/plugin-types/src/auth_resources.ts index 3b571a497c6..0112e06a317 100644 --- a/packages/plugin-types/src/auth_resources.ts +++ b/packages/plugin-types/src/auth_resources.ts @@ -51,6 +51,10 @@ export type AuthResources = { * The generated unauth role. */ unauthenticatedUserIamRole: IRole; + /** + * Identity pool Id + */ + identityPoolId: string; /** * L1 Cfn Resources, for when dipping down a level of abstraction is desirable. */ @@ -72,6 +76,43 @@ export type AuthResources = { }; }; +/** + * Reference auth resources + */ +export type ReferenceAuthResources = { + /** + * The referenced UserPool L2 Resource. + */ + userPool: IUserPool; + /** + * The referenced UserPoolClient L2 Resource. + */ + userPoolClient: IUserPoolClient; + /** + * The referenced auth role. + */ + authenticatedUserIamRole: IRole; + /** + * The referenced unauth role. + */ + unauthenticatedUserIamRole: IRole; + /** + * Identity pool Id + */ + identityPoolId: string; + /** + * A map of existing group names and their associated group role. + */ + groups: { + [groupName: string]: { + /** + * The generated Role for this group + */ + role: IRole; + }; + }; +}; + export type AuthRoleName = keyof Pick< AuthResources, 'authenticatedUserIamRole' | 'unauthenticatedUserIamRole' From 59cb4a398d57089e7d4cebe9c17a2c4a4c1287f0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:48:51 -0800 Subject: [PATCH 100/199] Version Packages (#2205) Co-authored-by: github-actions[bot] --- .changeset/cool-points-kneel.md | 5 ----- .changeset/cool-zoos-enjoy.md | 2 -- .changeset/eleven-numbers-hide.md | 5 ----- .changeset/good-pugs-rescue.md | 9 --------- .changeset/nine-toes-dress.md | 2 -- .changeset/orange-days-look.md | 2 -- .changeset/short-dryers-tell.md | 5 ----- .changeset/spicy-rules-speak.md | 2 -- .changeset/violet-tools-clap.md | 7 ------- packages/auth-construct/CHANGELOG.md | 11 +++++++++++ packages/auth-construct/package.json | 4 ++-- packages/backend-auth/CHANGELOG.md | 12 ++++++++++++ packages/backend-auth/package.json | 6 +++--- packages/backend-data/CHANGELOG.md | 11 +++++++++++ packages/backend-data/package.json | 4 ++-- packages/backend-deployer/CHANGELOG.md | 9 +++++++++ packages/backend-deployer/package.json | 4 ++-- packages/backend-function/CHANGELOG.md | 8 ++++++++ packages/backend-function/package.json | 4 ++-- packages/backend/CHANGELOG.md | 16 ++++++++++++++++ packages/backend/package.json | 10 +++++----- packages/cli/CHANGELOG.md | 13 +++++++++++++ packages/cli/package.json | 8 ++++---- packages/model-generator/CHANGELOG.md | 8 ++++++++ packages/model-generator/package.json | 4 ++-- packages/plugin-types/CHANGELOG.md | 6 ++++++ packages/plugin-types/package.json | 2 +- 27 files changed, 117 insertions(+), 62 deletions(-) delete mode 100644 .changeset/cool-points-kneel.md delete mode 100644 .changeset/cool-zoos-enjoy.md delete mode 100644 .changeset/eleven-numbers-hide.md delete mode 100644 .changeset/good-pugs-rescue.md delete mode 100644 .changeset/nine-toes-dress.md delete mode 100644 .changeset/orange-days-look.md delete mode 100644 .changeset/short-dryers-tell.md delete mode 100644 .changeset/spicy-rules-speak.md delete mode 100644 .changeset/violet-tools-clap.md diff --git a/.changeset/cool-points-kneel.md b/.changeset/cool-points-kneel.md deleted file mode 100644 index 766c97d2ee5..00000000000 --- a/.changeset/cool-points-kneel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/model-generator': patch ---- - -bump graphql-generator dependency version to 0.5.1 diff --git a/.changeset/cool-zoos-enjoy.md b/.changeset/cool-zoos-enjoy.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/cool-zoos-enjoy.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/eleven-numbers-hide.md b/.changeset/eleven-numbers-hide.md deleted file mode 100644 index 9e91b8a2372..00000000000 --- a/.changeset/eleven-numbers-hide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -Handle errors when checking CDK bootstrap. diff --git a/.changeset/good-pugs-rescue.md b/.changeset/good-pugs-rescue.md deleted file mode 100644 index 7f4a210605d..00000000000 --- a/.changeset/good-pugs-rescue.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@aws-amplify/auth-construct': minor -'@aws-amplify/backend-auth': minor -'@aws-amplify/backend-data': minor -'@aws-amplify/plugin-types': minor -'@aws-amplify/backend': minor ---- - -Add support for referenceAuth. diff --git a/.changeset/nine-toes-dress.md b/.changeset/nine-toes-dress.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/nine-toes-dress.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/orange-days-look.md b/.changeset/orange-days-look.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/orange-days-look.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/short-dryers-tell.md b/.changeset/short-dryers-tell.md deleted file mode 100644 index 267a7abdb4b..00000000000 --- a/.changeset/short-dryers-tell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-cli': patch ---- - -Handle case when AWS region is configured as blank string diff --git a/.changeset/spicy-rules-speak.md b/.changeset/spicy-rules-speak.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/spicy-rules-speak.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/violet-tools-clap.md b/.changeset/violet-tools-clap.md deleted file mode 100644 index 0837e670526..00000000000 --- a/.changeset/violet-tools-clap.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@aws-amplify/backend': patch -'@aws-amplify/backend-deployer': patch -'@aws-amplify/backend-function': patch ---- - -update error mapping to catch when Lambda layer ARN regions do not match function region diff --git a/packages/auth-construct/CHANGELOG.md b/packages/auth-construct/CHANGELOG.md index 50e25ead8dc..d4205498d77 100644 --- a/packages/auth-construct/CHANGELOG.md +++ b/packages/auth-construct/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/auth-construct +## 1.5.0 + +### Minor Changes + +- 90a7c49: Add support for referenceAuth. + +### Patch Changes + +- Updated dependencies [90a7c49] + - @aws-amplify/plugin-types@1.4.0 + ## 1.4.0 ### Minor Changes diff --git a/packages/auth-construct/package.json b/packages/auth-construct/package.json index d24130f5ab6..42c20143eeb 100644 --- a/packages/auth-construct/package.json +++ b/packages/auth-construct/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/auth-construct", - "version": "1.4.0", + "version": "1.5.0", "type": "commonjs", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.4.0", "@aws-sdk/util-arn-parser": "^3.568.0" }, "peerDependencies": { diff --git a/packages/backend-auth/CHANGELOG.md b/packages/backend-auth/CHANGELOG.md index 9359e6b29e9..be8dda7109e 100644 --- a/packages/backend-auth/CHANGELOG.md +++ b/packages/backend-auth/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/backend-auth +## 1.4.0 + +### Minor Changes + +- 90a7c49: Add support for referenceAuth. + +### Patch Changes + +- Updated dependencies [90a7c49] + - @aws-amplify/auth-construct@1.5.0 + - @aws-amplify/plugin-types@1.4.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/backend-auth/package.json b/packages/backend-auth/package.json index 4c3a4aed8a9..670e06dc2fe 100644 --- a/packages/backend-auth/package.json +++ b/packages/backend-auth/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-auth", - "version": "1.3.0", + "version": "1.4.0", "type": "module", "publishConfig": { "access": "public" @@ -19,10 +19,10 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/auth-construct": "^1.4.0", + "@aws-amplify/auth-construct": "^1.5.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.3.1" + "@aws-amplify/plugin-types": "^1.4.0" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", diff --git a/packages/backend-data/CHANGELOG.md b/packages/backend-data/CHANGELOG.md index 99884312f3b..0a9fc39de04 100644 --- a/packages/backend-data/CHANGELOG.md +++ b/packages/backend-data/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend-data +## 1.2.0 + +### Minor Changes + +- 90a7c49: Add support for referenceAuth. + +### Patch Changes + +- Updated dependencies [90a7c49] + - @aws-amplify/plugin-types@1.4.0 + ## 1.1.7 ### Patch Changes diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index 69d5f245979..5e0384b04ec 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-data", - "version": "1.1.7", + "version": "1.2.0", "type": "module", "publishConfig": { "access": "public" @@ -31,7 +31,7 @@ "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/data-construct": "^1.10.1", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.4.0", "@aws-amplify/data-schema-types": "^1.2.0" } } diff --git a/packages/backend-deployer/CHANGELOG.md b/packages/backend-deployer/CHANGELOG.md index 63b1f4ec38c..2e71d10a85d 100644 --- a/packages/backend-deployer/CHANGELOG.md +++ b/packages/backend-deployer/CHANGELOG.md @@ -1,5 +1,14 @@ # @aws-amplify/backend-deployer +## 1.1.9 + +### Patch Changes + +- 7f2f68b: Handle errors when checking CDK bootstrap. +- 12cf209: update error mapping to catch when Lambda layer ARN regions do not match function region +- Updated dependencies [90a7c49] + - @aws-amplify/plugin-types@1.4.0 + ## 1.1.8 ### Patch Changes diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index 555ebe641a2..484de0b34a9 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-deployer", - "version": "1.1.8", + "version": "1.1.9", "type": "module", "publishConfig": { "access": "public" @@ -20,7 +20,7 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.4.0", "execa": "^8.0.1", "tsx": "^4.6.1" }, diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index 26030ae2eb6..24c5e71424e 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-function +## 1.7.5 + +### Patch Changes + +- 12cf209: update error mapping to catch when Lambda layer ARN regions do not match function region +- Updated dependencies [90a7c49] + - @aws-amplify/plugin-types@1.4.0 + ## 1.7.4 ### Patch Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index 2f51b13678d..def869dd46f 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.4.0", "execa": "^8.0.1" }, "devDependencies": { diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 5e1f459d4dd..e454739964c 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @aws-amplify/backend +## 1.7.0 + +### Minor Changes + +- 90a7c49: Add support for referenceAuth. + +### Patch Changes + +- 12cf209: update error mapping to catch when Lambda layer ARN regions do not match function region +- Updated dependencies [90a7c49] +- Updated dependencies [12cf209] + - @aws-amplify/backend-auth@1.4.0 + - @aws-amplify/backend-data@1.2.0 + - @aws-amplify/plugin-types@1.4.0 + - @aws-amplify/backend-function@1.7.5 + ## 1.6.2 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 25a1e023158..f896e7fd01a 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.6.2", + "version": "1.7.0", "type": "module", "publishConfig": { "access": "public" @@ -26,16 +26,16 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/data-schema": "^1.0.0", - "@aws-amplify/backend-auth": "^1.3.0", - "@aws-amplify/backend-function": "^1.7.4", - "@aws-amplify/backend-data": "^1.1.7", + "@aws-amplify/backend-auth": "^1.4.0", + "@aws-amplify/backend-function": "^1.7.5", + "@aws-amplify/backend-data": "^1.2.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.2", "@aws-amplify/client-config": "^1.5.2", "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.4.0", "@aws-sdk/client-amplify": "^3.624.0", "lodash.snakecase": "^4.1.1" }, diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 8259daaccb7..8c97e85b34f 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/backend-cli +## 1.4.2 + +### Patch Changes + +- f08abe4: Handle case when AWS region is configured as blank string +- Updated dependencies [443e2ff] +- Updated dependencies [7f2f68b] +- Updated dependencies [90a7c49] +- Updated dependencies [12cf209] + - @aws-amplify/model-generator@1.0.9 + - @aws-amplify/backend-deployer@1.1.9 + - @aws-amplify/plugin-types@1.4.0 + ## 1.4.1 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index b765f084fad..fb5fc6913a1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-cli", - "version": "1.4.1", + "version": "1.4.2", "description": "Command line interface for various Amplify tools", "bin": { "ampx": "lib/ampx.js", @@ -31,16 +31,16 @@ }, "homepage": "https://github.com/aws-amplify/amplify-backend#readme", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.8", + "@aws-amplify/backend-deployer": "^1.1.9", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.0", "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/form-generator": "^1.0.3", - "@aws-amplify/model-generator": "^1.0.8", + "@aws-amplify/model-generator": "^1.0.9", "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.4.0", "@aws-amplify/sandbox": "^1.2.5", "@aws-amplify/schema-generator": "^1.2.5", "@aws-sdk/client-amplify": "^3.624.0", diff --git a/packages/model-generator/CHANGELOG.md b/packages/model-generator/CHANGELOG.md index 80adb901230..01d352bb5f4 100644 --- a/packages/model-generator/CHANGELOG.md +++ b/packages/model-generator/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/model-generator +## 1.0.9 + +### Patch Changes + +- 443e2ff: bump graphql-generator dependency version to 0.5.1 +- Updated dependencies [90a7c49] + - @aws-amplify/plugin-types@1.4.0 + ## 1.0.8 ### Patch Changes diff --git a/packages/model-generator/package.json b/packages/model-generator/package.json index c6dfc4971eb..e8633f937ab 100644 --- a/packages/model-generator/package.json +++ b/packages/model-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/model-generator", - "version": "1.0.8", + "version": "1.0.9", "type": "module", "publishConfig": { "access": "public" @@ -24,7 +24,7 @@ "@aws-amplify/graphql-generator": "^0.5.1", "@aws-amplify/graphql-types-generator": "^3.6.0", "@aws-amplify/platform-core": "^1.0.5", - "@aws-amplify/plugin-types": "^1.3.0", + "@aws-amplify/plugin-types": "^1.4.0", "@aws-sdk/client-appsync": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/credential-providers": "^3.624.0", diff --git a/packages/plugin-types/CHANGELOG.md b/packages/plugin-types/CHANGELOG.md index 3b997236490..670e7dba082 100644 --- a/packages/plugin-types/CHANGELOG.md +++ b/packages/plugin-types/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/plugin-types +## 1.4.0 + +### Minor Changes + +- 90a7c49: Add support for referenceAuth. + ## 1.3.1 ### Patch Changes diff --git a/packages/plugin-types/package.json b/packages/plugin-types/package.json index 65404568f2f..c672d5c9360 100644 --- a/packages/plugin-types/package.json +++ b/packages/plugin-types/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/plugin-types", - "version": "1.3.1", + "version": "1.4.0", "types": "lib/index.d.ts", "type": "commonjs", "publishConfig": { From 38d69861d400b41976fb2a7d149359afcc5f80ef Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 11 Nov 2024 16:07:31 -0800 Subject: [PATCH 101/199] Fix intermittent credential problem on Windows (#2217) --- .github/actions/setup_profile/action.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup_profile/action.yml b/.github/actions/setup_profile/action.yml index 4c86490af33..e7ab8be9f7a 100644 --- a/.github/actions/setup_profile/action.yml +++ b/.github/actions/setup_profile/action.yml @@ -19,8 +19,15 @@ runs: with: role-to-assume: ${{ inputs.role-to-assume }} aws-region: ${{ inputs.aws-region }} - output-credentials: true # places the credentials in the GH context object rather than setting env vars - # the AWS credentials action does not have an option to configure a profile, so this manually configures one + # Credentials with special characters are not handled correctly on Windows + # when put into profile files. This forces action to retry until credentials without special characters + # are retrieved + # See: https://github.com/aws-actions/configure-aws-credentials/issues/599 + # and https://github.com/aws-actions/configure-aws-credentials/issues/528 + special-characters-workaround: ${{ contains(runner.os, 'Windows') }} + # places the credentials in the GH context object rather than setting env vars + # the AWS credentials action does not have an option to configure a profile, so this manually configures one + output-credentials: true - shell: bash run: | aws configure set aws_access_key_id ${{ steps.credentials.outputs.aws-access-key-id }} --profile ${{ inputs.profile-name }} From 9fd064261b1a8ee3c9f544885f68b128a82564db Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Tue, 12 Nov 2024 13:07:24 -0800 Subject: [PATCH 102/199] Add retry mechanism to create amplify e2e test (#2220) * Add retry mechanism to create amplify e2e test * Add retry mechanism to create amplify e2e test * this. --- .changeset/serious-spoons-fetch.md | 2 + .../src/package_manager_sanity_checks.test.ts | 43 +++++++------------ packages/integration-tests/src/retry.ts | 28 +++++++++++- .../src/test-e2e/create_amplify.test.ts | 19 ++++---- 4 files changed, 55 insertions(+), 37 deletions(-) create mode 100644 .changeset/serious-spoons-fetch.md diff --git a/.changeset/serious-spoons-fetch.md b/.changeset/serious-spoons-fetch.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/serious-spoons-fetch.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/package_manager_sanity_checks.test.ts b/packages/integration-tests/src/package_manager_sanity_checks.test.ts index cafc707117e..3375aee6e16 100644 --- a/packages/integration-tests/src/package_manager_sanity_checks.test.ts +++ b/packages/integration-tests/src/package_manager_sanity_checks.test.ts @@ -26,7 +26,7 @@ import { runWithPackageManager, } from './process-controller/process_controller.js'; import { amplifyAtTag } from './constants.js'; -import { runWithRetry } from './retry.js'; +import { RetryPredicates, runWithRetry } from './retry.js'; void describe('getting started happy path', async () => { let branchBackendIdentifier: BackendIdentifier; @@ -83,34 +83,21 @@ void describe('getting started happy path', async () => { return; } - await runWithRetry( - async () => { - if (packageManager === 'yarn-classic') { - await execa('yarn', ['add', 'create-amplify'], { cwd: tempDir }); - await execaCommand( - './node_modules/.bin/create-amplify --yes --debug', - { - cwd: tempDir, - env: { npm_config_user_agent: 'yarn/1.22.21' }, - } - ); - } else { - await runPackageManager( - packageManager, - ['create', amplifyAtTag, '--yes'], - tempDir - ).run(); - } - }, - (error) => { - // Retry on network-related errors or command failures - return ( - error.message.includes('exit code 1') && - (error.message.includes('aws-amplify') || - error.message.includes('Command failed with exit code 1: yarn add')) - ); + await runWithRetry(async () => { + if (packageManager === 'yarn-classic') { + await execa('yarn', ['add', 'create-amplify'], { cwd: tempDir }); + await execaCommand('./node_modules/.bin/create-amplify --yes --debug', { + cwd: tempDir, + env: { npm_config_user_agent: 'yarn/1.22.21' }, + }); + } else { + await runPackageManager( + packageManager, + ['create', amplifyAtTag, '--yes'], + tempDir + ).run(); } - ); + }, RetryPredicates.createAmplifyRetryPredicate); const pathPrefix = path.join(tempDir, 'amplify'); diff --git a/packages/integration-tests/src/retry.ts b/packages/integration-tests/src/retry.ts index b7804c115b5..e972ca017cb 100644 --- a/packages/integration-tests/src/retry.ts +++ b/packages/integration-tests/src/retry.ts @@ -1,3 +1,5 @@ +export type RetryPredicate = (error: Error) => boolean; + /** * Executes an asynchronous operation with retry logic. * This function attempts to execute the provided callable function multiple times @@ -6,7 +8,7 @@ */ export const runWithRetry = async ( callable: () => Promise, - retryPredicate: (error: Error) => boolean, + retryPredicate: RetryPredicate, maxAttempts = 3 ): Promise => { const collectedErrors: Error[] = []; @@ -21,6 +23,10 @@ export const runWithRetry = async ( if (!retryPredicate(error)) { throw error; } + } else { + // re-throw non-Error. + // This should never happen, but we should be aware if it does. + throw error; } } } @@ -30,3 +36,23 @@ export const runWithRetry = async ( `All ${maxAttempts} attempts failed` ); }; + +/** + * Known retry predicates that repeat in multiple places. + */ +export class RetryPredicates { + static createAmplifyRetryPredicate: RetryPredicate = ( + error: Error + ): boolean => { + const message = error.message.toLowerCase(); + // Note: we can't assert on whole stdout or stderr because + // they're not always captured in the error due to settings we need for + // ProcessController to work. + const didProcessExitWithError = message.includes('exit code 1'); + const isKnownProcess = + (message.includes('yarn add') && message.includes('aws-amplify')) || + message.includes('npm create amplify') || + message.includes('pnpm create amplify'); + return didProcessExitWithError && isKnownProcess; + }; +} diff --git a/packages/integration-tests/src/test-e2e/create_amplify.test.ts b/packages/integration-tests/src/test-e2e/create_amplify.test.ts index 189405ef6f0..b7b3c1333a1 100644 --- a/packages/integration-tests/src/test-e2e/create_amplify.test.ts +++ b/packages/integration-tests/src/test-e2e/create_amplify.test.ts @@ -9,6 +9,7 @@ import { testConcurrencyLevel } from './test_concurrency.js'; import { findBaselineCdkVersion } from '../cdk_version_finder.js'; import { amplifyAtTag } from '../constants.js'; import { NpmProxyController } from '../npm_proxy_controller.js'; +import { RetryPredicates, runWithRetry } from '../retry.js'; void describe( 'create-amplify script', @@ -78,14 +79,16 @@ void describe( ); } - await execa( - 'npm', - ['create', amplifyAtTag, '--yes', '--', '--debug'], - { - cwd: tempDir, - stdio: 'inherit', - } - ); + await runWithRetry(async () => { + await execa( + 'npm', + ['create', amplifyAtTag, '--yes', '--', '--debug'], + { + cwd: tempDir, + stdio: 'inherit', + } + ); + }, RetryPredicates.createAmplifyRetryPredicate); // Override CDK installation with baseline version await execa( From bc6dc699fb7e39860940071da77f4a52f67a4806 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 13 Nov 2024 16:13:16 -0800 Subject: [PATCH 103/199] Fix case where tool use does not have input while streaming (#2226) --- .changeset/clean-hounds-exercise.md | 5 ++ .../runtime/bedrock_converse_adapter.test.ts | 90 ++++++++++++++++++- .../runtime/bedrock_converse_adapter.ts | 7 +- 3 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 .changeset/clean-hounds-exercise.md diff --git a/.changeset/clean-hounds-exercise.md b/.changeset/clean-hounds-exercise.md new file mode 100644 index 00000000000..c778ee364a3 --- /dev/null +++ b/.changeset/clean-hounds-exercise.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': patch +--- + +Fix case where tool use does not have input while streaming diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts index 38a32a0128c..4d735b5e939 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts @@ -823,6 +823,88 @@ void describe('Bedrock converse adapter', () => { }); }); + void it('handles tool use with empty input when streaming', async () => { + const toolOutput: ToolResultContentBlock = { + text: 'additionalToolOutput', + }; + const toolExecuteMock = mock.fn< + (input: unknown) => Promise + >(() => Promise.resolve(toolOutput)); + const tool: ExecutableTool = { + name: 'toolId', + description: 'tool description', + inputSchema: { + json: {}, + }, + execute: toolExecuteMock, + }; + + const event: ConversationTurnEvent = { + ...commonEvent, + }; + + const bedrockClient = new BedrockRuntimeClient(); + const bedrockResponseQueue: Array< + ConverseCommandOutput | ConverseStreamCommandOutput + > = []; + const toolUse1 = { + toolUseId: randomUUID().toString(), + name: tool.name, + input: undefined, + }; + const toolUse2 = { + toolUseId: randomUUID().toString(), + name: tool.name, + input: '', + }; + const toolUseBedrockResponse = mockBedrockResponse( + [ + { + toolUse: toolUse1, + }, + { + toolUse: toolUse2, + }, + ], + true + ); + bedrockResponseQueue.push(toolUseBedrockResponse); + const content = [ + { + text: 'finalResponse', + }, + ]; + const finalBedrockResponse = mockBedrockResponse(content, true); + bedrockResponseQueue.push(finalBedrockResponse); + + mock.method(bedrockClient, 'send', () => + Promise.resolve(bedrockResponseQueue.shift()) + ); + + const adapter = new BedrockConverseAdapter( + event, + [tool], + bedrockClient, + undefined, + messageHistoryRetriever + ); + + const chunks: Array = await askBedrockWithStreaming( + adapter + ); + const responseText = chunks.reduce((acc, next) => { + if (next.contentBlockText) { + acc += next.contentBlockText; + } + return acc; + }, ''); + assert.strictEqual(responseText, 'finalResponse'); + + assert.strictEqual(toolExecuteMock.mock.calls.length, 2); + assert.strictEqual(toolExecuteMock.mock.calls[0].arguments[0], undefined); + assert.strictEqual(toolExecuteMock.mock.calls[1].arguments[0], undefined); + }); + void it('throws if tool is duplicated', () => { assert.throws( () => @@ -1007,19 +1089,21 @@ const mockConverseStreamCommandOutput = ( }, }, }); - const input = JSON.stringify(block.toolUse.input); + const input = block.toolUse.input + ? JSON.stringify(block.toolUse.input) + : undefined; streamItems.push({ contentBlockDelta: { contentBlockIndex: i, delta: { toolUse: { // simulate chunked input - input: input.substring(0, 1), + input: input?.substring(0, 1), }, }, }, }); - if (input.length > 1) { + if (input && input.length > 1) { streamItems.push({ contentBlockDelta: { contentBlockIndex: i, diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts index 3bb48436989..b7f8b7c6921 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts @@ -232,8 +232,9 @@ export class BedrockConverseAdapter { if (chunk.contentBlockDelta.delta?.toolUse) { if (!chunk.contentBlockDelta.delta.toolUse.input) { toolUseInput = ''; + } else { + toolUseInput += chunk.contentBlockDelta.delta.toolUse.input; } - toolUseInput += chunk.contentBlockDelta.delta.toolUse.input; } else if (chunk.contentBlockDelta.delta?.text) { text += chunk.contentBlockDelta.delta.text; yield { @@ -249,7 +250,9 @@ export class BedrockConverseAdapter { } } else if (chunk.contentBlockStop) { if (toolUseBlock) { - toolUseBlock.toolUse.input = JSON.parse(toolUseInput); + if (toolUseInput) { + toolUseBlock.toolUse.input = JSON.parse(toolUseInput); + } accumulatedAssistantMessage.content?.push(toolUseBlock); if ( toolUseBlock.toolUse.name && From 5d873a181b8b1ae3ab41e9b5fac6699109f15d71 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:56:18 -0800 Subject: [PATCH 104/199] Version Packages (#2229) Co-authored-by: github-actions[bot] --- .changeset/clean-hounds-exercise.md | 5 ----- .changeset/serious-spoons-fetch.md | 2 -- packages/ai-constructs/CHANGELOG.md | 6 ++++++ packages/ai-constructs/package.json | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) delete mode 100644 .changeset/clean-hounds-exercise.md delete mode 100644 .changeset/serious-spoons-fetch.md diff --git a/.changeset/clean-hounds-exercise.md b/.changeset/clean-hounds-exercise.md deleted file mode 100644 index c778ee364a3..00000000000 --- a/.changeset/clean-hounds-exercise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': patch ---- - -Fix case where tool use does not have input while streaming diff --git a/.changeset/serious-spoons-fetch.md b/.changeset/serious-spoons-fetch.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/serious-spoons-fetch.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index 48831fdf10c..bc95583a01d 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/ai-constructs +## 0.8.2 + +### Patch Changes + +- bc6dc69: Fix case where tool use does not have input while streaming + ## 0.8.1 ### Patch Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index 4ca1106feb1..3b42ba8c809 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "0.8.1", + "version": "0.8.2", "type": "commonjs", "publishConfig": { "access": "public" From fd8759d79c62e1b3d93c9b551b3ceda48131160d Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 14 Nov 2024 09:58:23 -0800 Subject: [PATCH 105/199] Fix a case when Bedrock throws validation error if tool input is not persisted in history (#2230) * Fix a case when Bedrock throws validation error if tool input is not persisted in history * some prompt engineering --- .changeset/clever-emus-hope.md | 5 ++ .../runtime/bedrock_converse_adapter.test.ts | 4 +- .../runtime/bedrock_converse_adapter.ts | 4 ++ .../conversation_handler_project.ts | 61 +++++++++++++++++++ .../conversation-handler/amplify/constants.ts | 2 + .../custom_handler.ts | 28 ++++++++- 6 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 .changeset/clever-emus-hope.md diff --git a/.changeset/clever-emus-hope.md b/.changeset/clever-emus-hope.md new file mode 100644 index 00000000000..219f71312b3 --- /dev/null +++ b/.changeset/clever-emus-hope.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': patch +--- + +Fix a case when Bedrock throws validation error if tool input is not persisted in history diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts index 4d735b5e939..b3a99ed1a0b 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts @@ -901,8 +901,8 @@ void describe('Bedrock converse adapter', () => { assert.strictEqual(responseText, 'finalResponse'); assert.strictEqual(toolExecuteMock.mock.calls.length, 2); - assert.strictEqual(toolExecuteMock.mock.calls[0].arguments[0], undefined); - assert.strictEqual(toolExecuteMock.mock.calls[1].arguments[0], undefined); + assert.deepStrictEqual(toolExecuteMock.mock.calls[0].arguments[0], {}); + assert.deepStrictEqual(toolExecuteMock.mock.calls[1].arguments[0], {}); }); void it('throws if tool is duplicated', () => { diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts index b7f8b7c6921..0b692a848af 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts @@ -252,6 +252,10 @@ export class BedrockConverseAdapter { if (toolUseBlock) { if (toolUseInput) { toolUseBlock.toolUse.input = JSON.parse(toolUseInput); + } else { + // Bedrock API requires tool input to be non-null in message history. + // Therefore, falling back to empty object. + toolUseBlock.toolUse.input = {}; } accumulatedAssistantMessage.content?.push(toolUseBlock); if ( diff --git a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts index 53a9ea44a93..da7393d79e9 100644 --- a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts +++ b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts @@ -26,6 +26,7 @@ import assert from 'assert'; import { NormalizedCacheObject } from '@apollo/client'; import { bedrockModelId, + expectedRandomNumber, expectedTemperatureInDataToolScenario, expectedTemperaturesInProgrammaticToolScenario, } from '../test-projects/conversation-handler/amplify/constants.js'; @@ -278,6 +279,16 @@ class ConversationHandlerTestProject extends TestProjectBase { ) ); + await this.executeWithRetry(() => + this.assertCustomConversationHandlerCanExecuteTurnWithParameterLessTool( + backendId, + authenticatedUserCredentials.accessToken, + dataUrl, + apolloClient, + true + ) + ); + await this.executeWithRetry(() => this.assertDefaultConversationHandlerCanExecuteTurnWithDataTool( backendId, @@ -853,6 +864,56 @@ class ConversationHandlerTestProject extends TestProjectBase { ); }; + private assertCustomConversationHandlerCanExecuteTurnWithParameterLessTool = + async ( + backendId: BackendIdentifier, + accessToken: string, + graphqlApiEndpoint: string, + apolloClient: ApolloClient, + streamResponse: boolean + ): Promise => { + const customConversationHandlerFunction = ( + await this.resourceFinder.findByBackendIdentifier( + backendId, + 'AWS::Lambda::Function', + (name) => name.includes('custom') + ) + )[0]; + + const message: CreateConversationMessageChatInput = { + conversationId: randomUUID().toString(), + id: randomUUID().toString(), + role: 'user', + content: [ + { + text: 'Give me a random number', + }, + ], + }; + await this.insertMessage(apolloClient, message); + + // send event + const event: ConversationTurnEvent = { + conversationId: message.conversationId, + currentMessageId: message.id, + graphqlApiEndpoint: graphqlApiEndpoint, + request: { + headers: { authorization: accessToken }, + }, + ...this.getCommonEventProperties(streamResponse), + }; + const response = await this.executeConversationTurn( + event, + customConversationHandlerFunction, + apolloClient + ); + // Assert that tool was used. I.e. LLM used value provided by the tool. + assert.match( + response.content, + new RegExp(expectedRandomNumber.toString()) + ); + }; + private assertDefaultConversationHandlerCanPropagateError = async ( backendId: BackendIdentifier, accessToken: string, diff --git a/packages/integration-tests/src/test-projects/conversation-handler/amplify/constants.ts b/packages/integration-tests/src/test-projects/conversation-handler/amplify/constants.ts index 60961d61308..8a2256051e8 100644 --- a/packages/integration-tests/src/test-projects/conversation-handler/amplify/constants.ts +++ b/packages/integration-tests/src/test-projects/conversation-handler/amplify/constants.ts @@ -14,3 +14,5 @@ export const expectedTemperaturesInProgrammaticToolScenario = { }; export const expectedTemperatureInDataToolScenario = 85; + +export const expectedRandomNumber = 42; diff --git a/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts b/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts index add8b2c80b6..2382dc82d9c 100644 --- a/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts +++ b/packages/integration-tests/src/test-projects/conversation-handler/amplify/custom-conversation-handler/custom_handler.ts @@ -3,7 +3,10 @@ import { createExecutableTool, handleConversationTurnEvent, } from '@aws-amplify/backend-ai/conversation/runtime'; -import { expectedTemperaturesInProgrammaticToolScenario } from '../constants.js'; +import { + expectedRandomNumber, + expectedTemperaturesInProgrammaticToolScenario, +} from '../constants.js'; const thermometerInputSchema = { type: 'object', @@ -33,11 +36,32 @@ const thermometer = createExecutableTool( } ); +// Parameter-less tool. +const randomNumberGeneratorInputSchema = { + type: 'object', + properties: {}, + required: [], +} as const; + +const randomNumberGenerator = createExecutableTool( + 'randomNumberGenerator', + 'Returns a random number', + { + json: randomNumberGeneratorInputSchema, + }, + () => { + return Promise.resolve({ + // We use this value in test assertion. + text: `${expectedRandomNumber}`, + }); + } +); + /** * Handler with simple tool. */ export const handler = async (event: ConversationTurnEvent) => { await handleConversationTurnEvent(event, { - tools: [thermometer], + tools: [randomNumberGenerator, thermometer], }); }; From bbd6adddda9c47cb088c3f9c44841febe7596217 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 14 Nov 2024 10:32:51 -0800 Subject: [PATCH 106/199] GA release of backend AI features (#2231) --- .changeset/strange-fans-crash.md | 6 ++++++ scripts/check_package_versions.ts | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 .changeset/strange-fans-crash.md diff --git a/.changeset/strange-fans-crash.md b/.changeset/strange-fans-crash.md new file mode 100644 index 00000000000..10da0a6d2d8 --- /dev/null +++ b/.changeset/strange-fans-crash.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/ai-constructs': major +'@aws-amplify/backend-ai': major +--- + +GA release of backend AI features diff --git a/scripts/check_package_versions.ts b/scripts/check_package_versions.ts index a35822819af..149cf3fd724 100644 --- a/scripts/check_package_versions.ts +++ b/scripts/check_package_versions.ts @@ -11,8 +11,6 @@ const packagePaths = await glob('./packages/*'); const getExpectedMajorVersion = (packageName: string) => { switch (packageName) { case 'ampx': - case '@aws-amplify/ai-constructs': - case '@aws-amplify/backend-ai': return '0.'; default: return '1.'; From f83d6d58cf9b167c6b4aa59e6be92ee4e9051ab5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:53:15 +0000 Subject: [PATCH 107/199] Version Packages (#2232) Co-authored-by: github-actions[bot] --- .changeset/clever-emus-hope.md | 5 ----- .changeset/strange-fans-crash.md | 6 ------ packages/ai-constructs/CHANGELOG.md | 10 ++++++++++ packages/ai-constructs/package.json | 2 +- packages/backend-ai/CHANGELOG.md | 12 ++++++++++++ packages/backend-ai/package.json | 4 ++-- packages/integration-tests/package.json | 4 ++-- 7 files changed, 27 insertions(+), 16 deletions(-) delete mode 100644 .changeset/clever-emus-hope.md delete mode 100644 .changeset/strange-fans-crash.md diff --git a/.changeset/clever-emus-hope.md b/.changeset/clever-emus-hope.md deleted file mode 100644 index 219f71312b3..00000000000 --- a/.changeset/clever-emus-hope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': patch ---- - -Fix a case when Bedrock throws validation error if tool input is not persisted in history diff --git a/.changeset/strange-fans-crash.md b/.changeset/strange-fans-crash.md deleted file mode 100644 index 10da0a6d2d8..00000000000 --- a/.changeset/strange-fans-crash.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/ai-constructs': major -'@aws-amplify/backend-ai': major ---- - -GA release of backend AI features diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index bc95583a01d..757855305c4 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/ai-constructs +## 1.0.0 + +### Major Changes + +- bbd6add: GA release of backend AI features + +### Patch Changes + +- fd8759d: Fix a case when Bedrock throws validation error if tool input is not persisted in history + ## 0.8.2 ### Patch Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index 3b42ba8c809..73fdf04e270 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "0.8.2", + "version": "1.0.0", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index 9f8a59c7591..f40ac90b244 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/backend-ai +## 1.0.0 + +### Major Changes + +- bbd6add: GA release of backend AI features + +### Patch Changes + +- Updated dependencies [fd8759d] +- Updated dependencies [bbd6add] + - @aws-amplify/ai-constructs@1.0.0 + ## 0.3.5 ### Patch Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 2b4f5de6918..b596dc85037 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "0.3.5", + "version": "1.0.0", "type": "module", "publishConfig": { "access": "public" @@ -22,7 +22,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.8.0", + "@aws-amplify/ai-constructs": "^1.0.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/data-schema-types": "^1.2.0", diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index ce7b456a1c3..7f394b28b8a 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -5,10 +5,10 @@ "type": "module", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.8.0", + "@aws-amplify/ai-constructs": "^1.0.0", "@aws-amplify/auth-construct": "^1.4.0", "@aws-amplify/backend": "^1.6.0", - "@aws-amplify/backend-ai": "^0.3.5", + "@aws-amplify/backend-ai": "^1.0.0", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/data-schema": "^1.0.0", From f1db8868e035294fab1872dc153940480c352811 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 15 Nov 2024 11:49:08 -0800 Subject: [PATCH 108/199] add resourceGroupName prop to function (#2233) * add resourceGroupName prop to function and storage * PR feedback * update changeset --- .changeset/bright-guests-marry.md | 11 +++ .../backend-ai/src/conversation/factory.ts | 4 +- packages/backend-auth/src/factory.ts | 3 +- .../backend-auth/src/reference_factory.ts | 3 +- packages/backend-data/src/factory.ts | 3 +- packages/backend-function/API.md | 2 + packages/backend-function/src/factory.ts | 16 +++- .../src/storage_container_entry_generator.ts | 3 +- ...ular_dep_auth_data_func.deployment.test.ts | 4 + .../circular_dep_data_func.deployment.test.ts | 4 + ...ircular_dep_auth_data_func.sandbox.test.ts | 4 + .../circular_dep_data_func.sandbox.test.ts | 4 + .../circular_dep_auth_data_func.ts | 83 +++++++++++++++++++ .../circular_dep_data_func.ts | 83 +++++++++++++++++++ .../amplify/auth/resource.ts | 11 +++ .../amplify/backend.ts | 23 +++++ .../amplify/data/resource.ts | 25 ++++++ .../functions/api-function/resource.ts | 7 ++ .../amplify/functions/handler.ts | 1 + .../amplify/functions/pre-sign-up/resource.ts | 7 ++ .../amplify/auth/resource.ts | 7 ++ .../circular-dep-data-func/amplify/backend.ts | 23 +++++ .../amplify/data/resource.ts | 32 +++++++ .../functions/api-function/resource.ts | 7 ++ .../amplify/functions/handler.ts | 1 + .../functions/query-function/resource.ts | 7 ++ packages/plugin-types/API.md | 5 ++ .../src/amplify_resource_group_name.ts | 12 +++ packages/plugin-types/src/index.ts | 1 + 29 files changed, 389 insertions(+), 7 deletions(-) create mode 100644 .changeset/bright-guests-marry.md create mode 100644 packages/integration-tests/src/test-e2e/deployment/circular_dep_auth_data_func.deployment.test.ts create mode 100644 packages/integration-tests/src/test-e2e/deployment/circular_dep_data_func.deployment.test.ts create mode 100644 packages/integration-tests/src/test-e2e/sandbox/circular_dep_auth_data_func.sandbox.test.ts create mode 100644 packages/integration-tests/src/test-e2e/sandbox/circular_dep_data_func.sandbox.test.ts create mode 100644 packages/integration-tests/src/test-project-setup/circular_dep_auth_data_func.ts create mode 100644 packages/integration-tests/src/test-project-setup/circular_dep_data_func.ts create mode 100644 packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/auth/resource.ts create mode 100644 packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/backend.ts create mode 100644 packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/data/resource.ts create mode 100644 packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/functions/api-function/resource.ts create mode 100644 packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/functions/handler.ts create mode 100644 packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/functions/pre-sign-up/resource.ts create mode 100644 packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/auth/resource.ts create mode 100644 packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/backend.ts create mode 100644 packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/data/resource.ts create mode 100644 packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/functions/api-function/resource.ts create mode 100644 packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/functions/handler.ts create mode 100644 packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/functions/query-function/resource.ts create mode 100644 packages/plugin-types/src/amplify_resource_group_name.ts diff --git a/.changeset/bright-guests-marry.md b/.changeset/bright-guests-marry.md new file mode 100644 index 00000000000..48492708b7e --- /dev/null +++ b/.changeset/bright-guests-marry.md @@ -0,0 +1,11 @@ +--- +'@aws-amplify/backend-function': minor +'@aws-amplify/backend-storage': patch +'@aws-amplify/plugin-types': minor +'@aws-amplify/backend': minor +'@aws-amplify/backend-auth': patch +'@aws-amplify/backend-data': patch +'@aws-amplify/backend-ai': patch +--- + +add resourceGroupName prop to function diff --git a/packages/backend-ai/src/conversation/factory.ts b/packages/backend-ai/src/conversation/factory.ts index 6e04bbbcf54..6b4242288ff 100644 --- a/packages/backend-ai/src/conversation/factory.ts +++ b/packages/backend-ai/src/conversation/factory.ts @@ -1,5 +1,6 @@ import { AIConversationOutput } from '@aws-amplify/backend-output-schemas'; import { + AmplifyResourceGroupName, BackendOutputStorageStrategy, ConstructContainerEntryGenerator, ConstructFactory, @@ -20,7 +21,8 @@ import { AiModel } from '@aws-amplify/data-schema-types'; class ConversationHandlerFunctionGenerator implements ConstructContainerEntryGenerator { - readonly resourceGroupName = 'conversationHandlerFunction'; + readonly resourceGroupName: AmplifyResourceGroupName = + 'conversationHandlerFunction'; constructor( private readonly props: DefineConversationHandlerFunctionProps, diff --git a/packages/backend-auth/src/factory.ts b/packages/backend-auth/src/factory.ts index 767e08430f6..c0a4c63445f 100644 --- a/packages/backend-auth/src/factory.ts +++ b/packages/backend-auth/src/factory.ts @@ -12,6 +12,7 @@ import { TriggerEvent, } from '@aws-amplify/auth-construct'; import { + AmplifyResourceGroupName, AuthResources, AuthRoleName, ConstructContainerEntryGenerator, @@ -134,7 +135,7 @@ export class AmplifyAuthFactory implements ConstructFactory { } class AmplifyAuthGenerator implements ConstructContainerEntryGenerator { - readonly resourceGroupName = 'auth'; + readonly resourceGroupName: AmplifyResourceGroupName = 'auth'; private readonly name: string; constructor( diff --git a/packages/backend-auth/src/reference_factory.ts b/packages/backend-auth/src/reference_factory.ts index 926db833c94..81f4ff51b6c 100644 --- a/packages/backend-auth/src/reference_factory.ts +++ b/packages/backend-auth/src/reference_factory.ts @@ -1,4 +1,5 @@ import { + AmplifyResourceGroupName, AuthRoleName, BackendOutputStorageStrategy, ConstructContainerEntryGenerator, @@ -129,7 +130,7 @@ export class AmplifyReferenceAuthFactory class AmplifyReferenceAuthGenerator implements ConstructContainerEntryGenerator { - readonly resourceGroupName = 'auth'; + readonly resourceGroupName: AmplifyResourceGroupName = 'auth'; private readonly name: string; constructor( diff --git a/packages/backend-data/src/factory.ts b/packages/backend-data/src/factory.ts index 578d0cbcbc7..92436ec6b17 100644 --- a/packages/backend-data/src/factory.ts +++ b/packages/backend-data/src/factory.ts @@ -1,6 +1,7 @@ import { IConstruct } from 'constructs'; import { AmplifyFunction, + AmplifyResourceGroupName, AuthResources, BackendOutputStorageStrategy, ConstructContainerEntryGenerator, @@ -112,7 +113,7 @@ export class DataFactory implements ConstructFactory { } class DataGenerator implements ConstructContainerEntryGenerator { - readonly resourceGroupName = 'data'; + readonly resourceGroupName: AmplifyResourceGroupName = 'data'; private readonly name: string; constructor( diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index 7348dba85ec..c87d9469ef0 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -4,6 +4,7 @@ ```ts +import { AmplifyResourceGroupName } from '@aws-amplify/plugin-types'; import { BackendSecret } from '@aws-amplify/plugin-types'; import { ConstructFactory } from '@aws-amplify/plugin-types'; import { FunctionResources } from '@aws-amplify/plugin-types'; @@ -38,6 +39,7 @@ export type FunctionProps = { schedule?: FunctionSchedule | FunctionSchedule[]; layers?: Record; bundling?: FunctionBundlingOptions; + resourceGroupName?: AmplifyResourceGroupName; }; // @public (undocumented) diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 5d5a6938e26..5a62fcf36fb 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -9,6 +9,7 @@ import { TagName, } from '@aws-amplify/platform-core'; import { + AmplifyResourceGroupName, BackendOutputStorageStrategy, BackendSecret, BackendSecretResolver, @@ -150,6 +151,14 @@ export type FunctionProps = { * Options for bundling the function code. */ bundling?: FunctionBundlingOptions; + + /** + * Group the function with existing Amplify resources or separate the function into its own group. + * @default 'function' // grouping with other Amplify functions + * @example + * resourceGroupName: 'auth' // to group an auth trigger with an auth resource + */ + resourceGroupName?: AmplifyResourceGroupName; }; export type FunctionBundlingOptions = { @@ -208,6 +217,7 @@ class FunctionFactory implements ConstructFactory { schedule: this.resolveSchedule(), bundling: this.resolveBundling(), layers, + resourceGroupName: this.props.resourceGroupName ?? 'function', }; }; @@ -339,12 +349,14 @@ class FunctionFactory implements ConstructFactory { type HydratedFunctionProps = Required; class FunctionGenerator implements ConstructContainerEntryGenerator { - readonly resourceGroupName = 'function'; + readonly resourceGroupName: AmplifyResourceGroupName; constructor( private readonly props: HydratedFunctionProps, private readonly outputStorageStrategy: BackendOutputStorageStrategy - ) {} + ) { + this.resourceGroupName = props.resourceGroupName; + } generateContainerEntry = ({ scope, diff --git a/packages/backend-storage/src/storage_container_entry_generator.ts b/packages/backend-storage/src/storage_container_entry_generator.ts index b7da0eaaed4..441b54bb86c 100644 --- a/packages/backend-storage/src/storage_container_entry_generator.ts +++ b/packages/backend-storage/src/storage_container_entry_generator.ts @@ -1,4 +1,5 @@ import { + AmplifyResourceGroupName, ConstructContainerEntryGenerator, ConstructFactoryGetInstanceProps, GenerateContainerEntryProps, @@ -17,7 +18,7 @@ import { TagName } from '@aws-amplify/platform-core'; export class StorageContainerEntryGenerator implements ConstructContainerEntryGenerator { - readonly resourceGroupName = 'storage'; + readonly resourceGroupName: AmplifyResourceGroupName = 'storage'; /** * Initialize with context from storage factory diff --git a/packages/integration-tests/src/test-e2e/deployment/circular_dep_auth_data_func.deployment.test.ts b/packages/integration-tests/src/test-e2e/deployment/circular_dep_auth_data_func.deployment.test.ts new file mode 100644 index 00000000000..88f36000bf5 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/deployment/circular_dep_auth_data_func.deployment.test.ts @@ -0,0 +1,4 @@ +import { CircularDepAuthDataFuncTestProjectCreator } from '../../test-project-setup/circular_dep_auth_data_func.js'; +import { defineDeploymentTest } from './deployment.test.template.js'; + +defineDeploymentTest(new CircularDepAuthDataFuncTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/deployment/circular_dep_data_func.deployment.test.ts b/packages/integration-tests/src/test-e2e/deployment/circular_dep_data_func.deployment.test.ts new file mode 100644 index 00000000000..8725ac07c61 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/deployment/circular_dep_data_func.deployment.test.ts @@ -0,0 +1,4 @@ +import { CircularDepDataFuncTestProjectCreator } from '../../test-project-setup/circular_dep_data_func.js'; +import { defineDeploymentTest } from './deployment.test.template.js'; + +defineDeploymentTest(new CircularDepDataFuncTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/sandbox/circular_dep_auth_data_func.sandbox.test.ts b/packages/integration-tests/src/test-e2e/sandbox/circular_dep_auth_data_func.sandbox.test.ts new file mode 100644 index 00000000000..16def745992 --- /dev/null +++ b/packages/integration-tests/src/test-e2e/sandbox/circular_dep_auth_data_func.sandbox.test.ts @@ -0,0 +1,4 @@ +import { CircularDepAuthDataFuncTestProjectCreator } from '../../test-project-setup/circular_dep_auth_data_func.js'; +import { defineSandboxTest } from './sandbox.test.template.js'; + +defineSandboxTest(new CircularDepAuthDataFuncTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/sandbox/circular_dep_data_func.sandbox.test.ts b/packages/integration-tests/src/test-e2e/sandbox/circular_dep_data_func.sandbox.test.ts new file mode 100644 index 00000000000..d024c5f764a --- /dev/null +++ b/packages/integration-tests/src/test-e2e/sandbox/circular_dep_data_func.sandbox.test.ts @@ -0,0 +1,4 @@ +import { CircularDepDataFuncTestProjectCreator } from '../../test-project-setup/circular_dep_data_func.js'; +import { defineSandboxTest } from './sandbox.test.template.js'; + +defineSandboxTest(new CircularDepDataFuncTestProjectCreator()); diff --git a/packages/integration-tests/src/test-project-setup/circular_dep_auth_data_func.ts b/packages/integration-tests/src/test-project-setup/circular_dep_auth_data_func.ts new file mode 100644 index 00000000000..b75882fb144 --- /dev/null +++ b/packages/integration-tests/src/test-project-setup/circular_dep_auth_data_func.ts @@ -0,0 +1,83 @@ +import { TestProjectBase } from './test_project_base.js'; +import fs from 'fs/promises'; +import { createEmptyAmplifyProject } from './create_empty_amplify_project.js'; +import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; +import { TestProjectCreator } from './test_project_creator.js'; +import { AmplifyClient } from '@aws-sdk/client-amplify'; +import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; + +/** + * Creates test projects with circular dependency between auth, data, and functions + */ +export class CircularDepAuthDataFuncTestProjectCreator + implements TestProjectCreator +{ + readonly name = 'circular-dep-auth-data-func'; + + /** + * Creates project creator. + */ + constructor( + private readonly cfnClient: CloudFormationClient = new CloudFormationClient( + e2eToolingClientConfig + ), + private readonly amplifyClient: AmplifyClient = new AmplifyClient( + e2eToolingClientConfig + ) + ) {} + + createProject = async (e2eProjectDir: string): Promise => { + const { projectName, projectRoot, projectAmplifyDir } = + await createEmptyAmplifyProject(this.name, e2eProjectDir); + + const project = new CircularDepAuthDataFuncTestProject( + projectName, + projectRoot, + projectAmplifyDir, + this.cfnClient, + this.amplifyClient + ); + await fs.cp( + project.sourceProjectAmplifyDirURL, + project.projectAmplifyDirPath, + { + recursive: true, + } + ); + return project; + }; +} + +/** + * Test project with circular dependency between auth, data, and functions + */ +class CircularDepAuthDataFuncTestProject extends TestProjectBase { + readonly sourceProjectDirPath = + '../../src/test-projects/circular-dep-auth-data-func'; + + readonly sourceProjectAmplifyDirSuffix = `${this.sourceProjectDirPath}/amplify`; + + readonly sourceProjectAmplifyDirURL: URL = new URL( + this.sourceProjectAmplifyDirSuffix, + import.meta.url + ); + + /** + * Create a test project instance. + */ + constructor( + name: string, + projectDirPath: string, + projectAmplifyDirPath: string, + cfnClient: CloudFormationClient, + amplifyClient: AmplifyClient + ) { + super( + name, + projectDirPath, + projectAmplifyDirPath, + cfnClient, + amplifyClient + ); + } +} diff --git a/packages/integration-tests/src/test-project-setup/circular_dep_data_func.ts b/packages/integration-tests/src/test-project-setup/circular_dep_data_func.ts new file mode 100644 index 00000000000..d00c86facd2 --- /dev/null +++ b/packages/integration-tests/src/test-project-setup/circular_dep_data_func.ts @@ -0,0 +1,83 @@ +import { TestProjectBase } from './test_project_base.js'; +import fs from 'fs/promises'; +import { createEmptyAmplifyProject } from './create_empty_amplify_project.js'; +import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; +import { TestProjectCreator } from './test_project_creator.js'; +import { AmplifyClient } from '@aws-sdk/client-amplify'; +import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; + +/** + * Creates test projects with circular dependency between data, and functions + */ +export class CircularDepDataFuncTestProjectCreator + implements TestProjectCreator +{ + readonly name = 'circular-dep-data-func'; + + /** + * Creates project creator. + */ + constructor( + private readonly cfnClient: CloudFormationClient = new CloudFormationClient( + e2eToolingClientConfig + ), + private readonly amplifyClient: AmplifyClient = new AmplifyClient( + e2eToolingClientConfig + ) + ) {} + + createProject = async (e2eProjectDir: string): Promise => { + const { projectName, projectRoot, projectAmplifyDir } = + await createEmptyAmplifyProject(this.name, e2eProjectDir); + + const project = new CircularDepDataFuncTestProject( + projectName, + projectRoot, + projectAmplifyDir, + this.cfnClient, + this.amplifyClient + ); + await fs.cp( + project.sourceProjectAmplifyDirURL, + project.projectAmplifyDirPath, + { + recursive: true, + } + ); + return project; + }; +} + +/** + * Test project with circular dependency between data, and functions + */ +class CircularDepDataFuncTestProject extends TestProjectBase { + readonly sourceProjectDirPath = + '../../src/test-projects/circular-dep-data-func'; + + readonly sourceProjectAmplifyDirSuffix = `${this.sourceProjectDirPath}/amplify`; + + readonly sourceProjectAmplifyDirURL: URL = new URL( + this.sourceProjectAmplifyDirSuffix, + import.meta.url + ); + + /** + * Create a test project instance. + */ + constructor( + name: string, + projectDirPath: string, + projectAmplifyDirPath: string, + cfnClient: CloudFormationClient, + amplifyClient: AmplifyClient + ) { + super( + name, + projectDirPath, + projectAmplifyDirPath, + cfnClient, + amplifyClient + ); + } +} diff --git a/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/auth/resource.ts b/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/auth/resource.ts new file mode 100644 index 00000000000..284ced66562 --- /dev/null +++ b/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/auth/resource.ts @@ -0,0 +1,11 @@ +import { defineAuth } from '@aws-amplify/backend'; +import { preSignUp } from '../functions/pre-sign-up/resource.js'; + +export const auth = defineAuth({ + loginWith: { + email: true, + }, + triggers: { + preSignUp, + }, +}); diff --git a/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/backend.ts b/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/backend.ts new file mode 100644 index 00000000000..57a0b4da492 --- /dev/null +++ b/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/backend.ts @@ -0,0 +1,23 @@ +import { defineBackend } from '@aws-amplify/backend'; +import { auth } from './auth/resource.js'; +import { data } from './data/resource.js'; +import { apiFunction } from './functions/api-function/resource.js'; +import { preSignUp } from './functions/pre-sign-up/resource.js'; +import { DynamoEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; +import { StartingPosition } from 'aws-cdk-lib/aws-lambda'; + +const backend = defineBackend({ + auth, + data, + apiFunction, + preSignUp, +}); + +const eventSource = new DynamoEventSource( + backend.data.resources.tables['Todo'], + { + startingPosition: StartingPosition.LATEST, + } +); + +backend.apiFunction.resources.lambda.addEventSource(eventSource); diff --git a/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/data/resource.ts b/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/data/resource.ts new file mode 100644 index 00000000000..fdee70ae7dc --- /dev/null +++ b/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/data/resource.ts @@ -0,0 +1,25 @@ +import { type ClientSchema, a, defineData } from '@aws-amplify/backend'; + +const schema = a.schema({ + Todo: a + .model({ + content: a.string(), + }) + .authorization((allow) => [allow.publicApiKey()]), +}) as never; // Not 100% sure why TS is complaining here. The error I'm getting is "The inferred type of 'schema' references an inaccessible 'unique symbol' type. A type annotation is necessary." + +// ^ appears to be caused by these 2 rules in tsconfig.base.json: https://github.com/aws-amplify/amplify-backend/blob/8d9a7a4c3033c474b0fc78379cdd4c1854d890ce/tsconfig.base.json#L7-L8 +// Possibly something to do with all the `references` in the nested configs. Using the same tsconfig in a new amplify app doesn't cause the error. + +export type Schema = ClientSchema; + +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: 'apiKey', + // API Key is used for a.allow.public() rules + apiKeyAuthorizationMode: { + expiresInDays: 30, + }, + }, +}); diff --git a/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/functions/api-function/resource.ts b/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/functions/api-function/resource.ts new file mode 100644 index 00000000000..7bf6de291a0 --- /dev/null +++ b/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/functions/api-function/resource.ts @@ -0,0 +1,7 @@ +import { defineFunction } from '@aws-amplify/backend'; + +export const apiFunction = defineFunction({ + name: 'apiFunction', + entry: '../handler.ts', + resourceGroupName: 'data', +}); diff --git a/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/functions/handler.ts b/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/functions/handler.ts new file mode 100644 index 00000000000..ad5a6a9ead9 --- /dev/null +++ b/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/functions/handler.ts @@ -0,0 +1 @@ +export const handler = () => {}; diff --git a/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/functions/pre-sign-up/resource.ts b/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/functions/pre-sign-up/resource.ts new file mode 100644 index 00000000000..d62f7c08be6 --- /dev/null +++ b/packages/integration-tests/src/test-projects/circular-dep-auth-data-func/amplify/functions/pre-sign-up/resource.ts @@ -0,0 +1,7 @@ +import { defineFunction } from '@aws-amplify/backend'; + +export const preSignUp = defineFunction({ + name: 'preSignUp', + entry: '../handler.ts', + resourceGroupName: 'auth', +}); diff --git a/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/auth/resource.ts b/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/auth/resource.ts new file mode 100644 index 00000000000..cd2d8595084 --- /dev/null +++ b/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/auth/resource.ts @@ -0,0 +1,7 @@ +import { defineAuth } from '@aws-amplify/backend'; + +export const auth = defineAuth({ + loginWith: { + email: true, + }, +}); diff --git a/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/backend.ts b/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/backend.ts new file mode 100644 index 00000000000..68068eaff19 --- /dev/null +++ b/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/backend.ts @@ -0,0 +1,23 @@ +import { defineBackend } from '@aws-amplify/backend'; +import { auth } from './auth/resource.js'; +import { data } from './data/resource.js'; +import { apiFunction } from './functions/api-function/resource.js'; +import { DynamoEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; +import { StartingPosition } from 'aws-cdk-lib/aws-lambda'; +import { queryFunction } from './functions/query-function/resource.js'; + +const backend = defineBackend({ + auth, + data, + apiFunction, + queryFunction, +}); + +const eventSource = new DynamoEventSource( + backend.data.resources.tables['Todo'], + { + startingPosition: StartingPosition.LATEST, + } +); + +backend.apiFunction.resources.lambda.addEventSource(eventSource); diff --git a/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/data/resource.ts b/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/data/resource.ts new file mode 100644 index 00000000000..7d121dbf405 --- /dev/null +++ b/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/data/resource.ts @@ -0,0 +1,32 @@ +import { type ClientSchema, a, defineData } from '@aws-amplify/backend'; +import { queryFunction } from '../functions/query-function/resource.js'; + +const schema = a.schema({ + Todo: a + .model({ + content: a.string(), + }) + .authorization((allow) => [allow.publicApiKey()]), + query: a + .query() + .arguments({ content: a.string() }) + .returns(a.string()) + .authorization((allow) => [allow.publicApiKey()]) + .handler(a.handler.function(queryFunction)), +}) as never; // Not 100% sure why TS is complaining here. The error I'm getting is "The inferred type of 'schema' references an inaccessible 'unique symbol' type. A type annotation is necessary." + +// ^ appears to be caused by these 2 rules in tsconfig.base.json: https://github.com/aws-amplify/amplify-backend/blob/8d9a7a4c3033c474b0fc78379cdd4c1854d890ce/tsconfig.base.json#L7-L8 +// Possibly something to do with all the `references` in the nested configs. Using the same tsconfig in a new amplify app doesn't cause the error. + +export type Schema = ClientSchema; + +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: 'apiKey', + // API Key is used for a.allow.public() rules + apiKeyAuthorizationMode: { + expiresInDays: 30, + }, + }, +}); diff --git a/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/functions/api-function/resource.ts b/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/functions/api-function/resource.ts new file mode 100644 index 00000000000..7bf6de291a0 --- /dev/null +++ b/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/functions/api-function/resource.ts @@ -0,0 +1,7 @@ +import { defineFunction } from '@aws-amplify/backend'; + +export const apiFunction = defineFunction({ + name: 'apiFunction', + entry: '../handler.ts', + resourceGroupName: 'data', +}); diff --git a/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/functions/handler.ts b/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/functions/handler.ts new file mode 100644 index 00000000000..ad5a6a9ead9 --- /dev/null +++ b/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/functions/handler.ts @@ -0,0 +1 @@ +export const handler = () => {}; diff --git a/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/functions/query-function/resource.ts b/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/functions/query-function/resource.ts new file mode 100644 index 00000000000..dba0d24f036 --- /dev/null +++ b/packages/integration-tests/src/test-projects/circular-dep-data-func/amplify/functions/query-function/resource.ts @@ -0,0 +1,7 @@ +import { defineFunction } from '@aws-amplify/backend'; + +export const queryFunction = defineFunction({ + name: 'queryFunction', + entry: '../handler.ts', + resourceGroupName: 'data', +}); diff --git a/packages/plugin-types/API.md b/packages/plugin-types/API.md index 5633bd63c65..2b78dd2ae1c 100644 --- a/packages/plugin-types/API.md +++ b/packages/plugin-types/API.md @@ -26,6 +26,11 @@ import { Stack } from 'aws-cdk-lib'; // @public (undocumented) export type AmplifyFunction = ResourceProvider; +// @public +export type AmplifyResourceGroupName = 'auth' | 'data' | 'storage' | (string & { + resourceGroupNameLike?: any; +}); + // @public export type AppId = string; diff --git a/packages/plugin-types/src/amplify_resource_group_name.ts b/packages/plugin-types/src/amplify_resource_group_name.ts new file mode 100644 index 00000000000..45bfbabcd93 --- /dev/null +++ b/packages/plugin-types/src/amplify_resource_group_name.ts @@ -0,0 +1,12 @@ +/** + * Represents the types of resource group name + */ +export type AmplifyResourceGroupName = + | 'auth' + | 'data' + | 'storage' + // eslint-disable-next-line spellcheck/spell-checker + // `(string & { resourceGroupNameLike?: any} )` is a workaround to allow default resource group names to show up in IntelliSense while allowing any string to be passed. + // See https://github.com/microsoft/TypeScript/issues/29729#issuecomment-460346421. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + | (string & { resourceGroupNameLike?: any }); diff --git a/packages/plugin-types/src/index.ts b/packages/plugin-types/src/index.ts index ee50df55a3c..780c52ec02c 100644 --- a/packages/plugin-types/src/index.ts +++ b/packages/plugin-types/src/index.ts @@ -21,3 +21,4 @@ export * from './stable_backend_identifiers.js'; export * from './resource_name_validator.js'; export * from './aws_client_provider.js'; export * from './stack_provider.js'; +export * from './amplify_resource_group_name.js'; From f57a758e527c5485dfc868194238b177caaeb22b Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Fri, 15 Nov 2024 13:27:54 -0800 Subject: [PATCH 109/199] Use different questions when retrying AI use cases. (#2237) --- .changeset/pink-students-allow.md | 2 + packages/integration-tests/src/retry.ts | 4 +- .../conversation_handler_project.ts | 62 ++++++++++++------- 3 files changed, 42 insertions(+), 26 deletions(-) create mode 100644 .changeset/pink-students-allow.md diff --git a/.changeset/pink-students-allow.md b/.changeset/pink-students-allow.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/pink-students-allow.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/retry.ts b/packages/integration-tests/src/retry.ts index e972ca017cb..713d3e37a33 100644 --- a/packages/integration-tests/src/retry.ts +++ b/packages/integration-tests/src/retry.ts @@ -7,7 +7,7 @@ export type RetryPredicate = (error: Error) => boolean; * errors or temporary service unavailability. */ export const runWithRetry = async ( - callable: () => Promise, + callable: (attempt: number) => Promise, retryPredicate: RetryPredicate, maxAttempts = 3 ): Promise => { @@ -15,7 +15,7 @@ export const runWithRetry = async ( for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { - const result = await callable(); + const result = await callable(attempt); return result; } catch (error) { if (error instanceof Error) { diff --git a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts index da7393d79e9..4418a324b83 100644 --- a/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts +++ b/packages/integration-tests/src/test-project-setup/conversation_handler_project.ts @@ -34,6 +34,7 @@ import { resolve } from 'path'; import { fileURLToPath } from 'url'; import * as bedrock from '@aws-sdk/client-bedrock-runtime'; import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; +import { runWithRetry } from '../retry.js'; // TODO: this is a work around // it seems like as of amplify v6 , some of the code only runs in the browser ... @@ -279,13 +280,14 @@ class ConversationHandlerTestProject extends TestProjectBase { ) ); - await this.executeWithRetry(() => + await this.executeWithRetry((attempt) => this.assertCustomConversationHandlerCanExecuteTurnWithParameterLessTool( backendId, authenticatedUserCredentials.accessToken, dataUrl, apolloClient, - true + true, + attempt ) ); @@ -349,23 +351,25 @@ class ConversationHandlerTestProject extends TestProjectBase { ) ); - await this.executeWithRetry(() => + await this.executeWithRetry((attempt) => this.assertDefaultConversationHandlerCanPropagateError( backendId, authenticatedUserCredentials.accessToken, dataUrl, apolloClient, - true + true, + attempt ) ); - await this.executeWithRetry(() => + await this.executeWithRetry((attempt) => this.assertDefaultConversationHandlerCanPropagateError( backendId, authenticatedUserCredentials.accessToken, dataUrl, apolloClient, - false + false, + attempt ) ); } @@ -870,7 +874,8 @@ class ConversationHandlerTestProject extends TestProjectBase { accessToken: string, graphqlApiEndpoint: string, apolloClient: ApolloClient, - streamResponse: boolean + streamResponse: boolean, + attempt: number ): Promise => { const customConversationHandlerFunction = ( await this.resourceFinder.findByBackendIdentifier( @@ -880,13 +885,23 @@ class ConversationHandlerTestProject extends TestProjectBase { ) )[0]; + // Try different questions on retry. + // Retrying same question in narrow time frame usually yields same answer. + const questions = [ + 'Give me a random number', + 'Give me a random number please', + 'Can you please give me a random number', + 'Generate and print random number', + ]; + const question = questions[attempt % questions.length]; + const message: CreateConversationMessageChatInput = { conversationId: randomUUID().toString(), id: randomUUID().toString(), role: 'user', content: [ { - text: 'Give me a random number', + text: question, }, ], }; @@ -919,7 +934,8 @@ class ConversationHandlerTestProject extends TestProjectBase { accessToken: string, graphqlApiEndpoint: string, apolloClient: ApolloClient, - streamResponse: boolean + streamResponse: boolean, + attempt: number ): Promise => { const defaultConversationHandlerFunction = ( await this.resourceFinder.findByBackendIdentifier( @@ -929,13 +945,23 @@ class ConversationHandlerTestProject extends TestProjectBase { ) )[0]; + // Try different questions on retry. + // Retrying same question in narrow time frame usually yields same answer. + const questions = [ + 'What is the value of PI?', + 'Give me the value of PI', + 'Give me the value of PI please', + 'Can you please give me the value of PI?', + ]; + const question = questions[attempt % questions.length]; + const message: CreateConversationMessageChatInput = { id: randomUUID().toString(), conversationId: randomUUID().toString(), role: 'user', content: [ { - text: 'What is the value of PI?', + text: question, }, ], }; @@ -1031,20 +1057,8 @@ class ConversationHandlerTestProject extends TestProjectBase { * Therefore, we wrap transactions in retry loop. */ private executeWithRetry = async ( - callable: () => Promise, - maxAttempts = 3 + callable: (attempt: number) => Promise ) => { - const collectedErrors = []; - for (let i = 1; i <= maxAttempts; i++) { - try { - await callable(); - // if successful return early; - return; - } catch (e) { - collectedErrors.push(e); - } - } - // if we got here there were only errors - throw new AggregateError(collectedErrors); + await runWithRetry(callable, () => true, 4); }; } From 9dcd092a88844a2b7861403e57be82a8e63faf51 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 15 Nov 2024 13:29:37 -0800 Subject: [PATCH 110/199] add cleanup of generated env files to end of tests (#2234) --- .changeset/ninety-trainers-flow.md | 2 ++ packages/backend-function/src/factory.test.ts | 12 +++++++++++- .../src/function_env_translator.test.ts | 12 +++++++++++- .../src/function_env_type_generator.test.ts | 11 ++++++++++- packages/backend-function/src/layer_parser.test.ts | 12 +++++++++++- .../data_storage_auth_with_triggers.test.ts | 10 +++++++++- 6 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 .changeset/ninety-trainers-flow.md diff --git a/.changeset/ninety-trainers-flow.md b/.changeset/ninety-trainers-flow.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/ninety-trainers-flow.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index 127c10c33ce..95624b523bc 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, it, mock } from 'node:test'; +import { after, beforeEach, describe, it, mock } from 'node:test'; import { App, Stack } from 'aws-cdk-lib'; import { ConstructFactoryGetInstanceProps, @@ -17,6 +17,8 @@ import { NodeVersion, defineFunction } from './factory.js'; import { lambdaWithDependencies } from './test-assets/lambda-with-dependencies/resource.js'; import { Runtime } from 'aws-cdk-lib/aws-lambda'; import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; +import fsp from 'fs/promises'; +import path from 'node:path'; const createStackAndSetContext = (): Stack => { const app = new App(); @@ -52,6 +54,14 @@ void describe('AmplifyFunctionFactory', () => { }; }); + after(async () => { + // clean up generated env files + await fsp.rm(path.join(process.cwd(), '.amplify'), { + recursive: true, + force: true, + }); + }); + void it('creates singleton function instance', () => { const functionFactory = defaultLambda; const instance1 = functionFactory.getInstance(getInstanceProps); diff --git a/packages/backend-function/src/function_env_translator.test.ts b/packages/backend-function/src/function_env_translator.test.ts index c20535b61a7..b2f6f96fa53 100644 --- a/packages/backend-function/src/function_env_translator.test.ts +++ b/packages/backend-function/src/function_env_translator.test.ts @@ -1,5 +1,5 @@ import { Construct } from 'constructs'; -import { describe, it } from 'node:test'; +import { after, describe, it } from 'node:test'; import { FunctionEnvironmentTranslator } from './function_env_translator.js'; import { BackendIdentifier, @@ -13,6 +13,8 @@ import { ParameterPathConversions } from '@aws-amplify/platform-core'; import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda'; import { Template } from 'aws-cdk-lib/assertions'; import { FunctionEnvironmentTypeGenerator } from './function_env_type_generator.js'; +import path from 'node:path'; +import fsp from 'fs/promises'; const testStack = {} as Construct; @@ -55,6 +57,14 @@ class TestBackendSecret implements BackendSecret { void describe('FunctionEnvironmentTranslator', () => { const backendResolver = new TestBackendSecretResolver(); + after(async () => { + // clean up generated env files + await fsp.rm(path.join(process.cwd(), '.amplify'), { + recursive: true, + force: true, + }); + }); + void it('translates env props that do not contain secrets', () => { const functionEnvProp = { TEST_VAR: 'testValue', diff --git a/packages/backend-function/src/function_env_type_generator.test.ts b/packages/backend-function/src/function_env_type_generator.test.ts index 15a913479c3..64d5588bce5 100644 --- a/packages/backend-function/src/function_env_type_generator.test.ts +++ b/packages/backend-function/src/function_env_type_generator.test.ts @@ -1,11 +1,20 @@ -import { describe, it, mock } from 'node:test'; +import { after, describe, it, mock } from 'node:test'; import fs from 'fs'; import fsp from 'fs/promises'; import { FunctionEnvironmentTypeGenerator } from './function_env_type_generator.js'; import assert from 'assert'; import { pathToFileURL } from 'url'; +import path from 'path'; void describe('FunctionEnvironmentTypeGenerator', () => { + after(async () => { + // clean up generated env files + await fsp.rm(path.join(process.cwd(), '.amplify'), { + recursive: true, + force: true, + }); + }); + void it('generates a type definition file', () => { const fsOpenSyncMock = mock.method(fs, 'openSync'); const fsWriteFileSyncMock = mock.method(fs, 'writeFileSync', () => null); diff --git a/packages/backend-function/src/layer_parser.test.ts b/packages/backend-function/src/layer_parser.test.ts index 4d1e8ea5cc9..19c3087364a 100644 --- a/packages/backend-function/src/layer_parser.test.ts +++ b/packages/backend-function/src/layer_parser.test.ts @@ -12,8 +12,10 @@ import { import { App, Stack } from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; import assert from 'node:assert'; -import { beforeEach, describe, it } from 'node:test'; +import { after, beforeEach, describe, it } from 'node:test'; import { defineFunction } from './factory.js'; +import path from 'node:path'; +import fsp from 'fs/promises'; const createStackAndSetContext = (): Stack => { const app = new App(); @@ -49,6 +51,14 @@ void describe('AmplifyFunctionFactory - Layers', () => { }; }); + after(async () => { + // clean up generated env files + await fsp.rm(path.join(process.cwd(), '.amplify'), { + recursive: true, + force: true, + }); + }); + void it('sets a valid layer', () => { const layerArn = 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer:1'; const functionFactory = defineFunction({ diff --git a/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts b/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts index a08fc6cc6a8..59a1d17525b 100644 --- a/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts +++ b/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts @@ -4,6 +4,8 @@ import { synthesizeBackendTemplates, } from '../define_backend_template_harness.js'; import { dataStorageAuthWithTriggers } from '../test-projects/data-storage-auth-with-triggers-ts/amplify/test_factories.js'; +import path from 'node:path'; +import fsp from 'fs/promises'; /** * This test suite is meant to provide a fast feedback loop to sanity check that different feature verticals are working properly together. @@ -12,7 +14,7 @@ import { dataStorageAuthWithTriggers } from '../test-projects/data-storage-auth- * Critical path interactions should be exercised in a full e2e test. */ -void it('data storage auth with triggers', () => { +void it('data storage auth with triggers', async () => { const templates = synthesizeBackendTemplates(dataStorageAuthWithTriggers); assertExpectedLogicalIds(templates.root, 'AWS::CloudFormation::Stack', [ @@ -58,4 +60,10 @@ void it('data storage auth with triggers', () => { 'onDeletelambda96BB6F15', ]); /* eslint-enable spellcheck/spell-checker */ + + // clean up generated env files + await fsp.rm(path.join(process.cwd(), '.amplify'), { + recursive: true, + force: true, + }); }); From 8c8fc5ea2d700707d264247a146e1cd740334780 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 18 Nov 2024 12:18:02 -0800 Subject: [PATCH 111/199] Print bootstrap url when browser cannot be opened (#2241) --- .changeset/seven-chefs-trade.md | 5 ++ .../sandbox/src/file_watching_sandbox.test.ts | 47 +++++++++++++++++++ packages/sandbox/src/file_watching_sandbox.ts | 19 +++++++- 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 .changeset/seven-chefs-trade.md diff --git a/.changeset/seven-chefs-trade.md b/.changeset/seven-chefs-trade.md new file mode 100644 index 00000000000..9e36e91d413 --- /dev/null +++ b/.changeset/seven-chefs-trade.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/sandbox': patch +--- + +Print bootstrap url when browser cannot be opened diff --git a/packages/sandbox/src/file_watching_sandbox.test.ts b/packages/sandbox/src/file_watching_sandbox.test.ts index 390d917426a..68e410a56f3 100644 --- a/packages/sandbox/src/file_watching_sandbox.test.ts +++ b/packages/sandbox/src/file_watching_sandbox.test.ts @@ -187,6 +187,53 @@ void describe('Sandbox to check if region is bootstrapped', () => { openMock.mock.calls[0].arguments[0], getBootstrapUrl(region) ); + assert.strictEqual(printer.log.mock.callCount(), 1); + assert.strictEqual( + printer.log.mock.calls[0].arguments[0], + 'The given region has not been bootstrapped. Sign in to console as a Root user or Admin to complete the bootstrap process, then restart the sandbox.' + ); + assert.strictEqual(printer.log.mock.calls[0].arguments[1], undefined); + }); + + void it('when region has not bootstrapped, and opening console url fails prints url to initiate bootstrap', async () => { + ssmClientSendMock.mock.mockImplementationOnce(() => { + throw new ParameterNotFound({ + $metadata: {}, + message: 'Parameter not found', + }); + }); + + openMock.mock.mockImplementationOnce(() => + Promise.reject(new Error('open error')) + ); + + await sandboxInstance.start({ + dir: 'testDir', + exclude: ['exclude1', 'exclude2'], + }); + + assert.strictEqual(ssmClientSendMock.mock.callCount(), 1); + assert.strictEqual(openMock.mock.callCount(), 1); + assert.strictEqual( + openMock.mock.calls[0].arguments[0], + getBootstrapUrl(region) + ); + assert.strictEqual(printer.log.mock.callCount(), 3); + assert.strictEqual( + printer.log.mock.calls[0].arguments[0], + 'The given region has not been bootstrapped. Sign in to console as a Root user or Admin to complete the bootstrap process, then restart the sandbox.' + ); + assert.strictEqual(printer.log.mock.calls[0].arguments[1], undefined); + assert.strictEqual( + printer.log.mock.calls[1].arguments[0], + 'Unable to open bootstrap url, open error' + ); + assert.strictEqual(printer.log.mock.calls[1].arguments[1], LogLevel.DEBUG); + assert.strictEqual( + printer.log.mock.calls[2].arguments[0], + `Open ${getBootstrapUrl(region)} in the browser.` + ); + assert.strictEqual(printer.log.mock.calls[2].arguments[1], undefined); }); void it('when user does not have proper credentials throw user error', async () => { diff --git a/packages/sandbox/src/file_watching_sandbox.ts b/packages/sandbox/src/file_watching_sandbox.ts index 62e9a5cb94b..aa1bb1c6a1e 100644 --- a/packages/sandbox/src/file_watching_sandbox.ts +++ b/packages/sandbox/src/file_watching_sandbox.ts @@ -37,6 +37,7 @@ import { BackendIdentifierConversions, } from '@aws-amplify/platform-core'; import { LambdaFunctionLogStreamer } from './lambda_function_log_streamer.js'; + /** * CDK stores bootstrap version in parameter store. Example parameter name looks like /cdk-bootstrap//version. * The default value for qualifier is hnb659fds, i.e. default parameter path is /cdk-bootstrap/hnb659fds/version. @@ -125,7 +126,23 @@ export class FileWatchingSandbox extends EventEmitter implements Sandbox { ); // get region from an available sdk client; const region = await this.ssmClient.config.region(); - await this.open(getBootstrapUrl(region)); + const bootstrapUrl = getBootstrapUrl(region); + try { + await this.open(bootstrapUrl); + } catch (e) { + // If opening the link fails for any reason we fall back to + // printing the url in the console. + // This might happen: + // - in headless environments + // - if user does not have any app to open URL + // - if browser crashes + let logEntry = 'Unable to open bootstrap url'; + if (e instanceof Error) { + logEntry = `${logEntry}, ${e.message}`; + } + this.printer.log(logEntry, LogLevel.DEBUG); + this.printer.log(`Open ${bootstrapUrl} in the browser.`); + } return; } From 995a84cab3af6300ac56b5876d4c4e861d5483c2 Mon Sep 17 00:00:00 2001 From: Leena <81589006+ShadowCat567@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:10:01 -0500 Subject: [PATCH 112/199] Version bump for Verdaccio (#2244) * bumped cookie version * fixed unintended changes * fixed more issues with package-lock --------- Co-authored-by: Vieltojarvi --- package-lock.json | 478 ++++++++++++++-------------------------------- 1 file changed, 140 insertions(+), 338 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ce770cf49c..99d8ba4a7ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18250,21 +18250,19 @@ "license": "ISC" }, "node_modules/@verdaccio/auth": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/auth/-/auth-8.0.0-next-8.3.tgz", - "integrity": "sha512-x7/gt4R41i5hat5dVT2WfwTeWolSKTo3k8t12ZBhnf+R+L/a79dQ7/sR8JIT6R9A+nkkFn+RiPspH75H7lprZg==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/auth/-/auth-8.0.0-next-8.4.tgz", + "integrity": "sha512-Bv+du+eIMK2/KU2wIjha2FHQrhBT5RuDOVi1nyRyjEyjmJrUt2RWU0Cb7ASxzQy61nkcJ3bs7kuu9dPHK/Z+jw==", "dev": true, - "license": "MIT", "dependencies": { - "@verdaccio/config": "8.0.0-next-8.3", - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/config": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.4", "@verdaccio/loaders": "8.0.0-next-8.3", - "@verdaccio/logger": "8.0.0-next-8.3", "@verdaccio/signature": "8.0.0-next-8.1", - "@verdaccio/utils": "8.1.0-next-8.3", + "@verdaccio/utils": "8.1.0-next-8.4", "debug": "4.3.7", "lodash": "4.17.21", - "verdaccio-htpasswd": "13.0.0-next-8.3" + "verdaccio-htpasswd": "13.0.0-next-8.4" }, "engines": { "node": ">=18" @@ -18275,13 +18273,12 @@ } }, "node_modules/@verdaccio/auth/node_modules/@verdaccio/utils": { - "version": "8.1.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", - "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "version": "8.1.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", + "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", "dev": true, - "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -18299,7 +18296,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -18309,7 +18305,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -18346,14 +18341,13 @@ "license": "MIT" }, "node_modules/@verdaccio/config": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/config/-/config-8.0.0-next-8.3.tgz", - "integrity": "sha512-bxlesiVi7A1GAHurq7RLFAFd67NTySSwtVMw7D1Ku2Q3v6kAF4TLqxKUrOaA14k0Zk4qyRu4OgXzbjDg0oARcQ==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/config/-/config-8.0.0-next-8.4.tgz", + "integrity": "sha512-9CTYYhaO4xrGbiYjLqRy8EMFPm0YL4a7P6ae8Zm4Dx85Dd6i8XqZ7tlU/+a6qf1g/qggYloolU8pcjaLWNDKAQ==", "dev": true, - "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", - "@verdaccio/utils": "8.1.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/utils": "8.1.0-next-8.4", "debug": "4.3.7", "js-yaml": "4.1.0", "lodash": "4.17.21", @@ -18368,13 +18362,12 @@ } }, "node_modules/@verdaccio/config/node_modules/@verdaccio/utils": { - "version": "8.1.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", - "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "version": "8.1.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", + "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", "dev": true, - "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -18391,15 +18384,13 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" + "dev": true }, "node_modules/@verdaccio/config/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -18409,7 +18400,6 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -18422,7 +18412,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -18434,11 +18423,10 @@ } }, "node_modules/@verdaccio/core": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-8.0.0-next-8.3.tgz", - "integrity": "sha512-DPJmWANbpbtJN+cfz5CN4kfAl15F5JCv5qRAIQB9sOTNWjTw26cRpMYHFB9/buBQPuH3mWCjOwE6c+EVlvObLg==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-8.0.0-next-8.4.tgz", + "integrity": "sha512-TCaHwIpr97f4YQkU25E6pk1dbfWTQwYPos1tMb9Q7k6IapoxN0c1s+SyF5FBuMOIfJpTHoWJDo/z7QCouQC3lw==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "8.17.1", "core-js": "3.37.1", @@ -18460,7 +18448,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -18478,7 +18465,6 @@ "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", "dev": true, "hasInstallScript": true, - "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -18488,8 +18474,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@verdaccio/file-locking": { "version": "10.3.1", @@ -18513,7 +18498,6 @@ "resolved": "https://registry.npmjs.org/@verdaccio/loaders/-/loaders-8.0.0-next-8.3.tgz", "integrity": "sha512-7bIOdi+U1xSLRu0s1XxQwrV3zzzFaVaTX7JKFgj2tQvMy9AgzlpjbW1CqaH8OTVEqq03Pwvwj5hQlcvyzCwm1A==", "dev": true, - "license": "MIT", "dependencies": { "debug": "4.3.7", "lodash": "4.17.21" @@ -18583,13 +18567,12 @@ "license": "MIT" }, "node_modules/@verdaccio/logger": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/logger/-/logger-8.0.0-next-8.3.tgz", - "integrity": "sha512-rLjv/W9QfFHT80L1L/xXQwY3DaZ03NIq64/Bb4GHOudZgzl8C5Efe6u1q8Ti+jqXVAKCzEswMNISdq2XIQ+azQ==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/logger/-/logger-8.0.0-next-8.4.tgz", + "integrity": "sha512-zJIFpKYNR/api/mxj5HqJSlEMFh9J4sVKk+3QYlPmppW68beZLLzqwchb5+c/V559lnSrGy5HvDUEGLXvp6reA==", "dev": true, - "license": "MIT", "dependencies": { - "@verdaccio/logger-commons": "8.0.0-next-8.3", + "@verdaccio/logger-commons": "8.0.0-next-8.4", "pino": "9.4.0" }, "engines": { @@ -18601,13 +18584,12 @@ } }, "node_modules/@verdaccio/logger-commons": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/logger-commons/-/logger-commons-8.0.0-next-8.3.tgz", - "integrity": "sha512-eMG0UDh66JcPX8ez57HCpsZ0FE9G0pCZ51Xei1MeCFVgNLkzrEcnwOcEGZhd3Tew79A4wGgjFFFywkrRIIomwg==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/logger-commons/-/logger-commons-8.0.0-next-8.4.tgz", + "integrity": "sha512-neDbq5IIRoidFT4Rv3zH9YydICDCJEybb06BzCGVOzlhZ7F+fBzJH1qlBhAEISfbONugDgfuUQ2jbRCKEkHezQ==", "dev": true, - "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "@verdaccio/logger-prettify": "8.0.0-next-8.1", "colorette": "2.0.20", "debug": "4.3.7" @@ -18625,7 +18607,6 @@ "resolved": "https://registry.npmjs.org/@verdaccio/logger-prettify/-/logger-prettify-8.0.0-next-8.1.tgz", "integrity": "sha512-vLhaGq0q7wtMCcqa0aQY6QOsMNarhTu/l4e6Z8mG/5LUH95GGLsBwpXLnKS94P3deIjsHhc9ycnEmG39txbQ1w==", "dev": true, - "license": "MIT", "dependencies": { "colorette": "2.0.20", "dayjs": "1.11.13", @@ -18642,18 +18623,17 @@ } }, "node_modules/@verdaccio/middleware": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/middleware/-/middleware-8.0.0-next-8.3.tgz", - "integrity": "sha512-yUe4BGA2/o4GnFuKdquU53kI07xsLMKiMvZfj+M0ZFnFCshCqKmXOoSR8hbzQReUm/OZQSLOppliJn68xmXEVQ==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/middleware/-/middleware-8.0.0-next-8.4.tgz", + "integrity": "sha512-tzpfSpeLKUeyTsQ+fvUsokgdh1NrjDJX/oz2ya8wTYSInKAt1Ld9MRzRVSHJwIQc7wfg46zSjpcKZVLA/YkJ6w==", "dev": true, - "license": "MIT", "dependencies": { - "@verdaccio/config": "8.0.0-next-8.3", - "@verdaccio/core": "8.0.0-next-8.3", - "@verdaccio/url": "13.0.0-next-8.3", - "@verdaccio/utils": "8.1.0-next-8.3", + "@verdaccio/config": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/url": "13.0.0-next-8.4", + "@verdaccio/utils": "8.1.0-next-8.4", "debug": "4.3.7", - "express": "4.21.0", + "express": "4.21.1", "express-rate-limit": "5.5.1", "lodash": "4.17.21", "lru-cache": "7.18.3", @@ -18668,13 +18648,12 @@ } }, "node_modules/@verdaccio/middleware/node_modules/@verdaccio/utils": { - "version": "8.1.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", - "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "version": "8.1.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", + "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", "dev": true, - "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -18692,80 +18671,15 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, - "node_modules/@verdaccio/middleware/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@verdaccio/middleware/node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/@verdaccio/middleware/node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, "node_modules/@verdaccio/middleware/node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, - "license": "ISC", "engines": { "node": ">=12" } @@ -18775,7 +18689,6 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, - "license": "MIT", "bin": { "mime": "cli.js" }, @@ -18788,7 +18701,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -18799,19 +18711,11 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@verdaccio/middleware/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, "node_modules/@verdaccio/search-indexer": { - "version": "8.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/search-indexer/-/search-indexer-8.0.0-next-8.1.tgz", - "integrity": "sha512-Mwwg2o9GicZd6uiCbjBk6xZiWAH/O/2NbEkicPZINFIoJKy1NUihS4RexdDjcsxKEBEggGZXCkzHjzhfaZv1Gg==", + "version": "8.0.0-next-8.2", + "resolved": "https://registry.npmjs.org/@verdaccio/search-indexer/-/search-indexer-8.0.0-next-8.2.tgz", + "integrity": "sha512-sWliVN5BkAGbZ3e/GD0CsZMfPJdRMRuN0tEKQFsvEJifxToq5UkfCw6vKaVvhezsTWqb+Rp5y+2d4n5BDOA49w==", "dev": true, - "license": "MIT", "engines": { "node": ">=18" }, @@ -18825,7 +18729,6 @@ "resolved": "https://registry.npmjs.org/@verdaccio/signature/-/signature-8.0.0-next-8.1.tgz", "integrity": "sha512-lHD/Z2FoPQTtDYz6ZlXhj/lrg0SFirHrwCGt/cibl1GlePpx78WPdo03tgAyl0Qf+I35n484/gR1l9eixBQqYw==", "dev": true, - "license": "MIT", "dependencies": { "debug": "4.3.7", "jsonwebtoken": "9.0.2" @@ -18854,15 +18757,14 @@ } }, "node_modules/@verdaccio/tarball": { - "version": "13.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/tarball/-/tarball-13.0.0-next-8.3.tgz", - "integrity": "sha512-jJatpGgiKLmTqyW4WlRpIkldd26rHDj5WTugWqa2Wxa1hVS0b6sZKT/9fEffOvjjAJv69Ued4nH1YCcB2hlhfA==", + "version": "13.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/tarball/-/tarball-13.0.0-next-8.4.tgz", + "integrity": "sha512-zizQwACK+P9sHtArbuW5MJluRpc3lC6bilGTFNc0TLkHbwL73F8wxkKr5VLzWV7H54+sVKMDs1lCnaoHa0ygmw==", "dev": true, - "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", - "@verdaccio/url": "13.0.0-next-8.3", - "@verdaccio/utils": "8.1.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/url": "13.0.0-next-8.4", + "@verdaccio/utils": "8.1.0-next-8.4", "debug": "4.3.7", "gunzip-maybe": "^1.4.2", "lodash": "4.17.21", @@ -18877,13 +18779,12 @@ } }, "node_modules/@verdaccio/tarball/node_modules/@verdaccio/utils": { - "version": "8.1.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", - "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "version": "8.1.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", + "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", "dev": true, - "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -18901,7 +18802,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -18911,7 +18811,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -18923,20 +18822,18 @@ } }, "node_modules/@verdaccio/ui-theme": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-8.0.0-next-8.3.tgz", - "integrity": "sha512-3A5v8bkvK5Bm+ERsZLYPv4vwo49dwfSy5a3WcAQgWde/oc6jytl/5XOZOaKX33mEiLj313k02j9ArhKuY1JjgA==", - "dev": true, - "license": "MIT" + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-8.0.0-next-8.4.tgz", + "integrity": "sha512-j3STxUuIgvn058LqfXWv+SwRi1fQ7HapMSfVKXAhi09+f4zlD2mtvKLth0WbuzU1NqJPTGLAP9ueBf1210C1Hw==", + "dev": true }, "node_modules/@verdaccio/url": { - "version": "13.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/url/-/url-13.0.0-next-8.3.tgz", - "integrity": "sha512-77BkY3j1d1ZPpmBodK2QlRwNP9tn/IneVry4RHX9j+1xNj4clvpn5rObFnVRGeYf2GPZrYZvIdzVP3BjXl0nkA==", + "version": "13.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/url/-/url-13.0.0-next-8.4.tgz", + "integrity": "sha512-Xo+9DUcwYTBV6d0n4vjLAN2k92J33XM/9JNltWM6140oI8lz+VJKiajtejG/hRBi82RioRdWJ0RZDDY6FsbS3Q==", "dev": true, - "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "debug": "4.3.7", "lodash": "4.17.21", "validator": "13.12.0" @@ -20425,16 +20322,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/body-parser/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -20618,11 +20505,10 @@ } }, "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -21347,18 +21233,17 @@ } }, "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz", + "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", "dev": true, - "license": "MIT", "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", + "negotiator": "~0.6.4", "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "engines": { @@ -21382,12 +21267,14 @@ "dev": true, "license": "MIT" }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", "dev": true, - "license": "MIT" + "engines": { + "node": ">= 0.6" + } }, "node_modules/concat-map": { "version": "0.0.1", @@ -28506,16 +28393,6 @@ "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -31047,29 +30924,28 @@ } }, "node_modules/verdaccio": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-6.0.1.tgz", - "integrity": "sha512-fGP5V18Pz3yIDZNZjQrBqRnMr4sDn8fPO7eoZTIX2D7MvqoGELRG88Townq8PhYU5KAPZ2c2OnNRr8SKReO3Ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-6.0.2.tgz", + "integrity": "sha512-XthgJlF1hGW+GR/apRLZ7DQw26XpLI+xjMGb7dhJKxI4Pz2gSiEY1RXP9T9I/rlIBr9Zx6rYOgRk7A9Aeq/kpg==", "dev": true, - "license": "MIT", "dependencies": { "@cypress/request": "3.0.5", - "@verdaccio/auth": "8.0.0-next-8.3", - "@verdaccio/config": "8.0.0-next-8.3", - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/auth": "8.0.0-next-8.4", + "@verdaccio/config": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.4", "@verdaccio/local-storage-legacy": "11.0.2", - "@verdaccio/logger": "8.0.0-next-8.3", - "@verdaccio/middleware": "8.0.0-next-8.3", - "@verdaccio/search-indexer": "8.0.0-next-8.1", + "@verdaccio/logger": "8.0.0-next-8.4", + "@verdaccio/middleware": "8.0.0-next-8.4", + "@verdaccio/search-indexer": "8.0.0-next-8.2", "@verdaccio/signature": "8.0.0-next-8.1", "@verdaccio/streams": "10.2.1", - "@verdaccio/tarball": "13.0.0-next-8.3", - "@verdaccio/ui-theme": "8.0.0-next-8.3", - "@verdaccio/url": "13.0.0-next-8.3", + "@verdaccio/tarball": "13.0.0-next-8.4", + "@verdaccio/ui-theme": "8.0.0-next-8.4", + "@verdaccio/url": "13.0.0-next-8.4", "@verdaccio/utils": "7.0.1-next-8.1", "async": "3.2.6", "clipanion": "4.0.0-rc.4", - "compression": "1.7.4", + "compression": "1.7.5", "cors": "2.8.5", "debug": "4.3.7", "envinfo": "7.14.0", @@ -31088,8 +30964,8 @@ "pkginfo": "0.4.1", "semver": "7.6.3", "validator": "13.12.0", - "verdaccio-audit": "13.0.0-next-8.3", - "verdaccio-htpasswd": "13.0.0-next-8.3" + "verdaccio-audit": "13.0.0-next-8.4", + "verdaccio-htpasswd": "13.0.0-next-8.4" }, "bin": { "verdaccio": "bin/verdaccio" @@ -31103,15 +30979,14 @@ } }, "node_modules/verdaccio-audit": { - "version": "13.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-13.0.0-next-8.3.tgz", - "integrity": "sha512-/mgRfsg+RENtUggcf0xnfPKNJJqidyKING3nyHgS3vABE6CBe4/fWQKs67X4mfCFiIVLf0PiOTFGT8tmwSZubA==", + "version": "13.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-13.0.0-next-8.4.tgz", + "integrity": "sha512-T4yi/46fLngllx5mvFtXsGcW3MxGZZ9IkHYPK1OQw9+Xj9aOuMec2eFdztTRo9SgqZfgblGSY1ESZYy19sQLvw==", "dev": true, - "license": "MIT", "dependencies": { - "@verdaccio/config": "8.0.0-next-8.3", - "@verdaccio/core": "8.0.0-next-8.3", - "express": "4.21.0", + "@verdaccio/config": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.4", + "express": "4.21.1", "https-proxy-agent": "5.0.1", "node-fetch": "cjs" }, @@ -31123,85 +30998,14 @@ "url": "https://opencollective.com/verdaccio" } }, - "node_modules/verdaccio-audit/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/verdaccio-audit/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/verdaccio-audit/node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/verdaccio-audit/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, "node_modules/verdaccio-htpasswd": { - "version": "13.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-13.0.0-next-8.3.tgz", - "integrity": "sha512-Nl2rEEyGHJQ/1/93BE9TxBMRN4tKF/51VYTb+hWDQFhDEDAR20rcvJ4ND0jOIIZljI7Lg/WrCPBh90u5IyPJ5Q==", + "version": "13.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-13.0.0-next-8.4.tgz", + "integrity": "sha512-w0knjKz8SdBGSv0Kt61LoUOCYBZ2/iig3bVbGFWTj4MwCUG6eNewMoQ6nbrk+kyHNFVq75IzT1eIhXDEysx15Q==", "dev": true, - "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", - "@verdaccio/file-locking": "13.0.0-next-8.1", + "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/file-locking": "13.0.0-next-8.2", "apache-md5": "1.1.8", "bcryptjs": "2.4.3", "core-js": "3.37.1", @@ -31218,11 +31022,10 @@ } }, "node_modules/verdaccio-htpasswd/node_modules/@verdaccio/file-locking": { - "version": "13.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/file-locking/-/file-locking-13.0.0-next-8.1.tgz", - "integrity": "sha512-9PhfRKXsWaWJkON/2/jdG5/N+9Kk4UINvbMGuJz0A/PbzIYfVrBhry7fcnjn6hFKxVPTbSOSSztRzLF30nmBFg==", + "version": "13.0.0-next-8.2", + "resolved": "https://registry.npmjs.org/@verdaccio/file-locking/-/file-locking-13.0.0-next-8.2.tgz", + "integrity": "sha512-TcHgN3I/N28WBSvtukpGrJhBljl4jyIXq0vEv94vXAG6nUE3saK+vtgo8PfYA3Ueo88v/1zyAbiZM4uxwojCmQ==", "dev": true, - "license": "MIT", "dependencies": { "lockfile": "1.0.4" }, @@ -31240,7 +31043,6 @@ "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", "dev": true, "hasInstallScript": true, - "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -31815,7 +31617,7 @@ }, "packages/ai-constructs": { "name": "@aws-amplify/ai-constructs", - "version": "0.8.1", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", @@ -31840,12 +31642,12 @@ }, "packages/auth-construct": { "name": "@aws-amplify/auth-construct", - "version": "1.4.0", + "version": "1.5.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.4.0", "@aws-sdk/util-arn-parser": "^3.568.0" }, "peerDependencies": { @@ -31855,12 +31657,12 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.6.2", + "version": "1.7.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-auth": "^1.3.0", - "@aws-amplify/backend-data": "^1.1.7", - "@aws-amplify/backend-function": "^1.7.4", + "@aws-amplify/backend-auth": "^1.4.0", + "@aws-amplify/backend-data": "^1.2.0", + "@aws-amplify/backend-function": "^1.7.5", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/backend-secret": "^1.1.4", @@ -31868,7 +31670,7 @@ "@aws-amplify/client-config": "^1.5.2", "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.4.0", "@aws-sdk/client-amplify": "^3.624.0", "lodash.snakecase": "^4.1.1" }, @@ -31884,10 +31686,10 @@ }, "packages/backend-ai": { "name": "@aws-amplify/backend-ai", - "version": "0.3.5", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.8.0", + "@aws-amplify/ai-constructs": "^1.0.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/data-schema-types": "^1.2.0", @@ -31901,13 +31703,13 @@ }, "packages/backend-auth": { "name": "@aws-amplify/backend-auth", - "version": "1.3.0", + "version": "1.4.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/auth-construct": "^1.4.0", + "@aws-amplify/auth-construct": "^1.5.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.3.1" + "@aws-amplify/plugin-types": "^1.4.0" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", @@ -31924,14 +31726,14 @@ }, "packages/backend-data": { "name": "@aws-amplify/backend-data", - "version": "1.1.7", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/data-construct": "^1.10.1", "@aws-amplify/data-schema-types": "^1.2.0", - "@aws-amplify/plugin-types": "^1.3.1" + "@aws-amplify/plugin-types": "^1.4.0" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", @@ -31945,11 +31747,11 @@ }, "packages/backend-deployer": { "name": "@aws-amplify/backend-deployer", - "version": "1.1.8", + "version": "1.1.9", "license": "Apache-2.0", "dependencies": { "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.4.0", "execa": "^8.0.1", "tsx": "^4.6.1" }, @@ -31960,12 +31762,12 @@ }, "packages/backend-function": { "name": "@aws-amplify/backend-function", - "version": "1.7.4", + "version": "1.7.5", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.4.0", "execa": "^8.0.1" }, "devDependencies": { @@ -32061,19 +31863,19 @@ }, "packages/cli": { "name": "@aws-amplify/backend-cli", - "version": "1.4.1", + "version": "1.4.2", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.8", + "@aws-amplify/backend-deployer": "^1.1.9", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.0", "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/form-generator": "^1.0.3", - "@aws-amplify/model-generator": "^1.0.8", + "@aws-amplify/model-generator": "^1.0.9", "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.4.0", "@aws-amplify/sandbox": "^1.2.5", "@aws-amplify/schema-generator": "^1.2.5", "@aws-sdk/client-amplify": "^3.624.0", @@ -32404,10 +32206,10 @@ "license": "Apache-2.0", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^0.8.0", + "@aws-amplify/ai-constructs": "^1.0.0", "@aws-amplify/auth-construct": "^1.4.0", "@aws-amplify/backend": "^1.6.0", - "@aws-amplify/backend-ai": "^0.3.5", + "@aws-amplify/backend-ai": "^1.0.0", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/data-schema": "^1.0.0", @@ -32461,7 +32263,7 @@ }, "packages/model-generator": { "name": "@aws-amplify/model-generator", - "version": "1.0.8", + "version": "1.0.9", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", @@ -32469,7 +32271,7 @@ "@aws-amplify/graphql-generator": "^0.5.1", "@aws-amplify/graphql-types-generator": "^3.6.0", "@aws-amplify/platform-core": "^1.0.5", - "@aws-amplify/plugin-types": "^1.3.0", + "@aws-amplify/plugin-types": "^1.4.0", "@aws-sdk/client-appsync": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/credential-providers": "^3.624.0", @@ -32515,7 +32317,7 @@ }, "packages/plugin-types": { "name": "@aws-amplify/plugin-types", - "version": "1.3.1", + "version": "1.4.0", "license": "Apache-2.0", "devDependencies": { "execa": "^5.1.1" From 97697a96a3edc3fe7882572b8478968010691cc1 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Tue, 19 Nov 2024 10:59:41 -0800 Subject: [PATCH 113/199] set removal policy for resources to destroy in sandbox deployments (#2242) --- .changeset/hip-yaks-remain.md | 5 +++ .../backend/src/engine/amplify_stack.test.ts | 44 +++++++++++++++++-- packages/backend/src/engine/amplify_stack.ts | 32 ++++++++++++-- .../project_environment_main_stack_creator.ts | 6 +-- 4 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 .changeset/hip-yaks-remain.md diff --git a/.changeset/hip-yaks-remain.md b/.changeset/hip-yaks-remain.md new file mode 100644 index 00000000000..88d758eb9ff --- /dev/null +++ b/.changeset/hip-yaks-remain.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend': patch +--- + +set removal policy for resources to destroy in sandbox deployments diff --git a/packages/backend/src/engine/amplify_stack.test.ts b/packages/backend/src/engine/amplify_stack.test.ts index 707cb7f5373..a24b898f570 100644 --- a/packages/backend/src/engine/amplify_stack.test.ts +++ b/packages/backend/src/engine/amplify_stack.test.ts @@ -4,11 +4,19 @@ import { AmplifyStack } from './amplify_stack.js'; import { Template } from 'aws-cdk-lib/assertions'; import assert from 'node:assert'; import { FederatedPrincipal, Role } from 'aws-cdk-lib/aws-iam'; +import { BackendIdentifier } from '@aws-amplify/plugin-types'; +import { Bucket } from 'aws-cdk-lib/aws-s3'; + +const branchBackendId: BackendIdentifier = { + namespace: 'testId', + name: 'testBranch', + type: 'branch', +}; void describe('AmplifyStack', () => { void it('renames nested stack logical IDs to non-redundant value', () => { const app = new App(); - const rootStack = new AmplifyStack(app, 'test-id'); + const rootStack = new AmplifyStack(app, branchBackendId); new NestedStack(rootStack, 'testName'); const rootStackTemplate = Template.fromStack(rootStack); @@ -23,7 +31,7 @@ void describe('AmplifyStack', () => { void it('allows roles with properly configured cognito trust policies', () => { const app = new App(); - const rootStack = new AmplifyStack(app, 'test-id'); + const rootStack = new AmplifyStack(app, branchBackendId); new Role(rootStack, 'correctRole', { assumedBy: new FederatedPrincipal( 'cognito-identity.amazonaws.com', @@ -43,7 +51,7 @@ void describe('AmplifyStack', () => { void it('throws on roles with cognito trust policy missing amr condition', () => { const app = new App(); - const rootStack = new AmplifyStack(app, 'test-id'); + const rootStack = new AmplifyStack(app, branchBackendId); new Role(rootStack, 'missingAmrCondition', { assumedBy: new FederatedPrincipal( 'cognito-identity.amazonaws.com', @@ -64,7 +72,7 @@ void describe('AmplifyStack', () => { void it('throws on roles with cognito trust policy missing aud condition', () => { const app = new App(); - const rootStack = new AmplifyStack(app, 'test-id'); + const rootStack = new AmplifyStack(app, branchBackendId); new Role(rootStack, 'missingAudCondition', { assumedBy: new FederatedPrincipal( 'cognito-identity.amazonaws.com', @@ -82,4 +90,32 @@ void describe('AmplifyStack', () => { 'Cannot create a Role trust policy with Cognito that does not have a StringEquals condition for cognito-identity.amazonaws.com:aud', }); }); + + void it('keeps default removal policy of retain for resources in branch deployments', () => { + const app = new App(); + const rootStack = new AmplifyStack(app, branchBackendId); + // bucket has default removal policy to retain + new Bucket(rootStack, 'testBucket', { enforceSSL: true }); + const template = Template.fromStack(rootStack); + + template.hasResource('AWS::S3::Bucket', { + DeletionPolicy: 'Retain', + }); + }); + + void it('sets removal policy to destroy for resources in sandbox deployments', () => { + const app = new App(); + const rootStack = new AmplifyStack(app, { + namespace: 'testId', + name: 'testSandbox', + type: 'sandbox', + }); + // bucket has default removal policy to retain + new Bucket(rootStack, 'testBucket', { enforceSSL: true }); + const template = Template.fromStack(rootStack); + + template.hasResource('AWS::S3::Bucket', { + DeletionPolicy: 'Delete', + }); + }); }); diff --git a/packages/backend/src/engine/amplify_stack.ts b/packages/backend/src/engine/amplify_stack.ts index 4c7475658bc..03691563aad 100644 --- a/packages/backend/src/engine/amplify_stack.ts +++ b/packages/backend/src/engine/amplify_stack.ts @@ -1,5 +1,16 @@ -import { AmplifyFault } from '@aws-amplify/platform-core'; -import { Aspects, CfnElement, IAspect, Stack } from 'aws-cdk-lib'; +import { + AmplifyFault, + BackendIdentifierConversions, +} from '@aws-amplify/platform-core'; +import { BackendIdentifier } from '@aws-amplify/plugin-types'; +import { + Aspects, + CfnElement, + CfnResource, + IAspect, + RemovalPolicy, + Stack, +} from 'aws-cdk-lib'; import { Role } from 'aws-cdk-lib/aws-iam'; import { Construct, IConstruct } from 'constructs'; @@ -10,9 +21,13 @@ export class AmplifyStack extends Stack { /** * Default constructor */ - constructor(scope: Construct, id: string) { - super(scope, id); + constructor(scope: Construct, backendId: BackendIdentifier) { + super(scope, BackendIdentifierConversions.toStackName(backendId)); Aspects.of(this).add(new CognitoRoleTrustPolicyValidator()); + + if (backendId.type === 'sandbox') { + Aspects.of(this).add(new SandboxRemovalPolicyDestroyAspect()); + } } /** * Overrides Stack.allocateLogicalId to prevent redundant nested stack logical IDs @@ -93,3 +108,12 @@ class CognitoRoleTrustPolicyValidator implements IAspect { } }; } + +// This aspect sets removal policy of all resources to destroy for sandbox deployments +class SandboxRemovalPolicyDestroyAspect implements IAspect { + visit(node: IConstruct): void { + if (CfnResource.isCfnResource(node)) { + node.applyRemovalPolicy(RemovalPolicy.DESTROY); + } + } +} diff --git a/packages/backend/src/project_environment_main_stack_creator.ts b/packages/backend/src/project_environment_main_stack_creator.ts index fbadb59b576..04e3d091c5c 100644 --- a/packages/backend/src/project_environment_main_stack_creator.ts +++ b/packages/backend/src/project_environment_main_stack_creator.ts @@ -2,7 +2,6 @@ import { BackendIdentifier, MainStackCreator } from '@aws-amplify/plugin-types'; import { Construct } from 'constructs'; import { Stack, Tags } from 'aws-cdk-lib'; import { AmplifyStack } from './engine/amplify_stack.js'; -import { BackendIdentifierConversions } from '@aws-amplify/platform-core'; /** * Creates stacks that are tied to a given project environment via an SSM parameter @@ -22,10 +21,7 @@ export class ProjectEnvironmentMainStackCreator implements MainStackCreator { */ getOrCreateMainStack = (): Stack => { if (this.mainStack === undefined) { - this.mainStack = new AmplifyStack( - this.scope, - BackendIdentifierConversions.toStackName(this.backendId) - ); + this.mainStack = new AmplifyStack(this.scope, this.backendId); } const deploymentType = this.backendId.type; From 71ef3987de3c635cc60f16e477130f06aa3887cf Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Tue, 19 Nov 2024 13:51:13 -0800 Subject: [PATCH 114/199] Report npm user agent (#2248) * Report npm user agent * change field. --- .changeset/dry-falcons-hammer.md | 5 +++++ .../src/usage-data/usage_data.ts | 1 + .../src/usage-data/usage_data_emitter.test.ts | 21 ++++++++++++++++++- .../src/usage-data/usage_data_emitter.ts | 5 ++++- 4 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 .changeset/dry-falcons-hammer.md diff --git a/.changeset/dry-falcons-hammer.md b/.changeset/dry-falcons-hammer.md new file mode 100644 index 00000000000..263fe5b20fc --- /dev/null +++ b/.changeset/dry-falcons-hammer.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/platform-core': patch +--- + +Report npm user agent diff --git a/packages/platform-core/src/usage-data/usage_data.ts b/packages/platform-core/src/usage-data/usage_data.ts index 31190a5ffa2..68773639ed1 100644 --- a/packages/platform-core/src/usage-data/usage_data.ts +++ b/packages/platform-core/src/usage-data/usage_data.ts @@ -16,4 +16,5 @@ export type UsageData = { accountId: string; input: { command: string; plugin: string }; codePathDurations: { platformStartup?: number; totalDuration?: number }; + projectSetting: { editor?: string }; }; diff --git a/packages/platform-core/src/usage-data/usage_data_emitter.test.ts b/packages/platform-core/src/usage-data/usage_data_emitter.test.ts index 00542327021..89c53c5ad49 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter.test.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, mock, test } from 'node:test'; +import { after, afterEach, before, describe, mock, test } from 'node:test'; import assert from 'node:assert'; import { DefaultUsageDataEmitter } from './usage_data_emitter'; import { v4, validate } from 'uuid'; @@ -12,6 +12,9 @@ import { UsageData } from './usage_data'; import isCI from 'is-ci'; import { AmplifyError, AmplifyUserError } from '..'; +const originalNpmUserAgent = process.env.npm_config_user_agent; +const testNpmUserAgent = 'testNpmUserAgent'; + void describe('UsageDataEmitter', () => { let usageDataEmitter: DefaultUsageDataEmitter; @@ -39,6 +42,14 @@ void describe('UsageDataEmitter', () => { mock.method(https, 'request', () => reqMock); + before(() => { + process.env.npm_config_user_agent = testNpmUserAgent; + }); + + after(() => { + process.env.npm_config_user_agent = originalNpmUserAgent; + }); + afterEach(() => { onReqEndMock.mock.resetCalls(); onReqEndMock.mock.restore(); @@ -72,6 +83,10 @@ void describe('UsageDataEmitter', () => { assert.deepStrictEqual(usageDataSent.isCi, isCI); assert.deepStrictEqual(usageDataSent.osPlatform, os.platform()); assert.deepStrictEqual(usageDataSent.osRelease, os.release()); + assert.deepStrictEqual( + usageDataSent.projectSetting.editor, + testNpmUserAgent + ); assert.ok(validate(usageDataSent.sessionUuid)); assert.ok(validate(usageDataSent.installationUuid)); assert.ok(usageDataSent.error == undefined); @@ -105,6 +120,10 @@ void describe('UsageDataEmitter', () => { assert.deepStrictEqual(usageDataSent.isCi, isCI); assert.deepStrictEqual(usageDataSent.osPlatform, os.platform()); assert.deepStrictEqual(usageDataSent.osRelease, os.release()); + assert.deepStrictEqual( + usageDataSent.projectSetting.editor, + testNpmUserAgent + ); assert.ok(validate(usageDataSent.sessionUuid)); assert.ok(validate(usageDataSent.installationUuid)); assert.strictEqual(usageDataSent.error?.message, 'some error message'); diff --git a/packages/platform-core/src/usage-data/usage_data_emitter.ts b/packages/platform-core/src/usage-data/usage_data_emitter.ts index d108dc9fc75..2677c9b4479 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter.ts @@ -64,7 +64,7 @@ export class DefaultUsageDataEmitter implements UsageDataEmitter { metrics?: Record; dimensions?: Record; error?: AmplifyError; - }) => { + }): Promise => { return { accountId: await this.accountIdFetcher.fetch(), sessionUuid: this.sessionUuid, @@ -86,6 +86,9 @@ export class DefaultUsageDataEmitter implements UsageDataEmitter { codePathDurations: this.translateMetricsToUsageData(options.metrics), input: this.translateDimensionsToUsageData(options.dimensions), isCi: isCI, + projectSetting: { + editor: process.env.npm_config_user_agent, + }, }; }; From 38ca68e53a11643d933121714744f52170ba9b72 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 22:06:50 +0000 Subject: [PATCH 115/199] Version Packages (#2236) Co-authored-by: github-actions[bot] --- .changeset/bright-guests-marry.md | 11 ----------- .changeset/dry-falcons-hammer.md | 5 ----- .changeset/hip-yaks-remain.md | 5 ----- .changeset/ninety-trainers-flow.md | 2 -- .changeset/pink-students-allow.md | 2 -- .changeset/seven-chefs-trade.md | 5 ----- packages/backend-ai/CHANGELOG.md | 10 ++++++++++ packages/backend-ai/package.json | 6 +++--- packages/backend-auth/CHANGELOG.md | 8 ++++++++ packages/backend-auth/package.json | 6 +++--- packages/backend-data/CHANGELOG.md | 8 ++++++++ packages/backend-data/package.json | 6 +++--- packages/backend-function/CHANGELOG.md | 11 +++++++++++ packages/backend-function/package.json | 6 +++--- packages/backend-storage/CHANGELOG.md | 8 ++++++++ packages/backend-storage/package.json | 6 +++--- packages/backend/CHANGELOG.md | 18 ++++++++++++++++++ packages/backend/package.json | 14 +++++++------- packages/platform-core/CHANGELOG.md | 8 ++++++++ packages/platform-core/package.json | 4 ++-- packages/plugin-types/CHANGELOG.md | 6 ++++++ packages/plugin-types/package.json | 2 +- packages/sandbox/CHANGELOG.md | 10 ++++++++++ packages/sandbox/package.json | 6 +++--- 24 files changed, 115 insertions(+), 58 deletions(-) delete mode 100644 .changeset/bright-guests-marry.md delete mode 100644 .changeset/dry-falcons-hammer.md delete mode 100644 .changeset/hip-yaks-remain.md delete mode 100644 .changeset/ninety-trainers-flow.md delete mode 100644 .changeset/pink-students-allow.md delete mode 100644 .changeset/seven-chefs-trade.md diff --git a/.changeset/bright-guests-marry.md b/.changeset/bright-guests-marry.md deleted file mode 100644 index 48492708b7e..00000000000 --- a/.changeset/bright-guests-marry.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@aws-amplify/backend-function': minor -'@aws-amplify/backend-storage': patch -'@aws-amplify/plugin-types': minor -'@aws-amplify/backend': minor -'@aws-amplify/backend-auth': patch -'@aws-amplify/backend-data': patch -'@aws-amplify/backend-ai': patch ---- - -add resourceGroupName prop to function diff --git a/.changeset/dry-falcons-hammer.md b/.changeset/dry-falcons-hammer.md deleted file mode 100644 index 263fe5b20fc..00000000000 --- a/.changeset/dry-falcons-hammer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/platform-core': patch ---- - -Report npm user agent diff --git a/.changeset/hip-yaks-remain.md b/.changeset/hip-yaks-remain.md deleted file mode 100644 index 88d758eb9ff..00000000000 --- a/.changeset/hip-yaks-remain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend': patch ---- - -set removal policy for resources to destroy in sandbox deployments diff --git a/.changeset/ninety-trainers-flow.md b/.changeset/ninety-trainers-flow.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/ninety-trainers-flow.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/pink-students-allow.md b/.changeset/pink-students-allow.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/pink-students-allow.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/seven-chefs-trade.md b/.changeset/seven-chefs-trade.md deleted file mode 100644 index 9e36e91d413..00000000000 --- a/.changeset/seven-chefs-trade.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/sandbox': patch ---- - -Print bootstrap url when browser cannot be opened diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index f40ac90b244..86f57f0bfbe 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/backend-ai +## 1.0.1 + +### Patch Changes + +- f1db886: add resourceGroupName prop to function +- Updated dependencies [f1db886] +- Updated dependencies [71ef398] + - @aws-amplify/plugin-types@1.5.0 + - @aws-amplify/platform-core@1.2.1 + ## 1.0.0 ### Major Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index b596dc85037..6699cc19a5d 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "1.0.0", + "version": "1.0.1", "type": "module", "publishConfig": { "access": "public" @@ -26,8 +26,8 @@ "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/data-schema-types": "^1.2.0", - "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.3.1" + "@aws-amplify/platform-core": "^1.2.1", + "@aws-amplify/plugin-types": "^1.5.0" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0", diff --git a/packages/backend-auth/CHANGELOG.md b/packages/backend-auth/CHANGELOG.md index be8dda7109e..712a426ff33 100644 --- a/packages/backend-auth/CHANGELOG.md +++ b/packages/backend-auth/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-auth +## 1.4.1 + +### Patch Changes + +- f1db886: add resourceGroupName prop to function +- Updated dependencies [f1db886] + - @aws-amplify/plugin-types@1.5.0 + ## 1.4.0 ### Minor Changes diff --git a/packages/backend-auth/package.json b/packages/backend-auth/package.json index 670e06dc2fe..43ea82bfdc0 100644 --- a/packages/backend-auth/package.json +++ b/packages/backend-auth/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-auth", - "version": "1.4.0", + "version": "1.4.1", "type": "module", "publishConfig": { "access": "public" @@ -22,11 +22,11 @@ "@aws-amplify/auth-construct": "^1.5.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.4.0" + "@aws-amplify/plugin-types": "^1.5.0" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.0.6", + "@aws-amplify/platform-core": "^1.2.1", "@aws-sdk/client-cognito-identity-provider": "^3.624.0", "@aws-sdk/client-cognito-identity": "^3.624.0", "@types/aws-lambda": "^8.10.119", diff --git a/packages/backend-data/CHANGELOG.md b/packages/backend-data/CHANGELOG.md index 0a9fc39de04..2e415bac267 100644 --- a/packages/backend-data/CHANGELOG.md +++ b/packages/backend-data/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-data +## 1.2.1 + +### Patch Changes + +- f1db886: add resourceGroupName prop to function +- Updated dependencies [f1db886] + - @aws-amplify/plugin-types@1.5.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index 5e0384b04ec..ba3ed7e55a9 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-data", - "version": "1.2.0", + "version": "1.2.1", "type": "module", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "devDependencies": { "@aws-amplify/data-schema": "^1.0.0", "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.2.0" + "@aws-amplify/platform-core": "^1.2.1" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0", @@ -31,7 +31,7 @@ "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/data-construct": "^1.10.1", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/plugin-types": "^1.5.0", "@aws-amplify/data-schema-types": "^1.2.0" } } diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index 24c5e71424e..12240777bd9 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend-function +## 1.8.0 + +### Minor Changes + +- f1db886: add resourceGroupName prop to function + +### Patch Changes + +- Updated dependencies [f1db886] + - @aws-amplify/plugin-types@1.5.0 + ## 1.7.5 ### Patch Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index def869dd46f..412c6be0be9 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.7.5", + "version": "1.8.0", "type": "module", "publishConfig": { "access": "public" @@ -21,12 +21,12 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/plugin-types": "^1.5.0", "execa": "^8.0.1" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.1.0", + "@aws-amplify/platform-core": "^1.2.1", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", "uuid": "^9.0.1" diff --git a/packages/backend-storage/CHANGELOG.md b/packages/backend-storage/CHANGELOG.md index c164598d795..e1f09d6eb53 100644 --- a/packages/backend-storage/CHANGELOG.md +++ b/packages/backend-storage/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-storage +## 1.2.3 + +### Patch Changes + +- f1db886: add resourceGroupName prop to function +- Updated dependencies [f1db886] + - @aws-amplify/plugin-types@1.5.0 + ## 1.2.2 ### Patch Changes diff --git a/packages/backend-storage/package.json b/packages/backend-storage/package.json index e6f193aedce..c56134608f7 100644 --- a/packages/backend-storage/package.json +++ b/packages/backend-storage/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-storage", - "version": "1.2.2", + "version": "1.2.3", "type": "module", "publishConfig": { "access": "public" @@ -21,11 +21,11 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.1", "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.3.1" + "@aws-amplify/plugin-types": "^1.5.0" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.0.6" + "@aws-amplify/platform-core": "^1.2.1" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0", diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index e454739964c..4add5492b28 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,23 @@ # @aws-amplify/backend +## 1.8.0 + +### Minor Changes + +- f1db886: add resourceGroupName prop to function + +### Patch Changes + +- 97697a9: set removal policy for resources to destroy in sandbox deployments +- Updated dependencies [f1db886] +- Updated dependencies [71ef398] + - @aws-amplify/backend-function@1.8.0 + - @aws-amplify/backend-storage@1.2.3 + - @aws-amplify/plugin-types@1.5.0 + - @aws-amplify/backend-auth@1.4.1 + - @aws-amplify/backend-data@1.2.1 + - @aws-amplify/platform-core@1.2.1 + ## 1.7.0 ### Minor Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index f896e7fd01a..b436de0b74e 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.7.0", + "version": "1.8.0", "type": "module", "publishConfig": { "access": "public" @@ -26,16 +26,16 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/data-schema": "^1.0.0", - "@aws-amplify/backend-auth": "^1.4.0", - "@aws-amplify/backend-function": "^1.7.5", - "@aws-amplify/backend-data": "^1.2.0", + "@aws-amplify/backend-auth": "^1.4.1", + "@aws-amplify/backend-function": "^1.8.0", + "@aws-amplify/backend-data": "^1.2.1", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/backend-secret": "^1.1.4", - "@aws-amplify/backend-storage": "^1.2.2", + "@aws-amplify/backend-storage": "^1.2.3", "@aws-amplify/client-config": "^1.5.2", - "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/platform-core": "^1.2.1", + "@aws-amplify/plugin-types": "^1.5.0", "@aws-sdk/client-amplify": "^3.624.0", "lodash.snakecase": "^4.1.1" }, diff --git a/packages/platform-core/CHANGELOG.md b/packages/platform-core/CHANGELOG.md index 81f027b825c..2f8cc589ed0 100644 --- a/packages/platform-core/CHANGELOG.md +++ b/packages/platform-core/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/platform-core +## 1.2.1 + +### Patch Changes + +- 71ef398: Report npm user agent +- Updated dependencies [f1db886] + - @aws-amplify/plugin-types@1.5.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/platform-core/package.json b/packages/platform-core/package.json index c24f9b8382c..ae7d72a0e68 100644 --- a/packages/platform-core/package.json +++ b/packages/platform-core/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/platform-core", - "version": "1.2.0", + "version": "1.2.1", "type": "commonjs", "publishConfig": { "access": "public" @@ -24,7 +24,7 @@ "@types/uuid": "9.0.7" }, "dependencies": { - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.5.0", "@aws-sdk/client-sts": "^3.624.0", "is-ci": "^3.0.1", "lodash.mergewith": "^4.6.2", diff --git a/packages/plugin-types/CHANGELOG.md b/packages/plugin-types/CHANGELOG.md index 670e7dba082..88512aac069 100644 --- a/packages/plugin-types/CHANGELOG.md +++ b/packages/plugin-types/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/plugin-types +## 1.5.0 + +### Minor Changes + +- f1db886: add resourceGroupName prop to function + ## 1.4.0 ### Minor Changes diff --git a/packages/plugin-types/package.json b/packages/plugin-types/package.json index c672d5c9360..d60ad1902cb 100644 --- a/packages/plugin-types/package.json +++ b/packages/plugin-types/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/plugin-types", - "version": "1.4.0", + "version": "1.5.0", "types": "lib/index.d.ts", "type": "commonjs", "publishConfig": { diff --git a/packages/sandbox/CHANGELOG.md b/packages/sandbox/CHANGELOG.md index 904304aaa8f..42bdfd2209b 100644 --- a/packages/sandbox/CHANGELOG.md +++ b/packages/sandbox/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/sandbox +## 1.2.6 + +### Patch Changes + +- 8c8fc5e: Print bootstrap url when browser cannot be opened +- Updated dependencies [f1db886] +- Updated dependencies [71ef398] + - @aws-amplify/plugin-types@1.5.0 + - @aws-amplify/platform-core@1.2.1 + ## 1.2.5 ### Patch Changes diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index d0897748011..ff96f119597 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/sandbox", - "version": "1.2.5", + "version": "1.2.6", "type": "module", "publishConfig": { "access": "public" @@ -24,8 +24,8 @@ "@aws-amplify/cli-core": "^1.2.0", "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/platform-core": "^1.2.1", + "@aws-amplify/plugin-types": "^1.5.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", From 249c0e541dc705e7bdede847139eb57367c89527 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 20 Nov 2024 09:37:47 -0800 Subject: [PATCH 116/199] Handle insufficient disk space errors (#2250) --- .changeset/orange-garlics-reflect.md | 5 +++++ packages/platform-core/API.md | 2 +- .../src/errors/amplify_error.test.ts | 16 ++++++++++++++ .../platform-core/src/errors/amplify_error.ts | 21 +++++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 .changeset/orange-garlics-reflect.md diff --git a/.changeset/orange-garlics-reflect.md b/.changeset/orange-garlics-reflect.md new file mode 100644 index 00000000000..1be521b1521 --- /dev/null +++ b/.changeset/orange-garlics-reflect.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/platform-core': patch +--- + +Handle insufficient disk space errors diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index b0230e19c64..286ae416207 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -21,7 +21,7 @@ export abstract class AmplifyError extends Error { // (undocumented) readonly details?: string; // (undocumented) - static fromError: (error: unknown) => AmplifyError<'UnknownFault' | 'CredentialsError' | 'InvalidCommandInputError' | 'DomainNotFoundError' | 'SyntaxError'>; + static fromError: (error: unknown) => AmplifyError<'UnknownFault' | 'CredentialsError' | 'InsufficientDiskSpaceError' | 'InvalidCommandInputError' | 'DomainNotFoundError' | 'SyntaxError'>; // (undocumented) static fromStderr: (_stderr: string) => AmplifyError | undefined; static isAmplifyError: (error: unknown) => error is AmplifyError; diff --git a/packages/platform-core/src/errors/amplify_error.test.ts b/packages/platform-core/src/errors/amplify_error.test.ts index 2da01c0e280..94980ab2132 100644 --- a/packages/platform-core/src/errors/amplify_error.test.ts +++ b/packages/platform-core/src/errors/amplify_error.test.ts @@ -189,4 +189,20 @@ void describe('AmplifyError.fromError', async () => { `Failed the test for error ${error.message}` ); }); + void it('wraps InsufficientDiskSpaceError in AmplifyUserError', () => { + const insufficientDiskSpaceErrors = [ + new Error( + "ENOSPC: no space left on device, open '/some/path/amplify_outputs.json'" + ), + new Error('npm ERR! code ENOSPC'), + ]; + insufficientDiskSpaceErrors.forEach((error) => { + const actual = AmplifyError.fromError(error); + assert.ok( + AmplifyError.isAmplifyError(actual) && + actual.name === 'InsufficientDiskSpaceError', + `Failed the test for error ${error.message}` + ); + }); + }); }); diff --git a/packages/platform-core/src/errors/amplify_error.ts b/packages/platform-core/src/errors/amplify_error.ts index a61963a3203..9ea73c8ff1d 100644 --- a/packages/platform-core/src/errors/amplify_error.ts +++ b/packages/platform-core/src/errors/amplify_error.ts @@ -123,6 +123,7 @@ export abstract class AmplifyError extends Error { ): AmplifyError< | 'UnknownFault' | 'CredentialsError' + | 'InsufficientDiskSpaceError' | 'InvalidCommandInputError' | 'DomainNotFoundError' | 'SyntaxError' @@ -179,6 +180,17 @@ export abstract class AmplifyError extends Error { error ); } + if (error instanceof Error && isInsufficientDiskSpaceError(error)) { + return new AmplifyUserError( + 'InsufficientDiskSpaceError', + { + message: error.message, + resolution: + 'There appears to be insufficient space on your system to finish. Clear up some disk space and try again.', + }, + error + ); + } return new AmplifyFault( 'UnknownFault', { @@ -220,6 +232,15 @@ const isSyntaxError = (err?: Error): boolean => { return !!err && err.name === 'SyntaxError'; }; +const isInsufficientDiskSpaceError = (err?: Error): boolean => { + return ( + !!err && + ['ENOSPC: no space left on device', 'code ENOSPC'].some((message) => + err.message.includes(message) + ) + ); +}; + /** * Amplify exception classifications */ From d584b96b4f7930dcc88c5225dd5e04197e81e6d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 11:13:59 -0800 Subject: [PATCH 117/199] Bump cross-spawn, @changesets/cli and @changesets/git (#2253) Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) to 7.0.6 and updates ancestor dependencies [cross-spawn](https://github.com/moxystudio/node-cross-spawn), [@changesets/cli](https://github.com/changesets/changesets) and [@changesets/git](https://github.com/changesets/changesets). These dependencies need to be updated together. Updates `cross-spawn` from 7.0.3 to 7.0.6 - [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md) - [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6) Updates `@changesets/cli` from 2.27.8 to 2.27.10 - [Release notes](https://github.com/changesets/changesets/releases) - [Changelog](https://github.com/changesets/changesets/blob/main/docs/modifying-changelog-format.md) - [Commits](https://github.com/changesets/changesets/compare/@changesets/cli@2.27.8...@changesets/cli@2.27.10) Updates `@changesets/git` from 3.0.1 to 3.0.2 - [Release notes](https://github.com/changesets/changesets/releases) - [Changelog](https://github.com/changesets/changesets/blob/main/docs/modifying-changelog-format.md) - [Commits](https://github.com/changesets/changesets/compare/@changesets/git@3.0.1...@changesets/git@3.0.2) --- updated-dependencies: - dependency-name: cross-spawn dependency-type: indirect - dependency-name: "@changesets/cli" dependency-type: direct:development - dependency-name: "@changesets/git" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 258 ++++++++++++++-------------------------------- 1 file changed, 76 insertions(+), 182 deletions(-) diff --git a/package-lock.json b/package-lock.json index 99d8ba4a7ea..25adc2dc0a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13444,15 +13444,14 @@ "license": "MIT" }, "node_modules/@changesets/apply-release-plan": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.0.5.tgz", - "integrity": "sha512-1cWCk+ZshEkSVEZrm2fSj1Gz8sYvxgUL4Q78+1ZZqeqfuevPTPk033/yUZ3df8BKMohkqqHfzj0HOOrG0KtXTw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.0.6.tgz", + "integrity": "sha512-TKhVLtiwtQOgMAC0fCJfmv93faiViKSDqr8oMEqrnNs99gtSC1sZh/aEMS9a+dseU1ESZRCK+ofLgGY7o0fw/Q==", "dev": true, - "license": "MIT", "dependencies": { - "@changesets/config": "^3.0.3", + "@changesets/config": "^3.0.4", "@changesets/get-version-range-type": "^0.4.0", - "@changesets/git": "^3.0.1", + "@changesets/git": "^3.0.2", "@changesets/should-skip-package": "^0.1.1", "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", @@ -13470,7 +13469,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -13485,7 +13483,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, - "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -13495,17 +13492,15 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4.0.0" } }, "node_modules/@changesets/assemble-release-plan": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-6.0.4.tgz", - "integrity": "sha512-nqICnvmrwWj4w2x0fOhVj2QEGdlUuwVAwESrUo5HLzWMI1rE5SWfsr9ln+rDqWB6RQ2ZyaMZHUcU7/IRaUJS+Q==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-6.0.5.tgz", + "integrity": "sha512-IgvBWLNKZd6k4t72MBTBK3nkygi0j3t3zdC1zrfusYo0KpdsvnDjrMM9vPnTCLCMlfNs55jRL4gIMybxa64FCQ==", "dev": true, - "license": "MIT", "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.2", @@ -13526,41 +13521,38 @@ } }, "node_modules/@changesets/cli": { - "version": "2.27.8", - "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.27.8.tgz", - "integrity": "sha512-gZNyh+LdSsI82wBSHLQ3QN5J30P4uHKJ4fXgoGwQxfXwYFTJzDdvIJasZn8rYQtmKhyQuiBj4SSnLuKlxKWq4w==", + "version": "2.27.10", + "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.27.10.tgz", + "integrity": "sha512-PfeXjvs9OfQJV8QSFFHjwHX3QnUL9elPEQ47SgkiwzLgtKGyuikWjrdM+lO9MXzOE22FO9jEGkcs4b+B6D6X0Q==", "dev": true, - "license": "MIT", "dependencies": { - "@changesets/apply-release-plan": "^7.0.5", - "@changesets/assemble-release-plan": "^6.0.4", + "@changesets/apply-release-plan": "^7.0.6", + "@changesets/assemble-release-plan": "^6.0.5", "@changesets/changelog-git": "^0.2.0", - "@changesets/config": "^3.0.3", + "@changesets/config": "^3.0.4", "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.2", - "@changesets/get-release-plan": "^4.0.4", - "@changesets/git": "^3.0.1", + "@changesets/get-release-plan": "^4.0.5", + "@changesets/git": "^3.0.2", "@changesets/logger": "^0.1.1", "@changesets/pre": "^2.0.1", - "@changesets/read": "^0.6.1", + "@changesets/read": "^0.6.2", "@changesets/should-skip-package": "^0.1.1", "@changesets/types": "^6.0.0", "@changesets/write": "^0.3.2", "@manypkg/get-packages": "^1.1.3", - "@types/semver": "^7.5.0", "ansi-colors": "^4.1.3", "ci-info": "^3.7.0", "enquirer": "^2.3.0", "external-editor": "^3.1.0", "fs-extra": "^7.0.1", "mri": "^1.2.0", - "outdent": "^0.5.0", "p-limit": "^2.2.0", "package-manager-detector": "^0.2.0", "picocolors": "^1.1.0", "resolve-from": "^5.0.0", "semver": "^7.5.3", - "spawndamnit": "^2.0.0", + "spawndamnit": "^3.0.1", "term-size": "^2.1.0" }, "bin": { @@ -13603,11 +13595,10 @@ } }, "node_modules/@changesets/config": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@changesets/config/-/config-3.0.3.tgz", - "integrity": "sha512-vqgQZMyIcuIpw9nqFIpTSNyc/wgm/Lu1zKN5vECy74u95Qx/Wa9g27HdgO4NkVAaq+BGA8wUc/qvbvVNs93n6A==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@changesets/config/-/config-3.0.4.tgz", + "integrity": "sha512-+DiIwtEBpvvv1z30f8bbOsUQGuccnZl9KRKMM/LxUHuDu5oEjmN+bJQ1RIBKNJjfYMQn8RZzoPiX0UgPaLQyXw==", "dev": true, - "license": "MIT", "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.2", @@ -13615,7 +13606,7 @@ "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1", - "micromatch": "^4.0.2" + "micromatch": "^4.0.8" } }, "node_modules/@changesets/config/node_modules/fs-extra": { @@ -13677,16 +13668,15 @@ } }, "node_modules/@changesets/get-release-plan": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-4.0.4.tgz", - "integrity": "sha512-SicG/S67JmPTrdcc9Vpu0wSQt7IiuN0dc8iR5VScnnTVPfIaLvKmEGRvIaF0kcn8u5ZqLbormZNTO77bCEvyWw==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-4.0.5.tgz", + "integrity": "sha512-E6wW7JoSMcctdVakut0UB76FrrN3KIeJSXvB+DHMFo99CnC3ZVnNYDCVNClMlqAhYGmLmAj77QfApaI3ca4Fkw==", "dev": true, - "license": "MIT", "dependencies": { - "@changesets/assemble-release-plan": "^6.0.4", - "@changesets/config": "^3.0.3", + "@changesets/assemble-release-plan": "^6.0.5", + "@changesets/config": "^3.0.4", "@changesets/pre": "^2.0.1", - "@changesets/read": "^0.6.1", + "@changesets/read": "^0.6.2", "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3" } @@ -13695,21 +13685,19 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/@changesets/get-version-range-type/-/get-version-range-type-0.4.0.tgz", "integrity": "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@changesets/git": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@changesets/git/-/git-3.0.1.tgz", - "integrity": "sha512-pdgHcYBLCPcLd82aRcuO0kxCDbw/yISlOtkmwmE8Odo1L6hSiZrBOsRl84eYG7DRCab/iHnOkWqExqc4wxk2LQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@changesets/git/-/git-3.0.2.tgz", + "integrity": "sha512-r1/Kju9Y8OxRRdvna+nxpQIsMsRQn9dhhAZt94FLDeu0Hij2hnOozW8iqnHBgvu+KdnJppCveQwK4odwfw/aWQ==", "dev": true, - "license": "MIT", "dependencies": { "@changesets/errors": "^0.2.0", "@manypkg/get-packages": "^1.1.3", "is-subdir": "^1.1.1", - "micromatch": "^4.0.2", - "spawndamnit": "^2.0.0" + "micromatch": "^4.0.8", + "spawndamnit": "^3.0.1" } }, "node_modules/@changesets/logger": { @@ -13727,7 +13715,6 @@ "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-0.4.0.tgz", "integrity": "sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==", "dev": true, - "license": "MIT", "dependencies": { "@changesets/types": "^6.0.0", "js-yaml": "^3.13.1" @@ -13782,13 +13769,12 @@ } }, "node_modules/@changesets/read": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.6.1.tgz", - "integrity": "sha512-jYMbyXQk3nwP25nRzQQGa1nKLY0KfoOV7VLgwucI0bUO8t8ZLCr6LZmgjXsiKuRDc+5A6doKPr9w2d+FEJ55zQ==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.6.2.tgz", + "integrity": "sha512-wjfQpJvryY3zD61p8jR87mJdyx2FIhEcdXhKUqkja87toMrP/3jtg/Yg29upN+N4Ckf525/uvV7a4tzBlpk6gg==", "dev": true, - "license": "MIT", "dependencies": { - "@changesets/git": "^3.0.1", + "@changesets/git": "^3.0.2", "@changesets/logger": "^0.1.1", "@changesets/parse": "^0.4.0", "@changesets/types": "^6.0.0", @@ -13802,7 +13788,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -13817,7 +13802,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, - "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -13827,7 +13811,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4.0.0" } @@ -20280,7 +20263,6 @@ "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", "dev": true, - "license": "MIT", "dependencies": { "is-windows": "^1.0.0" }, @@ -21440,10 +21422,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "license": "MIT", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -21836,7 +21817,6 @@ "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -25319,7 +25299,6 @@ "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", "integrity": "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==", "dev": true, - "license": "MIT", "dependencies": { "better-path-resolve": "1.0.0" }, @@ -26264,8 +26243,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/log-symbols": { "version": "3.0.0", @@ -27443,15 +27421,13 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz", "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/p-filter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", "dev": true, - "license": "MIT", "dependencies": { "p-map": "^2.0.0" }, @@ -27491,7 +27467,6 @@ "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -28254,13 +28229,6 @@ "node": ">= 0.10" } }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true, - "license": "ISC" - }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -29436,89 +29404,15 @@ } }, "node_modules/spawndamnit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-2.0.0.tgz", - "integrity": "sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "node_modules/spawndamnit/node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "node_modules/spawndamnit/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "license": "ISC", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/spawndamnit/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spawndamnit/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spawndamnit/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/spawndamnit/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-3.0.1.tgz", + "integrity": "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==", "dev": true, - "license": "ISC", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" + "cross-spawn": "^7.0.5", + "signal-exit": "^4.0.1" } }, - "node_modules/spawndamnit/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true, - "license": "ISC" - }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -31657,20 +31551,20 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.7.0", + "version": "1.8.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-auth": "^1.4.0", - "@aws-amplify/backend-data": "^1.2.0", - "@aws-amplify/backend-function": "^1.7.5", + "@aws-amplify/backend-auth": "^1.4.1", + "@aws-amplify/backend-data": "^1.2.1", + "@aws-amplify/backend-function": "^1.8.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/backend-secret": "^1.1.4", - "@aws-amplify/backend-storage": "^1.2.2", + "@aws-amplify/backend-storage": "^1.2.3", "@aws-amplify/client-config": "^1.5.2", "@aws-amplify/data-schema": "^1.0.0", - "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/platform-core": "^1.2.1", + "@aws-amplify/plugin-types": "^1.5.0", "@aws-sdk/client-amplify": "^3.624.0", "lodash.snakecase": "^4.1.1" }, @@ -31686,15 +31580,15 @@ }, "packages/backend-ai": { "name": "@aws-amplify/backend-ai", - "version": "1.0.0", + "version": "1.0.1", "license": "Apache-2.0", "dependencies": { "@aws-amplify/ai-constructs": "^1.0.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/data-schema-types": "^1.2.0", - "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.3.1" + "@aws-amplify/platform-core": "^1.2.1", + "@aws-amplify/plugin-types": "^1.5.0" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0", @@ -31703,17 +31597,17 @@ }, "packages/backend-auth": { "name": "@aws-amplify/backend-auth", - "version": "1.4.0", + "version": "1.4.1", "license": "Apache-2.0", "dependencies": { "@aws-amplify/auth-construct": "^1.5.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.4.0" + "@aws-amplify/plugin-types": "^1.5.0" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.0.6", + "@aws-amplify/platform-core": "^1.2.1", "@aws-sdk/client-cognito-identity": "^3.624.0", "@aws-sdk/client-cognito-identity-provider": "^3.624.0", "@types/aws-lambda": "^8.10.119", @@ -31726,19 +31620,19 @@ }, "packages/backend-data": { "name": "@aws-amplify/backend-data", - "version": "1.2.0", + "version": "1.2.1", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/data-construct": "^1.10.1", "@aws-amplify/data-schema-types": "^1.2.0", - "@aws-amplify/plugin-types": "^1.4.0" + "@aws-amplify/plugin-types": "^1.5.0" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", "@aws-amplify/data-schema": "^1.0.0", - "@aws-amplify/platform-core": "^1.2.0" + "@aws-amplify/platform-core": "^1.2.1" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0", @@ -31762,17 +31656,17 @@ }, "packages/backend-function": { "name": "@aws-amplify/backend-function", - "version": "1.7.5", + "version": "1.8.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/plugin-types": "^1.5.0", "execa": "^8.0.1" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.1.0", + "@aws-amplify/platform-core": "^1.2.1", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", "uuid": "^9.0.1" @@ -31845,16 +31739,16 @@ }, "packages/backend-storage": { "name": "@aws-amplify/backend-storage", - "version": "1.2.2", + "version": "1.2.3", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.1", "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.3.1" + "@aws-amplify/plugin-types": "^1.5.0" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.0.6" + "@aws-amplify/platform-core": "^1.2.1" }, "peerDependencies": { "aws-cdk-lib": "^2.158.0", @@ -32285,10 +32179,10 @@ }, "packages/platform-core": { "name": "@aws-amplify/platform-core", - "version": "1.2.0", + "version": "1.2.1", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.2.1", + "@aws-amplify/plugin-types": "^1.5.0", "@aws-sdk/client-sts": "^3.624.0", "is-ci": "^3.0.1", "lodash.mergewith": "^4.6.2", @@ -32317,7 +32211,7 @@ }, "packages/plugin-types": { "name": "@aws-amplify/plugin-types", - "version": "1.4.0", + "version": "1.5.0", "license": "Apache-2.0", "devDependencies": { "execa": "^5.1.1" @@ -32446,7 +32340,7 @@ }, "packages/sandbox": { "name": "@aws-amplify/sandbox", - "version": "1.2.5", + "version": "1.2.6", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-deployer": "^1.1.8", @@ -32454,8 +32348,8 @@ "@aws-amplify/cli-core": "^1.2.0", "@aws-amplify/client-config": "^1.5.1", "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/platform-core": "^1.2.1", + "@aws-amplify/plugin-types": "^1.5.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", From 01145492efe60861adf775b1644a4f5c865c411c Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 20 Nov 2024 11:31:39 -0800 Subject: [PATCH 118/199] Handle invalid package.json error (#2254) --- .changeset/warm-garlics-raise.md | 5 +++++ .../backend-deployer/src/cdk_error_mapper.test.ts | 15 ++++++++++++++- packages/backend-deployer/src/cdk_error_mapper.ts | 10 ++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .changeset/warm-garlics-raise.md diff --git a/.changeset/warm-garlics-raise.md b/.changeset/warm-garlics-raise.md new file mode 100644 index 00000000000..f30209ff8eb --- /dev/null +++ b/.changeset/warm-garlics-raise.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +Handle invalid package.json error diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index f9e8524aaba..8ad97ec0487 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -386,6 +386,19 @@ const testErrorMappings = [ errorName: 'GetLambdaLayerVersionError', expectedDownstreamErrorMessage: undefined, }, + { + // eslint-disable-next-line spellcheck/spell-checker + errorMessage: `Error: npm error code EJSONPARSE +npm error path /home/some-path/package.json +npm error JSON.parse Expected double-quoted property name in JSON at position 868 while parsing near ...sbuild\\: \\^0.20.2\\,\\n<<<<<<< HEAD\\n\\t\\t\\hl-j... +npm error JSON.parse Failed to parse JSON data. +npm error JSON.parse Note: package.json must be actual JSON, not just JavaScript. +npm error A complete log of this run can be found in: /home/some-path/.npm/_logs/2024-10-01T19_56_46_705Z-debug-0.log`, + expectedTopLevelErrorMessage: + 'The /home/some-path/package.json is not a valid JSON.', + errorName: 'InvalidPackageJsonError', + expectedDownstreamErrorMessage: undefined, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { @@ -401,8 +414,8 @@ void describe('invokeCDKCommand', { concurrency: 1 }, () => { const humanReadableError = cdkErrorMapper.getAmplifyError( new Error(errorMessage) ); - assert.equal(humanReadableError.message, expectedTopLevelErrorMessage); assert.equal(humanReadableError.name, expectedErrorName); + assert.equal(humanReadableError.message, expectedTopLevelErrorMessage); expectedDownstreamErrorMessage && assert.equal( humanReadableError.cause?.message, diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 9f109318d59..474e145fcd1 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -278,6 +278,15 @@ export class CdkErrorMapper { errorName: 'CFNUpdateNotSupportedError', classification: 'ERROR', }, + { + errorRegex: new RegExp( + `npm error code EJSONPARSE${this.multiLineEolRegex}npm error path (?.*/package\\.json)${this.multiLineEolRegex}(npm error (.*)${this.multiLineEolRegex})*` + ), + humanReadableErrorMessage: 'The {filePath} is not a valid JSON.', + resolutionMessage: `Check package.json file and make sure it is a valid JSON.`, + errorName: 'InvalidPackageJsonError', + classification: 'ERROR', + }, { // Error: .* is printed to stderr during cdk synth // Also extracts the first line in the stack where the error happened @@ -368,6 +377,7 @@ export type CDKDeploymentError = | 'ExpiredTokenError' | 'FileConventionError' | 'ModuleNotFoundError' + | 'InvalidPackageJsonError' | 'SecretNotSetError' | 'SyntaxError' | 'GetLambdaLayerVersionError'; From b6f4c546ece2670256fb2c2a3de8b265db961096 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Wed, 20 Nov 2024 17:49:33 -0800 Subject: [PATCH 119/199] handle not authorized to perform on resource error (#2258) * handle not authorized to perform on resource error * add resource to resolution --- .changeset/slimy-sheep-wonder.md | 5 +++++ .../src/cdk_error_mapper.test.ts | 7 +++++++ .../backend-deployer/src/cdk_error_mapper.ts | 18 +++++++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .changeset/slimy-sheep-wonder.md diff --git a/.changeset/slimy-sheep-wonder.md b/.changeset/slimy-sheep-wonder.md new file mode 100644 index 00000000000..1c79787ba61 --- /dev/null +++ b/.changeset/slimy-sheep-wonder.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +handle not authorized to perform on resource error diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 8ad97ec0487..94649149e10 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -399,6 +399,13 @@ npm error A complete log of this run can be found in: /home/some-path/.npm/_logs errorName: 'InvalidPackageJsonError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: `Error: some-stack failed: ValidationError: User: is not authorized to perform: ssm:GetParameters on resource: because no identity-based policy allows the ssm:GetParameters action`, + expectedTopLevelErrorMessage: + 'Unable to deploy due to insufficient permissions', + errorName: 'AccessDeniedError', + expectedDownstreamErrorMessage: undefined, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 474e145fcd1..909457d5011 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -40,12 +40,18 @@ export class CdkErrorMapper { if (matchGroups.groups) { for (const [key, value] of Object.entries(matchGroups.groups)) { const placeHolder = `{${key}}`; - if (matchingError.humanReadableErrorMessage.includes(placeHolder)) { + if ( + matchingError.humanReadableErrorMessage.includes(placeHolder) || + matchingError.resolutionMessage.includes(placeHolder) + ) { matchingError.humanReadableErrorMessage = matchingError.humanReadableErrorMessage.replace( placeHolder, value ); + + matchingError.resolutionMessage = + matchingError.resolutionMessage.replace(placeHolder, value); // reset the stderr dump in the underlying error underlyingError = undefined; } @@ -205,6 +211,16 @@ export class CdkErrorMapper { errorName: 'GetLambdaLayerVersionError', classification: 'ERROR', }, + { + errorRegex: + /User:(.*) is not authorized to perform:(.*) on resource:(?.*) because no identity-based policy allows the (?.*) action/, + humanReadableErrorMessage: + 'Unable to deploy due to insufficient permissions', + resolutionMessage: + 'Ensure you have permissions to call {action} for {resource}', + errorName: 'AccessDeniedError', + classification: 'ERROR', + }, { // Also extracts the first line in the stack where the error happened errorRegex: new RegExp( From 691e7caf07ee971336ed24bd7abb92abbf7f5789 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:13:17 +0100 Subject: [PATCH 120/199] fix: truncate large error messages before printing to customer (#2260) * fix: truncate large error messages before printing to customer * PR Updates --- .changeset/rude-wasps-look.md | 5 +++++ packages/backend-deployer/src/cdk_deployer.ts | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 .changeset/rude-wasps-look.md diff --git a/.changeset/rude-wasps-look.md b/.changeset/rude-wasps-look.md new file mode 100644 index 00000000000..dee6f59b718 --- /dev/null +++ b/.changeset/rude-wasps-look.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +truncate large error messages before printing to customer diff --git a/packages/backend-deployer/src/cdk_deployer.ts b/packages/backend-deployer/src/cdk_deployer.ts index 0771673117c..4989b126015 100644 --- a/packages/backend-deployer/src/cdk_deployer.ts +++ b/packages/backend-deployer/src/cdk_deployer.ts @@ -199,14 +199,24 @@ export class CDKDeployer implements BackendDeployer { // However if the cdk process didn't run or produced no output, then we have nothing to go on with. So we throw // this error to aid in some debugging. if (aggregatedStderr.trim()) { + // If the string is more than 65KB, truncate and keep the last portion. // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors - throw new Error(aggregatedStderr); + throw new Error(this.truncateString(aggregatedStderr, 65000)); } else { throw error; } } }; + private truncateString = (str: string, size: number) => { + const encoder = new TextEncoder(); + const decoder = new TextDecoder(); + const encoded = encoder.encode(str); + return encoded.byteLength > size + ? '...truncated...' + decoder.decode(encoded.slice(-size)) + : str; + }; + private getAppCommand = () => this.packageManagerController.getCommand([ 'tsx', From a23f6560a8fefeb90999db9fc0eb897d423426ef Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Thu, 21 Nov 2024 15:57:25 -0800 Subject: [PATCH 121/199] add more forms of transform errors to cdk error mapping (#2263) * add more forms of transform errors to cdk error mapping * Update packages/backend-deployer/src/cdk_error_mapper.ts Co-authored-by: Kamil Sobol --------- Co-authored-by: Kamil Sobol --- .changeset/great-otters-study.md | 5 + .../src/cdk_error_mapper.test.ts | 100 ++++++++++++++++++ .../backend-deployer/src/cdk_error_mapper.ts | 13 ++- 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 .changeset/great-otters-study.md diff --git a/.changeset/great-otters-study.md b/.changeset/great-otters-study.md new file mode 100644 index 00000000000..67954f92cac --- /dev/null +++ b/.changeset/great-otters-study.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +add more forms of transform errors to cdk error mapping diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 94649149e10..36237639510 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -406,6 +406,106 @@ npm error A complete log of this run can be found in: /home/some-path/.npm/_logs errorName: 'AccessDeniedError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: + `Error: Transform failed with 1 error:` + + EOL + + `/Users/some-path/amplify/storage/resource.ts:1:2: ERROR: Expected identifier but found }` + + EOL + + `at failureErrorWithLog (/Users/some-path/esbuild/lib/main.js:123:45)` + + EOL + + `at /Users/some-path/esbuild/lib/main.js:678:90`, + expectedTopLevelErrorMessage: + '/Users/some-path/amplify/storage/resource.ts:1:2: ERROR: Expected identifier but found }', + errorName: 'ESBuildError', + expectedDownstreamErrorMessage: undefined, + }, + { + errorMessage: + `Error [TransformError]:` + + EOL + + `You installed esbuild for another platform than the one you're currently using. + This won't work because esbuild is written with native code and needs to + install a platform-specific binary executable.` + + EOL + + `Specifically the @esbuild/linux-arm64 package is present but this platform + needs the @esbuild/darwin-arm64 package instead. People often get into this + situation by installing esbuild on Windows or macOS and copying node_modules + into a Docker image that runs Linux, or by copying node_modules between + Windows and WSL environments.` + + EOL + + `If you are installing with npm, you can try not copying the node_modules + directory when you copy the files over, and running npm ci or npm install + on the destination platform after the copy. Or you could consider using yarn + instead of npm which has built-in support for installing a package on multiple + platforms simultaneously.` + + EOL + + `If you are installing with yarn, you can try listing both this platform and the + other platform in your .yarnrc.yml file using the supportedArchitectures + feature: https://yarnpkg.com/configuration/yarnrc/#supportedArchitectures + Keep in mind that this means multiple copies of esbuild will be present.` + + EOL + + // eslint-disable-next-line spellcheck/spell-checker + `Another alternative is to use the esbuild-wasm package instead, which works + the same way on all platforms. But it comes with a heavy performance cost and + can sometimes be 10x slower than the esbuild package, so you may also not want to do that.`, + expectedTopLevelErrorMessage: + `You installed esbuild for another platform than the one you're currently using. + This won't work because esbuild is written with native code and needs to + install a platform-specific binary executable.` + + EOL + + `Specifically the @esbuild/linux-arm64 package is present but this platform + needs the @esbuild/darwin-arm64 package instead. People often get into this + situation by installing esbuild on Windows or macOS and copying node_modules + into a Docker image that runs Linux, or by copying node_modules between + Windows and WSL environments.` + + EOL + + `If you are installing with npm, you can try not copying the node_modules + directory when you copy the files over, and running npm ci or npm install + on the destination platform after the copy. Or you could consider using yarn + instead of npm which has built-in support for installing a package on multiple + platforms simultaneously.` + + EOL + + `If you are installing with yarn, you can try listing both this platform and the + other platform in your .yarnrc.yml file using the supportedArchitectures + feature: https://yarnpkg.com/configuration/yarnrc/#supportedArchitectures + Keep in mind that this means multiple copies of esbuild will be present.` + + EOL + + // eslint-disable-next-line spellcheck/spell-checker + `Another alternative is to use the esbuild-wasm package instead, which works + the same way on all platforms. But it comes with a heavy performance cost and + can sometimes be 10x slower than the esbuild package, so you may also not want to do that.`, + errorName: 'ESBuildError', + expectedDownstreamErrorMessage: undefined, + }, + { + errorMessage: + `Error [TransformError]: The package esbuild-package could not be found, and is needed by esbuild.` + + EOL + + `If you are installing esbuild with npm, make sure that you don't specify the +--no-optional or --omit=optional flags. The optionalDependencies feature +of package.json is used by esbuild to install the correct binary executable +for your current platform. +` + + EOL + + `at generateBinPath (/Users/some-path/esbuild/lib/main.js:123:45)` + + EOL + + `at /Users/some-path/esbuild/lib/main.js:678:90`, + expectedTopLevelErrorMessage: + `The package esbuild-package could not be found, and is needed by esbuild.` + + EOL + + `If you are installing esbuild with npm, make sure that you don't specify the +--no-optional or --omit=optional flags. The optionalDependencies feature +of package.json is used by esbuild to install the correct binary executable +for your current platform. +` + + EOL + + `at generateBinPath (/Users/some-path/esbuild/lib/main.js:123:45)` + + EOL + + `at /Users/some-path/esbuild/lib/main.js:678:90`, + errorName: 'ESBuildError', + expectedDownstreamErrorMessage: undefined, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 909457d5011..0b41aff2198 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -248,7 +248,7 @@ export class CdkErrorMapper { { // If there are multiple errors, capture all lines containing the errors errorRegex: new RegExp( - `\\[TransformError\\]: Transform failed with .* error(s?):${this.multiLineEolRegex}(?(.*ERROR:.*${this.multiLineEolRegex})+)` + `(\\[TransformError\\]|Error): Transform failed with .* error(s?):${this.multiLineEolRegex}(?(.*ERROR:.*${this.multiLineEolRegex})+)` ), humanReadableErrorMessage: '{esBuildErrorMessage}', resolutionMessage: @@ -256,6 +256,17 @@ export class CdkErrorMapper { errorName: 'ESBuildError', classification: 'ERROR', }, + { + // Captures other forms of transform error + errorRegex: new RegExp( + `Error \\[TransformError\\]:(${this.multiLineEolRegex}|\\s)?(?(.*(${this.multiLineEolRegex})?)+)` + ), + humanReadableErrorMessage: '{esBuildErrorMessage}', + resolutionMessage: + 'Make sure esbuild is installed and is compatible with the platform you are currently using.', + errorName: 'ESBuildError', + classification: 'ERROR', + }, { errorRegex: /Amplify Backend not found in/, humanReadableErrorMessage: From 754d0f79d991736adb7543f1d7327163a4923f09 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Mon, 25 Nov 2024 09:27:42 +0100 Subject: [PATCH 122/199] fix: map another form of access denied validation error (#2261) * fix: map another form of access denied validation error * Update .changeset/healthy-trains-doubt.md Co-authored-by: Roshane Pascual --------- Co-authored-by: Roshane Pascual --- .changeset/healthy-trains-doubt.md | 5 +++++ packages/backend-deployer/src/cdk_error_mapper.test.ts | 7 +++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 10 ++++++++++ 3 files changed, 22 insertions(+) create mode 100644 .changeset/healthy-trains-doubt.md diff --git a/.changeset/healthy-trains-doubt.md b/.changeset/healthy-trains-doubt.md new file mode 100644 index 00000000000..8dca0dd4fc4 --- /dev/null +++ b/.changeset/healthy-trains-doubt.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +map another form of access denied validation error diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 36237639510..64b5aebb6aa 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -406,6 +406,13 @@ npm error A complete log of this run can be found in: /home/some-path/.npm/_logs errorName: 'AccessDeniedError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: `User: some:great:user is not authorized to perform: appsync:StartSchemaCreation on resource: arn:aws:appsync:us-east-1:235494812930:/v1/api/myApi`, + expectedTopLevelErrorMessage: + 'Unable to deploy due to insufficient permissions', + errorName: 'AccessDeniedError', + expectedDownstreamErrorMessage: undefined, + }, { errorMessage: `Error: Transform failed with 1 error:` + diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 0b41aff2198..b57ed16ccc2 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -221,6 +221,16 @@ export class CdkErrorMapper { errorName: 'AccessDeniedError', classification: 'ERROR', }, + { + errorRegex: + /User:(.*) is not authorized to perform:(?.*) on resource:(?.*)/, + humanReadableErrorMessage: + 'Unable to deploy due to insufficient permissions', + resolutionMessage: + 'Ensure you have permissions to call {action} for {resource}', + errorName: 'AccessDeniedError', + classification: 'ERROR', + }, { // Also extracts the first line in the stack where the error happened errorRegex: new RegExp( From 716f844cf1571c393c35d9a8cd1f8c66426c43f8 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 25 Nov 2024 10:50:58 -0800 Subject: [PATCH 123/199] Handle ENOENT error from npm (#2267) --- .changeset/nasty-jars-peel.md | 5 ++ .../src/cdk_error_mapper.test.ts | 46 +++++++++++++++++++ .../backend-deployer/src/cdk_error_mapper.ts | 10 ++++ 3 files changed, 61 insertions(+) create mode 100644 .changeset/nasty-jars-peel.md diff --git a/.changeset/nasty-jars-peel.md b/.changeset/nasty-jars-peel.md new file mode 100644 index 00000000000..e6ba8b24256 --- /dev/null +++ b/.changeset/nasty-jars-peel.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +Handle ENOENT error from npm diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 64b5aebb6aa..d2a78715311 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -513,6 +513,52 @@ for your current platform. errorName: 'ESBuildError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: + // eslint-disable-next-line spellcheck/spell-checker + `Error: npm ERR! code ENOENT +npm ERR! syscall lstat +npm ERR! path /opt/homebrew/Cellar/node/22.2.0/lib +npm ERR! errno -2 +npm ERR! enoent ENOENT: no such file or directory, lstat '/opt/homebrew/Cellar/node/22.2.0/lib' +npm ERR! enoent This is related to npm not being able to find a file. +npm ERR! enoent +`, + expectedTopLevelErrorMessage: + // eslint-disable-next-line spellcheck/spell-checker + `NPM error occurred: npm ERR! code ENOENT +npm ERR! syscall lstat +npm ERR! path /opt/homebrew/Cellar/node/22.2.0/lib +npm ERR! errno -2 +npm ERR! enoent ENOENT: no such file or directory, lstat '/opt/homebrew/Cellar/node/22.2.0/lib' +npm ERR! enoent This is related to npm not being able to find a file. +npm ERR! enoent`, + errorName: 'CommonNPMError', + expectedDownstreamErrorMessage: undefined, + }, + { + errorMessage: + // eslint-disable-next-line spellcheck/spell-checker + `Error: npm error code ENOENT +npm error syscall lstat +npm error path C:\\Users\testUser\\AppData\\Roaming\\npm +npm error errno -4058 +npm error enoent ENOENT: no such file or directory, lstat 'C:\\Users\\testUser\\AppData\\Roaming\\npm' +npm error enoent This is related to npm not being able to find a file. +npm error enoent +`, + expectedTopLevelErrorMessage: + // eslint-disable-next-line spellcheck/spell-checker + `NPM error occurred: npm error code ENOENT +npm error syscall lstat +npm error path C:\\Users\testUser\\AppData\\Roaming\\npm +npm error errno -4058 +npm error enoent ENOENT: no such file or directory, lstat 'C:\\Users\\testUser\\AppData\\Roaming\\npm' +npm error enoent This is related to npm not being able to find a file. +npm error enoent`, + errorName: 'CommonNPMError', + expectedDownstreamErrorMessage: undefined, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index b57ed16ccc2..ebdbde41f4a 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -324,6 +324,15 @@ export class CdkErrorMapper { errorName: 'InvalidPackageJsonError', classification: 'ERROR', }, + { + errorRegex: new RegExp( + `(?(npm error|npm ERR!) code ENOENT${this.multiLineEolRegex}((npm error|npm ERR!) (.*)${this.multiLineEolRegex})*)` + ), + humanReadableErrorMessage: 'NPM error occurred: {npmError}', + resolutionMessage: `See https://docs.npmjs.com/common-errors for resolution.`, + errorName: 'CommonNPMError', + classification: 'ERROR', + }, { // Error: .* is printed to stderr during cdk synth // Also extracts the first line in the stack where the error happened @@ -407,6 +416,7 @@ export type CDKDeploymentError = | 'CDKVersionMismatchError' | 'CFNUpdateNotSupportedError' | 'CloudFormationDeploymentError' + | 'CommonNPMError' | 'FilePermissionsError' | 'MissingDefineBackendError' | 'MultipleSandboxInstancesError' From a6fa42eaca695ba009626f0a452c79cf19239707 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Mon, 25 Nov 2024 20:07:05 +0100 Subject: [PATCH 124/199] fix: specifically catch AppSync 'Code contains one or more errors' (#2264) * fix: specifically catch AppSync 'Code contains one or more errors' * PR Updates --- .changeset/early-suits-wave.md | 5 ++++ .../src/cdk_error_mapper.test.ts | 20 ++++++++++++++-- .../backend-deployer/src/cdk_error_mapper.ts | 24 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 .changeset/early-suits-wave.md diff --git a/.changeset/early-suits-wave.md b/.changeset/early-suits-wave.md new file mode 100644 index 00000000000..650e9790ac5 --- /dev/null +++ b/.changeset/early-suits-wave.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +specifically catch AppSync "Code contains one or more errors" diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index d2a78715311..fe68e71753e 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -163,10 +163,10 @@ const testErrorMappings = [ expectedDownstreamErrorMessage: undefined, }, { - errorMessage: `[31m some-stack failed: The stack named some-stack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: The code contains one or more errors. (Service: AppSync, Status Code: 400, Request ID: 12345) (RequestToken: 123, HandlerErrorCode: GeneralServiceException), Embedded stack was not successfully updated. Currently in UPDATE_ROLLBACK_IN_PROGRESS with reason: The following resource(s) failed to create: [resource1, resource2]. [39m`, + errorMessage: `[31m some-stack failed: The stack named some-stack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: Some amazing error message (Service: AppSync, Status Code: 400, Request ID: 12345) (RequestToken: 123, HandlerErrorCode: GeneralServiceException), Embedded stack was not successfully updated. Currently in UPDATE_ROLLBACK_IN_PROGRESS with reason: The following resource(s) failed to create: [resource1, resource2]. [39m`, expectedTopLevelErrorMessage: 'The CloudFormation deployment has failed.', errorName: 'CloudFormationDeploymentError', - expectedDownstreamErrorMessage: `The stack named some-stack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: The code contains one or more errors. (Service: AppSync, Status Code: 400, Request ID: 12345) (RequestToken: 123, HandlerErrorCode: GeneralServiceException), Embedded stack was not successfully updated. Currently in UPDATE_ROLLBACK_IN_PROGRESS with reason: The following resource(s) failed to create: [resource1, resource2]. [39m`, + expectedDownstreamErrorMessage: `The stack named some-stack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: Some amazing error message (Service: AppSync, Status Code: 400, Request ID: 12345) (RequestToken: 123, HandlerErrorCode: GeneralServiceException), Embedded stack was not successfully updated. Currently in UPDATE_ROLLBACK_IN_PROGRESS with reason: The following resource(s) failed to create: [resource1, resource2]. [39m`, }, { errorMessage: `[31m some-stack failed: The stack named some-stack failed creation, it may need to be manually deleted from the AWS console: ROLLBACK_COMPLETE`, @@ -406,6 +406,22 @@ npm error A complete log of this run can be found in: /home/some-path/.npm/_logs errorName: 'AccessDeniedError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: `amplify-stack-user-sandbox failed: BadRequestException: The code contains one or more errors.`, + expectedTopLevelErrorMessage: + 'A custom resolver used in your defineData contains one or more errors', + errorName: 'AppSyncResolverSyntaxError', + expectedDownstreamErrorMessage: + 'amplify-stack-user-sandbox failed: BadRequestException: The code contains one or more errors.', + }, + { + errorMessage: `Deployment failed: Error: The stack named amplify-stack-user-sandbox failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: The code contains one or more errors. (Service: AppSync, Status Code: 400,...`, + expectedTopLevelErrorMessage: + 'A custom resolver used in your defineData contains one or more errors', + errorName: 'AppSyncResolverSyntaxError', + expectedDownstreamErrorMessage: + 'Deployment failed: Error: The stack named amplify-stack-user-sandbox failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: The code contains one or more errors. (Service: AppSync, Status Code: 400,...', + }, { errorMessage: `User: some:great:user is not authorized to perform: appsync:StartSchemaCreation on resource: arn:aws:appsync:us-east-1:235494812930:/v1/api/myApi`, expectedTopLevelErrorMessage: diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index ebdbde41f4a..078915b6256 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -381,6 +381,29 @@ export class CdkErrorMapper { errorName: 'SecretNotSetError', classification: 'ERROR', }, + { + errorRegex: + /BadRequestException: The code contains one or more errors|The code contains one or more errors.*AppSync/, + humanReadableErrorMessage: `A custom resolver used in your defineData contains one or more errors`, + resolutionMessage: `Check for any syntax errors in your custom resolvers code.`, + errorName: 'AppSyncResolverSyntaxError', + classification: 'ERROR', + }, + // Generic error printed by CDK. Order matters so keep this towards the bottom of this list + { + // Error: .* is printed to stderr during cdk synth + // Also extracts the first line in the stack where the error happened + errorRegex: new RegExp( + `^Error: (.*${this.multiLineEolRegex}.*at.*)`, + 'm' + ), + humanReadableErrorMessage: + 'Unable to build the Amplify backend definition.', + resolutionMessage: + 'Check your backend definition in the `amplify` folder for syntax and type errors.', + errorName: 'BackendSynthError', + classification: 'ERROR', + }, { errorRegex: /(?amplify-[a-z0-9-]+)(.*) failed: ValidationError: Stack:(.*) is in (?.*) state and can not be updated/, @@ -407,6 +430,7 @@ export class CdkErrorMapper { export type CDKDeploymentError = | 'AccessDeniedError' + | 'AppSyncResolverSyntaxError' | 'BackendBuildError' | 'BackendSynthError' | 'BootstrapNotDetectedError' From d332c516a4c58555d542342b998650b8405332ac Mon Sep 17 00:00:00 2001 From: Leena <81589006+ShadowCat567@users.noreply.github.com> Date: Mon, 25 Nov 2024 14:32:32 -0500 Subject: [PATCH 125/199] Catch "stack is in failed state, you may need to delete it from AWS Console" (DELETE_FAILED) (#2265) * delete stack regex * test for cloud deletion error * added changeset * making some adjustments * added stack name to error message * update resolution message with failed resources --------- Co-authored-by: Vieltojarvi --- .changeset/chatty-goats-smell.md | 5 +++++ .../backend-deployer/src/cdk_error_mapper.test.ts | 7 +++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 12 ++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 .changeset/chatty-goats-smell.md diff --git a/.changeset/chatty-goats-smell.md b/.changeset/chatty-goats-smell.md new file mode 100644 index 00000000000..a0938664ae4 --- /dev/null +++ b/.changeset/chatty-goats-smell.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +map a form of deletion/destroy failed error diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index fe68e71753e..f1d7d9653c6 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -575,6 +575,13 @@ npm error enoent`, errorName: 'CommonNPMError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: `[31m: destroy failed Error: The stack named amplify-some-stack is in a failed state. You may need to delete it from the AWS console : DELETE_FAILED (The following resource(s) failed to delete: [resource1, resource2]. )`, + expectedTopLevelErrorMessage: + 'The CloudFormation deletion failed due to amplify-some-stack being in DELETE_FAILED state. Ensure all your resources are able to be deleted', + errorName: 'CloudFormationDeletionError', + expectedDownstreamErrorMessage: undefined, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 078915b6256..9ed05d9d7ef 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -211,6 +211,17 @@ export class CdkErrorMapper { errorName: 'GetLambdaLayerVersionError', classification: 'ERROR', }, + { + //This has some overlap with "User:__ is not authorized to perform:__ on resource: __" - some resources cannot be deleted due to lack of permissions + errorRegex: + /The stack named (?.*) is in a failed state. You may need to delete it from the AWS console : DELETE_FAILED \(The following resource\(s\) failed to delete: (?.*). \)/, + humanReadableErrorMessage: + 'The CloudFormation deletion failed due to {stackName} being in DELETE_FAILED state. Ensure all your resources are able to be deleted', + resolutionMessage: + 'The following resource(s) failed to delete: {resources}. Ensure they are in a state where they can be deleted. Find more information in the CloudFormation AWS Console for this stack.', + errorName: 'CloudFormationDeletionError', + classification: 'ERROR', + }, { errorRegex: /User:(.*) is not authorized to perform:(.*) on resource:(?.*) because no identity-based policy allows the (?.*) action/, @@ -439,6 +450,7 @@ export type CDKDeploymentError = | 'CDKResolveAWSAccountError' | 'CDKVersionMismatchError' | 'CFNUpdateNotSupportedError' + | 'CloudFormationDeletionError' | 'CloudFormationDeploymentError' | 'CommonNPMError' | 'FilePermissionsError' From 273a9ba047ea3bc3cd44b1811850b1021a867d27 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 19:46:31 +0000 Subject: [PATCH 126/199] Version Packages (#2251) Co-authored-by: github-actions[bot] --- .changeset/chatty-goats-smell.md | 5 ----- .changeset/early-suits-wave.md | 5 ----- .changeset/great-otters-study.md | 5 ----- .changeset/healthy-trains-doubt.md | 5 ----- .changeset/nasty-jars-peel.md | 5 ----- .changeset/orange-garlics-reflect.md | 5 ----- .changeset/rude-wasps-look.md | 5 ----- .changeset/slimy-sheep-wonder.md | 5 ----- .changeset/warm-garlics-raise.md | 5 ----- packages/backend-deployer/CHANGELOG.md | 15 +++++++++++++++ packages/backend-deployer/package.json | 4 ++-- packages/platform-core/CHANGELOG.md | 6 ++++++ packages/platform-core/package.json | 2 +- 13 files changed, 24 insertions(+), 48 deletions(-) delete mode 100644 .changeset/chatty-goats-smell.md delete mode 100644 .changeset/early-suits-wave.md delete mode 100644 .changeset/great-otters-study.md delete mode 100644 .changeset/healthy-trains-doubt.md delete mode 100644 .changeset/nasty-jars-peel.md delete mode 100644 .changeset/orange-garlics-reflect.md delete mode 100644 .changeset/rude-wasps-look.md delete mode 100644 .changeset/slimy-sheep-wonder.md delete mode 100644 .changeset/warm-garlics-raise.md diff --git a/.changeset/chatty-goats-smell.md b/.changeset/chatty-goats-smell.md deleted file mode 100644 index a0938664ae4..00000000000 --- a/.changeset/chatty-goats-smell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -map a form of deletion/destroy failed error diff --git a/.changeset/early-suits-wave.md b/.changeset/early-suits-wave.md deleted file mode 100644 index 650e9790ac5..00000000000 --- a/.changeset/early-suits-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -specifically catch AppSync "Code contains one or more errors" diff --git a/.changeset/great-otters-study.md b/.changeset/great-otters-study.md deleted file mode 100644 index 67954f92cac..00000000000 --- a/.changeset/great-otters-study.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -add more forms of transform errors to cdk error mapping diff --git a/.changeset/healthy-trains-doubt.md b/.changeset/healthy-trains-doubt.md deleted file mode 100644 index 8dca0dd4fc4..00000000000 --- a/.changeset/healthy-trains-doubt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -map another form of access denied validation error diff --git a/.changeset/nasty-jars-peel.md b/.changeset/nasty-jars-peel.md deleted file mode 100644 index e6ba8b24256..00000000000 --- a/.changeset/nasty-jars-peel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -Handle ENOENT error from npm diff --git a/.changeset/orange-garlics-reflect.md b/.changeset/orange-garlics-reflect.md deleted file mode 100644 index 1be521b1521..00000000000 --- a/.changeset/orange-garlics-reflect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/platform-core': patch ---- - -Handle insufficient disk space errors diff --git a/.changeset/rude-wasps-look.md b/.changeset/rude-wasps-look.md deleted file mode 100644 index dee6f59b718..00000000000 --- a/.changeset/rude-wasps-look.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -truncate large error messages before printing to customer diff --git a/.changeset/slimy-sheep-wonder.md b/.changeset/slimy-sheep-wonder.md deleted file mode 100644 index 1c79787ba61..00000000000 --- a/.changeset/slimy-sheep-wonder.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -handle not authorized to perform on resource error diff --git a/.changeset/warm-garlics-raise.md b/.changeset/warm-garlics-raise.md deleted file mode 100644 index f30209ff8eb..00000000000 --- a/.changeset/warm-garlics-raise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -Handle invalid package.json error diff --git a/packages/backend-deployer/CHANGELOG.md b/packages/backend-deployer/CHANGELOG.md index 2e71d10a85d..cdf90c774d2 100644 --- a/packages/backend-deployer/CHANGELOG.md +++ b/packages/backend-deployer/CHANGELOG.md @@ -1,5 +1,20 @@ # @aws-amplify/backend-deployer +## 1.1.10 + +### Patch Changes + +- d332c51: map a form of deletion/destroy failed error +- a6fa42e: specifically catch AppSync "Code contains one or more errors" +- a23f656: add more forms of transform errors to cdk error mapping +- 754d0f7: map another form of access denied validation error +- 716f844: Handle ENOENT error from npm +- 691e7ca: truncate large error messages before printing to customer +- b6f4c54: handle not authorized to perform on resource error +- 0114549: Handle invalid package.json error +- Updated dependencies [249c0e5] + - @aws-amplify/platform-core@1.2.2 + ## 1.1.9 ### Patch Changes diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index 484de0b34a9..36e80d58173 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-deployer", - "version": "1.1.9", + "version": "1.1.10", "type": "module", "publishConfig": { "access": "public" @@ -19,7 +19,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.2.0", + "@aws-amplify/platform-core": "^1.2.2", "@aws-amplify/plugin-types": "^1.4.0", "execa": "^8.0.1", "tsx": "^4.6.1" diff --git a/packages/platform-core/CHANGELOG.md b/packages/platform-core/CHANGELOG.md index 2f8cc589ed0..44de4598fc3 100644 --- a/packages/platform-core/CHANGELOG.md +++ b/packages/platform-core/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/platform-core +## 1.2.2 + +### Patch Changes + +- 249c0e5: Handle insufficient disk space errors + ## 1.2.1 ### Patch Changes diff --git a/packages/platform-core/package.json b/packages/platform-core/package.json index ae7d72a0e68..890f108a4b2 100644 --- a/packages/platform-core/package.json +++ b/packages/platform-core/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/platform-core", - "version": "1.2.1", + "version": "1.2.2", "type": "commonjs", "publishConfig": { "access": "public" From 0a360fb6e3a75659a993866679f59e2735505216 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:43:00 +0100 Subject: [PATCH 127/199] fix: extract generic cdk asset publish failures (#2271) --- .changeset/poor-phones-attend.md | 5 +++++ package-lock.json | 1 + packages/backend-deployer/package.json | 3 ++- .../src/cdk_error_mapper.test.ts | 18 ++++++++++++++++++ .../backend-deployer/src/cdk_error_mapper.ts | 17 +++++++++++++++-- 5 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 .changeset/poor-phones-attend.md diff --git a/.changeset/poor-phones-attend.md b/.changeset/poor-phones-attend.md new file mode 100644 index 00000000000..b3dbdabe6ad --- /dev/null +++ b/.changeset/poor-phones-attend.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +extract generic cdk asset publish failures diff --git a/package-lock.json b/package-lock.json index 25adc2dc0a0..a89d39fc1d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31647,6 +31647,7 @@ "@aws-amplify/platform-core": "^1.2.0", "@aws-amplify/plugin-types": "^1.4.0", "execa": "^8.0.1", + "strip-ansi": "^6.0.1", "tsx": "^4.6.1" }, "peerDependencies": { diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index 36e80d58173..adc2a77a229 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -22,7 +22,8 @@ "@aws-amplify/platform-core": "^1.2.2", "@aws-amplify/plugin-types": "^1.4.0", "execa": "^8.0.1", - "tsx": "^4.6.1" + "tsx": "^4.6.1", + "strip-ansi": "^6.0.1" }, "peerDependencies": { "aws-cdk": "^2.158.0", diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index f1d7d9653c6..be08eedc179 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -429,6 +429,24 @@ npm error A complete log of this run can be found in: /home/some-path/.npm/_logs errorName: 'AccessDeniedError', expectedDownstreamErrorMessage: undefined, }, + { + // eslint-disable-next-line spellcheck/spell-checker + errorMessage: `[31mamplify-stack-sandbox-11[22m: fail: Bucket named 'cdk-abc-assets-11-us-west-2' exists, but we do not have access to it. +amplify-stack-sandbox-11: fail: Bucket named 'cdk-abc-assets-11-us-west-2' exists, but we do not have access to it. +Failed to publish asset abc:current_account-current_region`, + expectedTopLevelErrorMessage: `CDK failed to publish assets due to 'Bucket named 'cdk-abc-assets-11-us-west-2' exists, but we do not have access to it.'`, + errorName: 'CDKAssetPublishError', + expectedDownstreamErrorMessage: undefined, + }, + { + // eslint-disable-next-line spellcheck/spell-checker + errorMessage: `[31mamplify-user-sandbox-c71414864a: fail: socket hang up + +Failed to publish asset abc:current_account-current_region`, + expectedTopLevelErrorMessage: `CDK failed to publish assets due to 'socket hang up'`, + errorName: 'CDKAssetPublishError', + expectedDownstreamErrorMessage: undefined, + }, { errorMessage: `Error: Transform failed with 1 error:` + diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 9ed05d9d7ef..67d5159d50f 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -4,6 +4,7 @@ import { AmplifyFault, AmplifyUserError, } from '@aws-amplify/platform-core'; +import stripANSI from 'strip-ansi'; import { BackendDeployerOutputFormatter } from './types.js'; /** @@ -27,13 +28,14 @@ export class CdkErrorMapper { return amplifyError; } + const errorMessage = stripANSI(error.message); const matchingError = this.getKnownErrors().find((knownError) => - knownError.errorRegex.test(error.message) + knownError.errorRegex.test(errorMessage) ); if (matchingError) { // Extract meaningful contextual information if available - const matchGroups = error.message.match(matchingError.errorRegex); + const matchGroups = errorMessage.match(matchingError.errorRegex); if (matchGroups && matchGroups.length > 1) { // If the contextual information can be used in the error message use it, else consider it as a downstream cause @@ -400,6 +402,16 @@ export class CdkErrorMapper { errorName: 'AppSyncResolverSyntaxError', classification: 'ERROR', }, + { + errorRegex: new RegExp( + `amplify-.*-(branch|sandbox)-.*fail: (?.*)${this.multiLineEolRegex}.*Failed to publish asset`, + 'm' + ), + humanReadableErrorMessage: `CDK failed to publish assets due to '{publishFailure}'`, + resolutionMessage: `Check the error message for more details.`, + errorName: 'CDKAssetPublishError', + classification: 'ERROR', + }, // Generic error printed by CDK. Order matters so keep this towards the bottom of this list { // Error: .* is printed to stderr during cdk synth @@ -447,6 +459,7 @@ export type CDKDeploymentError = | 'BootstrapNotDetectedError' | 'BootstrapDetectionError' | 'BootstrapOutdatedError' + | 'CDKAssetPublishError' | 'CDKResolveAWSAccountError' | 'CDKVersionMismatchError' | 'CFNUpdateNotSupportedError' From 72b2fe0d763392641d1001ebccc57c98191c24e4 Mon Sep 17 00:00:00 2001 From: Andrew Taylor Date: Tue, 26 Nov 2024 10:10:26 -0800 Subject: [PATCH 128/199] Allow Node 22 functions (#2269) * Add support to `@aws-amplify/backend-function` for Node 22 Add support to `@aws-amplify/backend-function` for Node 22, which is a [supported Lambda runtime](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtime-deprecation-levels) that was added in [`aws-cdk-lib/aws-lambda` version `2.168.0`](https://github.com/aws/aws-cdk/releases/tag/v2.168.0) on November 20th, 2024 * Regen API docs * npm clean * Update aws-cdk-lib and aws-cdk to 2.168.0 * Update aws-cdk-lib and aws-cdk to 2.168.0 * Update test * Changeset for aws-cdk-lib upgrade * Update .changeset/forty-bulldogs-end.md --------- Co-authored-by: Kamil Sobol --- .changeset/forty-bulldogs-end.md | 8 +++ .changeset/new-rings-suffer.md | 20 ++++++ package-lock.json | 68 +++++++++---------- packages/ai-constructs/package.json | 2 +- packages/auth-construct/package.json | 2 +- packages/backend-ai/package.json | 2 +- packages/backend-auth/package.json | 2 +- packages/backend-data/package.json | 2 +- packages/backend-deployer/package.json | 2 +- packages/backend-function/API.md | 2 +- packages/backend-function/package.json | 2 +- packages/backend-function/src/factory.test.ts | 6 +- packages/backend-function/src/factory.ts | 3 +- packages/backend-output-storage/package.json | 2 +- .../backend-platform-test-stubs/package.json | 2 +- packages/backend-storage/package.json | 2 +- packages/backend/package.json | 2 +- packages/integration-tests/package.json | 2 +- packages/plugin-types/package.json | 2 +- packages/sandbox/package.json | 2 +- 20 files changed, 82 insertions(+), 53 deletions(-) create mode 100644 .changeset/forty-bulldogs-end.md create mode 100644 .changeset/new-rings-suffer.md diff --git a/.changeset/forty-bulldogs-end.md b/.changeset/forty-bulldogs-end.md new file mode 100644 index 00000000000..99aa5a81e2f --- /dev/null +++ b/.changeset/forty-bulldogs-end.md @@ -0,0 +1,8 @@ +--- +'@aws-amplify/backend-function': minor +'@aws-amplify/backend': minor +--- + +Add support to `@aws-amplify/backend-function` for Node 22 + +Add support to `@aws-amplify/backend-function` for Node 22, which is a [supported Lambda runtime](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtime-deprecation-levels) that was added in [`aws-cdk-lib/aws-lambda` version `2.168.0`](https://github.com/aws/aws-cdk/releases/tag/v2.168.0) on November 20th, 2024 diff --git a/.changeset/new-rings-suffer.md b/.changeset/new-rings-suffer.md new file mode 100644 index 00000000000..f3df724642e --- /dev/null +++ b/.changeset/new-rings-suffer.md @@ -0,0 +1,20 @@ +--- +'@aws-amplify/backend-platform-test-stubs': patch +'@aws-amplify/backend-output-storage': patch +'@aws-amplify/integration-tests': patch +'@aws-amplify/backend-deployer': patch +'@aws-amplify/backend-function': patch +'@aws-amplify/schema-generator': patch +'@aws-amplify/backend-storage': patch +'@aws-amplify/auth-construct': patch +'@aws-amplify/ai-constructs': patch +'@aws-amplify/client-config': patch +'@aws-amplify/backend-auth': patch +'@aws-amplify/backend-data': patch +'@aws-amplify/plugin-types': patch +'@aws-amplify/backend-ai': patch +'@aws-amplify/backend': patch +'@aws-amplify/sandbox': patch +--- + +update aws-cdk lib to ^2.168.0 diff --git a/package-lock.json b/package-lock.json index a89d39fc1d2..abf7f393397 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6159,15 +6159,15 @@ } }, "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.202", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz", - "integrity": "sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg==", + "version": "2.2.213", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.213.tgz", + "integrity": "sha512-crm1yDJmORJF2Y9gDvNUX4Q3iQXVhWrL7oaZfpx3QDqrvVz5UEgWGpJdysqDuWFZTmIgtrI5Svq3UfdwCNNpsg==", "license": "Apache-2.0" }, "node_modules/@aws-cdk/asset-kubectl-v20": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", - "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.3.tgz", + "integrity": "sha512-cDG1w3ieM6eOT9mTefRuTypk95+oyD7P5X/wRltwmYxU7nZc3+076YEVS6vrjDKr3ADYbfn0lDKpfB1FBtO9CQ==", "license": "Apache-2.0" }, "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { @@ -19581,9 +19581,9 @@ "license": "0BSD" }, "node_modules/aws-cdk": { - "version": "2.164.1", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.164.1.tgz", - "integrity": "sha512-dWRViQgHLe7GHkPIQGA+8EQSm8TBcxemyCC3HHW3wbLMWUDbspio9Dktmw5EmWxlFjjWh86Dk1JWf1zKQo8C5g==", + "version": "2.171.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.171.0.tgz", + "integrity": "sha512-tVo4hYS0iAbiCFxUh2/7KoDL6EHEIUAurCJaBs2BOUAB9DfBuUAPp8DGUK4iVF8XzrQQ4f3a5ivN7DteQrGBEQ==", "license": "Apache-2.0", "peer": true, "bin": { @@ -19597,9 +19597,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.164.1", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.164.1.tgz", - "integrity": "sha512-jNvVmfZJbZoAYU94b5dzTlF2z6JXJ204NgcYY5haOa6mq3m2bzdYPXnPtB5kpAX3oBi++yoRdmLhqgckdEhUZA==", + "version": "2.171.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.171.0.tgz", + "integrity": "sha512-N0O0mWI+S8PAbiED7eV05qVfbJgHkdh+jQS4BOG6CUAc/i2s9U4w+XDRkK8auO0HgTM9+ahEaFfucMuQ4abRWQ==", "bundleDependencies": [ "@balena/dockerignore", "case", @@ -19615,10 +19615,10 @@ ], "license": "Apache-2.0", "dependencies": { - "@aws-cdk/asset-awscli-v1": "^2.2.202", - "@aws-cdk/asset-kubectl-v20": "^2.1.2", + "@aws-cdk/asset-awscli-v1": "^2.2.208", + "@aws-cdk/asset-kubectl-v20": "^2.1.3", "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", - "@aws-cdk/cloud-assembly-schema": "^38.0.0", + "@aws-cdk/cloud-assembly-schema": "^38.0.1", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", "fs-extra": "^11.2.0", @@ -19742,9 +19742,9 @@ "license": "MIT" }, "node_modules/aws-cdk-lib/node_modules/fast-uri": { - "version": "3.0.1", + "version": "3.0.3", "inBundle": true, - "license": "MIT" + "license": "BSD-3-Clause" }, "node_modules/aws-cdk-lib/node_modules/fs-extra": { "version": "11.2.0", @@ -31526,7 +31526,7 @@ "typescript": "^5.0.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } }, @@ -31545,7 +31545,7 @@ "@aws-sdk/util-arn-parser": "^3.568.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } }, @@ -31574,7 +31574,7 @@ "aws-lambda": "^1.0.7" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } }, @@ -31591,7 +31591,7 @@ "@aws-amplify/plugin-types": "^1.5.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } }, @@ -31614,7 +31614,7 @@ "aws-lambda": "^1.0.7" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } }, @@ -31635,23 +31635,23 @@ "@aws-amplify/platform-core": "^1.2.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } }, "packages/backend-deployer": { "name": "@aws-amplify/backend-deployer", - "version": "1.1.9", + "version": "1.1.10", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.2.0", + "@aws-amplify/platform-core": "^1.2.2", "@aws-amplify/plugin-types": "^1.4.0", "execa": "^8.0.1", "strip-ansi": "^6.0.1", "tsx": "^4.6.1" }, "peerDependencies": { - "aws-cdk": "^2.158.0", + "aws-cdk": "^2.168.0", "typescript": "^5.0.0" } }, @@ -31673,7 +31673,7 @@ "uuid": "^9.0.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } }, @@ -31712,7 +31712,7 @@ "@aws-amplify/plugin-types": "^1.3.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0" + "aws-cdk-lib": "^2.168.0" } }, "packages/backend-platform-test-stubs": { @@ -31721,7 +31721,7 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/plugin-types": "^1.3.1", - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } }, @@ -31752,7 +31752,7 @@ "@aws-amplify/platform-core": "^1.2.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } }, @@ -32129,7 +32129,7 @@ "@zip.js/zip.js": "^2.7.52", "aws-amplify": "^6.0.16", "aws-appsync-auth-link": "^3.0.7", - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0", "execa": "^8.0.1", "fs-extra": "^11.1.1", @@ -32180,7 +32180,7 @@ }, "packages/platform-core": { "name": "@aws-amplify/platform-core", - "version": "1.2.1", + "version": "1.2.2", "license": "Apache-2.0", "dependencies": { "@aws-amplify/plugin-types": "^1.5.0", @@ -32219,7 +32219,7 @@ }, "peerDependencies": { "@aws-sdk/types": "^3.609.0", - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } }, @@ -32367,7 +32367,7 @@ "@types/parse-gitignore": "^1.0.0" }, "peerDependencies": { - "aws-cdk": "^2.158.0" + "aws-cdk": "^2.168.0" } }, "packages/schema-generator": { diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index 73fdf04e270..584d3d361c2 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -38,7 +38,7 @@ "typescript": "^5.0.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } } diff --git a/packages/auth-construct/package.json b/packages/auth-construct/package.json index 42c20143eeb..cb70f430740 100644 --- a/packages/auth-construct/package.json +++ b/packages/auth-construct/package.json @@ -25,7 +25,7 @@ "@aws-sdk/util-arn-parser": "^3.568.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } } diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 6699cc19a5d..1cf30fb297e 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -30,7 +30,7 @@ "@aws-amplify/plugin-types": "^1.5.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } } diff --git a/packages/backend-auth/package.json b/packages/backend-auth/package.json index 43ea82bfdc0..054d782dc6c 100644 --- a/packages/backend-auth/package.json +++ b/packages/backend-auth/package.json @@ -33,7 +33,7 @@ "aws-lambda": "^1.0.7" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } } diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index ba3ed7e55a9..0ec2b12a4c8 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -24,7 +24,7 @@ "@aws-amplify/platform-core": "^1.2.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" }, "dependencies": { diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index adc2a77a229..3a57923c2bd 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -26,7 +26,7 @@ "strip-ansi": "^6.0.1" }, "peerDependencies": { - "aws-cdk": "^2.158.0", + "aws-cdk": "^2.168.0", "typescript": "^5.0.0" } } diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index c87d9469ef0..8e0ad66d853 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -46,7 +46,7 @@ export type FunctionProps = { export type FunctionSchedule = TimeInterval | CronSchedule; // @public (undocumented) -export type NodeVersion = 16 | 18 | 20; +export type NodeVersion = 16 | 18 | 20 | 22; // @public (undocumented) export type TimeInterval = `every ${number}m` | `every ${number}h` | `every day` | `every week` | `every month` | `every year`; diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index 412c6be0be9..212f8567ed9 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -32,7 +32,7 @@ "uuid": "^9.0.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } } diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index 95624b523bc..c7ce48a2cfe 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -308,12 +308,12 @@ void describe('AmplifyFunctionFactory', () => { void it('sets valid runtime', () => { const lambda = defineFunction({ entry: './test-assets/default-lambda/handler.ts', - runtime: 16, + runtime: 22, }).getInstance(getInstanceProps); const template = Template.fromStack(lambda.stack); template.hasResourceProperties('AWS::Lambda::Function', { - Runtime: Runtime.NODEJS_16_X.name, + Runtime: Runtime.NODEJS_22_X.name, }); }); @@ -335,7 +335,7 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', runtime: 14 as NodeVersion, }).getInstance(getInstanceProps), - new Error('runtime must be one of the following: 16, 18, 20') + new Error('runtime must be one of the following: 16, 18, 20, 22') ); }); diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 5a62fcf36fb..9c29357c217 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -570,10 +570,11 @@ const isWholeNumberBetweenInclusive = ( max: number ) => min <= test && test <= max && test % 1 === 0; -export type NodeVersion = 16 | 18 | 20; +export type NodeVersion = 16 | 18 | 20 | 22; const nodeVersionMap: Record = { 16: Runtime.NODEJS_16_X, 18: Runtime.NODEJS_18_X, 20: Runtime.NODEJS_20_X, + 22: Runtime.NODEJS_22_X, }; diff --git a/packages/backend-output-storage/package.json b/packages/backend-output-storage/package.json index 839a1351da5..0269f794b62 100644 --- a/packages/backend-output-storage/package.json +++ b/packages/backend-output-storage/package.json @@ -24,6 +24,6 @@ "@aws-amplify/plugin-types": "^1.3.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0" + "aws-cdk-lib": "^2.168.0" } } diff --git a/packages/backend-platform-test-stubs/package.json b/packages/backend-platform-test-stubs/package.json index 4d8ab06e8fd..8d86327dc9a 100644 --- a/packages/backend-platform-test-stubs/package.json +++ b/packages/backend-platform-test-stubs/package.json @@ -17,7 +17,7 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/plugin-types": "^1.3.1", - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } } diff --git a/packages/backend-storage/package.json b/packages/backend-storage/package.json index c56134608f7..051a4c975d7 100644 --- a/packages/backend-storage/package.json +++ b/packages/backend-storage/package.json @@ -28,7 +28,7 @@ "@aws-amplify/platform-core": "^1.2.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } } diff --git a/packages/backend/package.json b/packages/backend/package.json index b436de0b74e..106acaac429 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -40,7 +40,7 @@ "lodash.snakecase": "^4.1.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" }, "devDependencies": { diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 7f394b28b8a..9943548811e 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -33,7 +33,7 @@ "@zip.js/zip.js": "^2.7.52", "aws-amplify": "^6.0.16", "aws-appsync-auth-link": "^3.0.7", - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0", "execa": "^8.0.1", "fs-extra": "^11.1.1", diff --git a/packages/plugin-types/package.json b/packages/plugin-types/package.json index d60ad1902cb..b5aa9f69956 100644 --- a/packages/plugin-types/package.json +++ b/packages/plugin-types/package.json @@ -11,7 +11,7 @@ }, "license": "Apache-2.0", "peerDependencies": { - "aws-cdk-lib": "^2.158.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0", "@aws-sdk/types": "^3.609.0" }, diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index ff96f119597..fbb7da66ab9 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -42,6 +42,6 @@ "@types/parse-gitignore": "^1.0.0" }, "peerDependencies": { - "aws-cdk": "^2.158.0" + "aws-cdk": "^2.168.0" } } From 65abf6a22ed9591bf11678a3798c459b767955db Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 27 Nov 2024 09:44:10 -0800 Subject: [PATCH 129/199] Functions logging (#2245) * Functions logging * changeset * more * conversation handler * conversation handler * this works * more tests * more tests * more tests * fix build * fix build * pr feedback * pr feedback * api * pr feedback --- .changeset/poor-moons-refuse.md | 9 + package-lock.json | 4 + packages/ai-constructs/API.md | 6 + .../conversation_handler_construct.test.ts | 39 +++++ .../conversation_handler_construct.ts | 10 +- packages/backend-ai/API.md | 18 ++ .../src/conversation/factory.test.ts | 37 ++++ .../backend-ai/src/conversation/factory.ts | 28 +++ packages/backend-ai/src/conversation/index.ts | 6 + packages/backend-function/API.md | 19 ++ packages/backend-function/src/factory.test.ts | 33 ++++ packages/backend-function/src/factory.ts | 25 +++ .../src/logging_options_parser.test.ts | 56 ++++++ .../src/logging_options_parser.ts | 48 ++++++ packages/platform-core/API.md | 24 +++ packages/platform-core/api-extractor.json | 3 +- packages/platform-core/package.json | 9 + packages/platform-core/src/.eslintrc.json | 15 ++ packages/platform-core/src/cdk/.eslintrc.json | 5 + .../src/cdk/enum_converters.test.ts | 162 ++++++++++++++++++ .../platform-core/src/cdk/enum_converters.ts | 97 +++++++++++ packages/platform-core/src/cdk/index.ts | 3 + packages/platform-core/src/index.internal.ts | 12 ++ packages/plugin-types/API.md | 6 + packages/plugin-types/src/index.ts | 2 + packages/plugin-types/src/log_level.ts | 1 + packages/plugin-types/src/log_retention.ts | 24 +++ 27 files changed, 699 insertions(+), 2 deletions(-) create mode 100644 .changeset/poor-moons-refuse.md create mode 100644 packages/backend-function/src/logging_options_parser.test.ts create mode 100644 packages/backend-function/src/logging_options_parser.ts create mode 100644 packages/platform-core/src/.eslintrc.json create mode 100644 packages/platform-core/src/cdk/.eslintrc.json create mode 100644 packages/platform-core/src/cdk/enum_converters.test.ts create mode 100644 packages/platform-core/src/cdk/enum_converters.ts create mode 100644 packages/platform-core/src/cdk/index.ts create mode 100644 packages/platform-core/src/index.internal.ts create mode 100644 packages/plugin-types/src/log_level.ts create mode 100644 packages/plugin-types/src/log_retention.ts diff --git a/.changeset/poor-moons-refuse.md b/.changeset/poor-moons-refuse.md new file mode 100644 index 00000000000..4da456357c7 --- /dev/null +++ b/.changeset/poor-moons-refuse.md @@ -0,0 +1,9 @@ +--- +'@aws-amplify/ai-constructs': minor +'@aws-amplify/backend-ai': minor +'@aws-amplify/backend-function': minor +'@aws-amplify/backend': minor +'@aws-amplify/platform-core': minor +--- + +Add options to control log settings diff --git a/package-lock.json b/package-lock.json index abf7f393397..c60a804d40e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32195,6 +32195,10 @@ "@types/is-ci": "^3.0.4", "@types/lodash.mergewith": "^4.6.2", "@types/uuid": "9.0.7" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.0.0" } }, "packages/platform-core/node_modules/uuid": { diff --git a/packages/ai-constructs/API.md b/packages/ai-constructs/API.md index a5621d37df5..0f1550885f2 100644 --- a/packages/ai-constructs/API.md +++ b/packages/ai-constructs/API.md @@ -7,12 +7,14 @@ /// import { AIConversationOutput } from '@aws-amplify/backend-output-schemas'; +import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda'; import { BackendOutputStorageStrategy } from '@aws-amplify/plugin-types'; import * as bedrock from '@aws-sdk/client-bedrock-runtime'; import { Construct } from 'constructs'; import { FunctionResources } from '@aws-amplify/plugin-types'; import * as jsonSchemaToTypeScript from 'json-schema-to-ts'; import { ResourceProvider } from '@aws-amplify/plugin-types'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; declare namespace __export__conversation { export { @@ -55,6 +57,10 @@ type ConversationHandlerFunctionProps = { region?: string; }>; memoryMB?: number; + logging?: { + level?: ApplicationLogLevel; + retention?: RetentionDays; + }; outputStorageStrategy?: BackendOutputStorageStrategy; }; diff --git a/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts b/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts index b0130e711f9..284024a89e4 100644 --- a/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts +++ b/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts @@ -5,6 +5,8 @@ import { ConversationHandlerFunction } from './conversation_handler_construct'; import { Template } from 'aws-cdk-lib/assertions'; import path from 'path'; import { StackMetadataBackendOutputStorageStrategy } from '@aws-amplify/backend-output-storage'; +import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; void describe('Conversation Handler Function construct', () => { void it('creates handler with log group with JWT token redacting policy', () => { @@ -284,4 +286,41 @@ void describe('Conversation Handler Function construct', () => { }, new Error('memoryMB must be a whole number between 128 and 10240 inclusive')); }); }); + + void describe('logging options', () => { + void it('sets log level', () => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [], + logging: { + level: ApplicationLogLevel.DEBUG, + }, + }); + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + LoggingConfig: { + ApplicationLogLevel: 'DEBUG', + LogFormat: 'JSON', + }, + }); + }); + + void it('sets log retention', () => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [], + logging: { + retention: RetentionDays.ONE_YEAR, + }, + }); + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Logs::LogGroup', { + RetentionInDays: 365, + }); + }); + }); }); diff --git a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts index 995b92fed6c..e5b563a6df9 100644 --- a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts +++ b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts @@ -6,6 +6,7 @@ import { import { Duration, Stack, Tags } from 'aws-cdk-lib'; import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam'; import { + ApplicationLogLevel, CfnFunction, Runtime as LambdaRuntime, LoggingFormat, @@ -40,6 +41,12 @@ export type ConversationHandlerFunctionProps = { * Default is 512MB. */ memoryMB?: number; + + logging?: { + level?: ApplicationLogLevel; + retention?: RetentionDays; + }; + /** * @internal */ @@ -100,8 +107,9 @@ export class ConversationHandlerFunction bundleAwsSDK: !!this.props.entry, }, loggingFormat: LoggingFormat.JSON, + applicationLogLevelV2: this.props.logging?.level, logGroup: new LogGroup(this, 'conversationHandlerFunctionLogGroup', { - retention: RetentionDays.INFINITE, + retention: this.props.logging?.retention ?? RetentionDays.INFINITE, dataProtectionPolicy: new DataProtectionPolicy({ identifiers: [ new CustomDataIdentifier( diff --git a/packages/backend-ai/API.md b/packages/backend-ai/API.md index 2b75a01c579..fcacb75c61c 100644 --- a/packages/backend-ai/API.md +++ b/packages/backend-ai/API.md @@ -8,12 +8,17 @@ import { AiModel } from '@aws-amplify/data-schema-types'; import { ConstructFactory } from '@aws-amplify/plugin-types'; import { ConversationTurnEventVersion } from '@aws-amplify/ai-constructs/conversation'; import { FunctionResources } from '@aws-amplify/plugin-types'; +import { LogLevel } from '@aws-amplify/plugin-types'; +import { LogRetention } from '@aws-amplify/plugin-types'; import { ResourceProvider } from '@aws-amplify/plugin-types'; import * as runtime from '@aws-amplify/ai-constructs/conversation/runtime'; declare namespace __export__conversation { export { ConversationHandlerFunctionFactory, + ConversationHandlerFunctionLogLevel, + ConversationHandlerFunctionLogRetention, + ConversationHandlerFunctionLoggingOptions, DefineConversationHandlerFunctionProps, defineConversationHandlerFunction } @@ -36,6 +41,18 @@ type ConversationHandlerFunctionFactory = ConstructFactory; memoryMB?: number; + logging?: ConversationHandlerFunctionLoggingOptions; }; // @public (undocumented) diff --git a/packages/backend-ai/src/conversation/factory.test.ts b/packages/backend-ai/src/conversation/factory.test.ts index 9802e4944bc..a264731ffb2 100644 --- a/packages/backend-ai/src/conversation/factory.test.ts +++ b/packages/backend-ai/src/conversation/factory.test.ts @@ -203,4 +203,41 @@ void describe('ConversationHandlerFactory', () => { MemorySize: 271, }); }); + + void it('passes log level to construct', () => { + const factory = defineConversationHandlerFunction({ + entry: './test-assets/with-default-entry/handler.ts', + name: 'testHandlerName', + models: [], + logging: { + level: 'debug', + }, + }); + const lambda = factory.getInstance(getInstanceProps); + const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('AWS::Lambda::Function', { + LoggingConfig: { + ApplicationLogLevel: 'DEBUG', + LogFormat: 'JSON', + }, + }); + }); + + void it('passes log retention to construct', () => { + const factory = defineConversationHandlerFunction({ + entry: './test-assets/with-default-entry/handler.ts', + name: 'testHandlerName', + models: [], + logging: { + retention: '1 day', + }, + }); + const lambda = factory.getInstance(getInstanceProps); + const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('AWS::Logs::LogGroup', { + RetentionInDays: 1, + }); + }); }); diff --git a/packages/backend-ai/src/conversation/factory.ts b/packages/backend-ai/src/conversation/factory.ts index 6b4242288ff..d5a88c3f03e 100644 --- a/packages/backend-ai/src/conversation/factory.ts +++ b/packages/backend-ai/src/conversation/factory.ts @@ -7,6 +7,8 @@ import { ConstructFactoryGetInstanceProps, FunctionResources, GenerateContainerEntryProps, + LogLevel, + LogRetention, ResourceProvider, } from '@aws-amplify/plugin-types'; import { @@ -17,6 +19,10 @@ import { import path from 'path'; import { CallerDirectoryExtractor } from '@aws-amplify/platform-core'; import { AiModel } from '@aws-amplify/data-schema-types'; +import { + LogLevelConverter, + LogRetentionConverter, +} from '@aws-amplify/platform-core/cdk'; class ConversationHandlerFunctionGenerator implements ConstructContainerEntryGenerator @@ -47,6 +53,18 @@ class ConversationHandlerFunctionGenerator outputStorageStrategy: this.outputStorageStrategy, memoryMB: this.props.memoryMB, }; + const logging: typeof constructProps.logging = {}; + if (this.props.logging?.level) { + logging.level = new LogLevelConverter().toCDKLambdaApplicationLogLevel( + this.props.logging.level + ); + } + if (this.props.logging?.retention) { + logging.retention = new LogRetentionConverter().toCDKRetentionDays( + this.props.logging.retention + ); + } + constructProps.logging = logging; const conversationHandlerFunction = new ConversationHandlerFunction( scope, this.props.name, @@ -115,6 +133,15 @@ class DefaultConversationHandlerFunctionFactory }; } +export type ConversationHandlerFunctionLogLevel = LogLevel; + +export type ConversationHandlerFunctionLogRetention = LogRetention; + +export type ConversationHandlerFunctionLoggingOptions = { + retention?: ConversationHandlerFunctionLogRetention; + level?: ConversationHandlerFunctionLogLevel; +}; + export type DefineConversationHandlerFunctionProps = { name: string; entry?: string; @@ -128,6 +155,7 @@ export type DefineConversationHandlerFunctionProps = { * Default is 512MB. */ memoryMB?: number; + logging?: ConversationHandlerFunctionLoggingOptions; }; /** diff --git a/packages/backend-ai/src/conversation/index.ts b/packages/backend-ai/src/conversation/index.ts index 489209c219d..69eb8d3c5cb 100644 --- a/packages/backend-ai/src/conversation/index.ts +++ b/packages/backend-ai/src/conversation/index.ts @@ -1,11 +1,17 @@ import { ConversationHandlerFunctionFactory, + ConversationHandlerFunctionLogLevel, + ConversationHandlerFunctionLogRetention, + ConversationHandlerFunctionLoggingOptions, DefineConversationHandlerFunctionProps, defineConversationHandlerFunction, } from './factory.js'; export { ConversationHandlerFunctionFactory, + ConversationHandlerFunctionLogLevel, + ConversationHandlerFunctionLogRetention, + ConversationHandlerFunctionLoggingOptions, DefineConversationHandlerFunctionProps, defineConversationHandlerFunction, }; diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index 8e0ad66d853..500972914db 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -8,6 +8,8 @@ import { AmplifyResourceGroupName } from '@aws-amplify/plugin-types'; import { BackendSecret } from '@aws-amplify/plugin-types'; import { ConstructFactory } from '@aws-amplify/plugin-types'; import { FunctionResources } from '@aws-amplify/plugin-types'; +import { LogLevel } from '@aws-amplify/plugin-types'; +import { LogRetention } from '@aws-amplify/plugin-types'; import { ResourceAccessAcceptorFactory } from '@aws-amplify/plugin-types'; import { ResourceProvider } from '@aws-amplify/plugin-types'; import { StackProvider } from '@aws-amplify/plugin-types'; @@ -28,6 +30,22 @@ export type FunctionBundlingOptions = { minify?: boolean; }; +// @public (undocumented) +export type FunctionLoggingOptions = ({ + format: 'json'; + level?: FunctionLogLevel; +} | { + format?: 'text'; +}) & { + retention?: FunctionLogRetention; +}; + +// @public (undocumented) +export type FunctionLogLevel = LogLevel; + +// @public (undocumented) +export type FunctionLogRetention = LogRetention; + // @public (undocumented) export type FunctionProps = { name?: string; @@ -40,6 +58,7 @@ export type FunctionProps = { layers?: Record; bundling?: FunctionBundlingOptions; resourceGroupName?: AmplifyResourceGroupName; + logging?: FunctionLoggingOptions; }; // @public (undocumented) diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index c7ce48a2cfe..8b964f336c2 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -440,6 +440,39 @@ void describe('AmplifyFunctionFactory', () => { }); }); + void describe('logging options', () => { + void it('sets logging options', () => { + const lambda = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + bundling: { + minify: false, + }, + logging: { + format: 'json', + level: 'warn', + retention: '13 months', + }, + }).getInstance(getInstanceProps); + const template = Template.fromStack(lambda.stack); + // Enabling log retention adds extra lambda. + template.resourceCountIs('AWS::Lambda::Function', 2); + const lambdas = template.findResources('AWS::Lambda::Function'); + assert.ok( + Object.keys(lambdas).some((key) => key.startsWith('LogRetention')) + ); + template.hasResourceProperties('Custom::LogRetention', { + RetentionInDays: 400, + }); + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'index.handler', + LoggingConfig: { + ApplicationLogLevel: 'WARN', + LogFormat: 'JSON', + }, + }); + }); + }); + void describe('resourceAccessAcceptor', () => { void it('attaches policy to execution role and configures ssm environment context', () => { const functionFactory = defineFunction({ diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 9c29357c217..e752b46d44e 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -18,6 +18,8 @@ import { ConstructFactoryGetInstanceProps, FunctionResources, GenerateContainerEntryProps, + LogLevel, + LogRetention, ResourceAccessAcceptorFactory, ResourceNameValidator, ResourceProvider, @@ -45,6 +47,7 @@ import { FunctionEnvironmentTranslator } from './function_env_translator.js'; import { FunctionEnvironmentTypeGenerator } from './function_env_type_generator.js'; import { FunctionLayerArnParser } from './layer_parser.js'; import { convertFunctionSchedulesToRuleSchedules } from './schedule_parser.js'; +import { convertLoggingOptionsToCDK } from './logging_options_parser.js'; const functionStackType = 'function-Lambda'; @@ -64,6 +67,9 @@ export type TimeInterval = | `every year`; export type FunctionSchedule = TimeInterval | CronSchedule; +export type FunctionLogLevel = LogLevel; +export type FunctionLogRetention = LogRetention; + /** * Entry point for defining a function in the Amplify ecosystem */ @@ -159,6 +165,8 @@ export type FunctionProps = { * resourceGroupName: 'auth' // to group an auth trigger with an auth resource */ resourceGroupName?: AmplifyResourceGroupName; + + logging?: FunctionLoggingOptions; }; export type FunctionBundlingOptions = { @@ -170,6 +178,18 @@ export type FunctionBundlingOptions = { minify?: boolean; }; +export type FunctionLoggingOptions = ( + | { + format: 'json'; + level?: FunctionLogLevel; + } + | { + format?: 'text'; + } +) & { + retention?: FunctionLogRetention; +}; + /** * Create Lambda functions in the context of an Amplify backend definition */ @@ -218,6 +238,7 @@ class FunctionFactory implements ConstructFactory { bundling: this.resolveBundling(), layers, resourceGroupName: this.props.resourceGroupName ?? 'function', + logging: this.props.logging ?? {}, }; }; @@ -438,6 +459,7 @@ class AmplifyFunction functionEnvironmentTypeGenerator.generateProcessEnvShim(); let functionLambda: NodejsFunction; + const cdkLoggingOptions = convertLoggingOptionsToCDK(props.logging); try { functionLambda = new NodejsFunction(scope, `${id}-lambda`, { entry: props.entry, @@ -451,6 +473,9 @@ class AmplifyFunction inject: shims, externalModules: Object.keys(props.layers), }, + logRetention: cdkLoggingOptions.retention, + applicationLogLevelV2: cdkLoggingOptions.level, + loggingFormat: cdkLoggingOptions.format, }); } catch (error) { // If the error is from ES Bundler which is executed as a child process by CDK, diff --git a/packages/backend-function/src/logging_options_parser.test.ts b/packages/backend-function/src/logging_options_parser.test.ts new file mode 100644 index 00000000000..4d6abec9589 --- /dev/null +++ b/packages/backend-function/src/logging_options_parser.test.ts @@ -0,0 +1,56 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert'; +import { FunctionLoggingOptions } from './factory.js'; +import { + CDKLoggingOptions, + convertLoggingOptionsToCDK, +} from './logging_options_parser.js'; +import { ApplicationLogLevel, LoggingFormat } from 'aws-cdk-lib/aws-lambda'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; + +type TestCase = { + input: FunctionLoggingOptions; + expectedOutput: CDKLoggingOptions; +}; + +const testCases: Array = [ + { + input: {}, + expectedOutput: { + format: undefined, + level: undefined, + retention: undefined, + }, + }, + { + input: { + format: 'text', + retention: '13 months', + }, + expectedOutput: { + format: LoggingFormat.TEXT, + retention: RetentionDays.THIRTEEN_MONTHS, + level: undefined, + }, + }, + { + input: { + format: 'json', + level: 'debug', + }, + expectedOutput: { + format: LoggingFormat.JSON, + retention: undefined, + level: ApplicationLogLevel.DEBUG, + }, + }, +]; + +void describe('LoggingOptions converter', () => { + testCases.forEach((testCase, index) => { + void it(`converts to cdk options[${index}]`, () => { + const convertedOptions = convertLoggingOptionsToCDK(testCase.input); + assert.deepStrictEqual(convertedOptions, testCase.expectedOutput); + }); + }); +}); diff --git a/packages/backend-function/src/logging_options_parser.ts b/packages/backend-function/src/logging_options_parser.ts new file mode 100644 index 00000000000..ce5693d6253 --- /dev/null +++ b/packages/backend-function/src/logging_options_parser.ts @@ -0,0 +1,48 @@ +import { FunctionLoggingOptions } from './factory.js'; +import { ApplicationLogLevel, LoggingFormat } from 'aws-cdk-lib/aws-lambda'; +import { + LogLevelConverter, + LogRetentionConverter, +} from '@aws-amplify/platform-core/cdk'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; + +export type CDKLoggingOptions = { + level?: ApplicationLogLevel; + retention?: RetentionDays; + format?: LoggingFormat; +}; + +/** + * Converts logging options to CDK. + */ +export const convertLoggingOptionsToCDK = ( + loggingOptions: FunctionLoggingOptions +): CDKLoggingOptions => { + let level: ApplicationLogLevel | undefined = undefined; + if ('level' in loggingOptions) { + level = new LogLevelConverter().toCDKLambdaApplicationLogLevel( + loggingOptions.level + ); + } + const retention = new LogRetentionConverter().toCDKRetentionDays( + loggingOptions.retention + ); + const format = convertFormat(loggingOptions.format); + + return { + level, + retention, + format, + }; +}; + +const convertFormat = (format: 'json' | 'text' | undefined) => { + switch (format) { + case undefined: + return undefined; + case 'json': + return LoggingFormat.JSON; + case 'text': + return LoggingFormat.TEXT; + } +}; diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index 286ae416207..55997c87478 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -5,10 +5,22 @@ ```ts import { AppId } from '@aws-amplify/plugin-types'; +import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; import { DeepPartialAmplifyGeneratedConfigs } from '@aws-amplify/plugin-types'; +import { LogLevel } from '@aws-amplify/plugin-types'; +import { LogRetention } from '@aws-amplify/plugin-types'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; import z from 'zod'; +declare namespace __export__cdk { + export { + LogLevelConverter, + LogRetentionConverter + } +} +export { __export__cdk } + // @public export abstract class AmplifyError extends Error { constructor(name: T, classification: AmplifyErrorClassification, options: AmplifyErrorOptions, cause?: Error | undefined); @@ -121,6 +133,18 @@ export class FilePathExtractor { // @public (undocumented) export type LocalConfigurationFileName = 'usage_data_preferences.json'; +// @public +class LogLevelConverter { + // (undocumented) + toCDKLambdaApplicationLogLevel: (logLevel: LogLevel | undefined) => ApplicationLogLevel | undefined; +} + +// @public +class LogRetentionConverter { + // (undocumented) + toCDKRetentionDays: (retention: LogRetention | undefined) => RetentionDays | undefined; +} + // @public export class ObjectAccumulator { constructor(accumulator: DeepPartialAmplifyGeneratedConfigs, versionKey?: string); diff --git a/packages/platform-core/api-extractor.json b/packages/platform-core/api-extractor.json index 0f56de03f66..cc2ebea8cf9 100644 --- a/packages/platform-core/api-extractor.json +++ b/packages/platform-core/api-extractor.json @@ -1,3 +1,4 @@ { - "extends": "../../api-extractor.base.json" + "extends": "../../api-extractor.base.json", + "mainEntryPointFilePath": "/lib/index.internal.d.ts" } diff --git a/packages/platform-core/package.json b/packages/platform-core/package.json index 890f108a4b2..131980b90a2 100644 --- a/packages/platform-core/package.json +++ b/packages/platform-core/package.json @@ -10,6 +10,11 @@ "types": "./lib/index.d.ts", "import": "./lib/index.js", "require": "./lib/index.js" + }, + "./cdk": { + "types": "./lib/cdk/index.d.ts", + "import": "./lib/cdk/index.js", + "require": "./lib/cdk/index.js" } }, "main": "lib/index.js", @@ -31,5 +36,9 @@ "semver": "^7.6.3", "uuid": "^9.0.1", "zod": "^3.22.2" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.0.0" } } diff --git a/packages/platform-core/src/.eslintrc.json b/packages/platform-core/src/.eslintrc.json new file mode 100644 index 00000000000..0da8b97bbb6 --- /dev/null +++ b/packages/platform-core/src/.eslintrc.json @@ -0,0 +1,15 @@ +{ + "rules": { + "no-restricted-imports": [ + "error", + { + "patterns": [ + { + "group": ["aws-cdk-lib", "aws-cdk-lib/*", "constructs"], + "message": "Usage of CDK lib is not allowed in platform-core. Except /cdk entry point. This is to ensure that we don't load CDK eagerly from package root." + } + ] + } + ] + } +} diff --git a/packages/platform-core/src/cdk/.eslintrc.json b/packages/platform-core/src/cdk/.eslintrc.json new file mode 100644 index 00000000000..c3220a910c1 --- /dev/null +++ b/packages/platform-core/src/cdk/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "rules": { + "no-restricted-imports": "off" + } +} diff --git a/packages/platform-core/src/cdk/enum_converters.test.ts b/packages/platform-core/src/cdk/enum_converters.test.ts new file mode 100644 index 00000000000..19edc0d1441 --- /dev/null +++ b/packages/platform-core/src/cdk/enum_converters.test.ts @@ -0,0 +1,162 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert'; +import { LogLevel, LogRetention } from '@aws-amplify/plugin-types'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; +import { LogLevelConverter, LogRetentionConverter } from './enum_converters'; +import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda'; + +type TestCase = { + input: TSource | undefined; + expectedOutput: TTarget | undefined; +}; + +void describe('LogRetentionConverter', () => { + const testCases: Array> = [ + { + input: undefined, + expectedOutput: undefined, + }, + { + input: '1 day', + expectedOutput: RetentionDays.ONE_DAY, + }, + { + input: '3 days', + expectedOutput: RetentionDays.THREE_DAYS, + }, + { + input: '5 days', + expectedOutput: RetentionDays.FIVE_DAYS, + }, + { + input: '1 week', + expectedOutput: RetentionDays.ONE_WEEK, + }, + { + input: '2 weeks', + expectedOutput: RetentionDays.TWO_WEEKS, + }, + { + input: '1 month', + expectedOutput: RetentionDays.ONE_MONTH, + }, + { + input: '2 months', + expectedOutput: RetentionDays.TWO_MONTHS, + }, + { + input: '3 months', + expectedOutput: RetentionDays.THREE_MONTHS, + }, + { + input: '4 months', + expectedOutput: RetentionDays.FOUR_MONTHS, + }, + { + input: '5 months', + expectedOutput: RetentionDays.FIVE_MONTHS, + }, + { + input: '6 months', + expectedOutput: RetentionDays.SIX_MONTHS, + }, + { + input: '13 months', + expectedOutput: RetentionDays.THIRTEEN_MONTHS, + }, + { + input: '18 months', + expectedOutput: RetentionDays.EIGHTEEN_MONTHS, + }, + { + input: '1 year', + expectedOutput: RetentionDays.ONE_YEAR, + }, + { + input: '2 years', + expectedOutput: RetentionDays.TWO_YEARS, + }, + { + input: '3 years', + expectedOutput: RetentionDays.THREE_YEARS, + }, + { + input: '5 years', + expectedOutput: RetentionDays.FIVE_YEARS, + }, + { + input: '6 years', + expectedOutput: RetentionDays.SIX_YEARS, + }, + { + input: '7 years', + expectedOutput: RetentionDays.SEVEN_YEARS, + }, + { + input: '8 years', + expectedOutput: RetentionDays.EIGHT_YEARS, + }, + { + input: '9 years', + expectedOutput: RetentionDays.NINE_YEARS, + }, + { + input: '10 years', + expectedOutput: RetentionDays.TEN_YEARS, + }, + { + input: 'infinite', + expectedOutput: RetentionDays.INFINITE, + }, + ]; + + testCases.forEach((testCase, index) => { + void it(`converts log retention[${index}]`, () => { + const convertedValue = new LogRetentionConverter().toCDKRetentionDays( + testCase.input + ); + assert.strictEqual(convertedValue, testCase.expectedOutput); + }); + }); +}); + +void describe('LogLevelConverter', () => { + const testCases: Array> = [ + { + input: undefined, + expectedOutput: undefined, + }, + { + input: 'info', + expectedOutput: ApplicationLogLevel.INFO, + }, + { + input: 'debug', + expectedOutput: ApplicationLogLevel.DEBUG, + }, + { + input: 'error', + expectedOutput: ApplicationLogLevel.ERROR, + }, + { + input: 'warn', + expectedOutput: ApplicationLogLevel.WARN, + }, + { + input: 'trace', + expectedOutput: ApplicationLogLevel.TRACE, + }, + { + input: 'fatal', + expectedOutput: ApplicationLogLevel.FATAL, + }, + ]; + + testCases.forEach((testCase, index) => { + void it(`converts log retention[${index}]`, () => { + const convertedValue = + new LogLevelConverter().toCDKLambdaApplicationLogLevel(testCase.input); + assert.strictEqual(convertedValue, testCase.expectedOutput); + }); + }); +}); diff --git a/packages/platform-core/src/cdk/enum_converters.ts b/packages/platform-core/src/cdk/enum_converters.ts new file mode 100644 index 00000000000..3f467785a0c --- /dev/null +++ b/packages/platform-core/src/cdk/enum_converters.ts @@ -0,0 +1,97 @@ +import { LogLevel, LogRetention } from '@aws-amplify/plugin-types'; +import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; + +/** + * Converts LogRetention to CDK types. + */ +export class LogRetentionConverter { + toCDKRetentionDays = ( + retention: LogRetention | undefined + ): RetentionDays | undefined => { + switch (retention) { + case undefined: + return undefined; + + case '1 day': + return RetentionDays.ONE_DAY; + case '3 days': + return RetentionDays.THREE_DAYS; + case '5 days': + return RetentionDays.FIVE_DAYS; + case '1 week': + return RetentionDays.ONE_WEEK; + case '2 weeks': + return RetentionDays.TWO_WEEKS; + case '1 month': + return RetentionDays.ONE_MONTH; + case '2 months': + return RetentionDays.TWO_MONTHS; + case '3 months': + return RetentionDays.THREE_MONTHS; + case '4 months': + return RetentionDays.FOUR_MONTHS; + case '5 months': + return RetentionDays.FIVE_MONTHS; + case '6 months': + return RetentionDays.SIX_MONTHS; + case '1 year': + return RetentionDays.ONE_YEAR; + case '13 months': + return RetentionDays.THIRTEEN_MONTHS; + case '18 months': + return RetentionDays.EIGHTEEN_MONTHS; + case '2 years': + return RetentionDays.TWO_YEARS; + case '3 years': + return RetentionDays.THREE_YEARS; + case '5 years': + return RetentionDays.FIVE_YEARS; + case '6 years': + return RetentionDays.SIX_YEARS; + case '7 years': + return RetentionDays.SEVEN_YEARS; + case '8 years': + return RetentionDays.EIGHT_YEARS; + case '9 years': + return RetentionDays.NINE_YEARS; + case '10 years': + return RetentionDays.TEN_YEARS; + case 'infinite': + return RetentionDays.INFINITE; + } + }; +} + +/** + * Converts LogLevel to CDK types. + */ +export class LogLevelConverter { + toCDKLambdaApplicationLogLevel = ( + logLevel: LogLevel | undefined + ): ApplicationLogLevel | undefined => { + switch (logLevel) { + case undefined: { + return undefined; + } + case 'info': { + return ApplicationLogLevel.INFO; + } + case 'debug': { + return ApplicationLogLevel.DEBUG; + } + case 'warn': { + return ApplicationLogLevel.WARN; + } + case 'error': { + return ApplicationLogLevel.ERROR; + } + case 'fatal': { + return ApplicationLogLevel.FATAL; + } + case 'trace': { + return ApplicationLogLevel.TRACE; + } + } + }; +} diff --git a/packages/platform-core/src/cdk/index.ts b/packages/platform-core/src/cdk/index.ts new file mode 100644 index 00000000000..d5c9d704c97 --- /dev/null +++ b/packages/platform-core/src/cdk/index.ts @@ -0,0 +1,3 @@ +import { LogLevelConverter, LogRetentionConverter } from './enum_converters.js'; + +export { LogLevelConverter, LogRetentionConverter }; diff --git a/packages/platform-core/src/index.internal.ts b/packages/platform-core/src/index.internal.ts new file mode 100644 index 00000000000..336a22d74e2 --- /dev/null +++ b/packages/platform-core/src/index.internal.ts @@ -0,0 +1,12 @@ +// Suppressing to allow special prefix __export__ that is recognized by API checks. +// eslint-disable-next-line @typescript-eslint/naming-convention +import * as __export__cdk from './cdk/index.js'; + +export * from './index.js'; + +/* + Api-extractor does not ([yet](https://github.com/microsoft/rushstack/issues/1596)) support multiple package entry points + Because this package has a submodule export, we are working around this issue by including that export here and directing api-extract to this entry point instead + This allows api-extractor to pick up the submodule exports in its analysis + */ +export { __export__cdk }; diff --git a/packages/plugin-types/API.md b/packages/plugin-types/API.md index 2b78dd2ae1c..832ba6c782d 100644 --- a/packages/plugin-types/API.md +++ b/packages/plugin-types/API.md @@ -175,6 +175,12 @@ export type ImportPathVerifier = { verify: (importStack: string | undefined, expectedImportingFile: string, errorMessage: string) => void; }; +// @public (undocumented) +export type LogLevel = 'info' | 'debug' | 'warn' | 'error' | 'fatal' | 'trace'; + +// @public (undocumented) +export type LogRetention = '1 day' | '3 days' | '5 days' | '1 week' | '2 weeks' | '1 month' | '2 months' | '3 months' | '4 months' | '5 months' | '6 months' | '1 year' | '13 months' | '18 months' | '2 years' | '3 years' | '5 years' | '6 years' | '7 years' | '8 years' | '9 years' | '10 years' | 'infinite'; + // @public export type MainStackCreator = { getOrCreateMainStack: () => Stack; diff --git a/packages/plugin-types/src/index.ts b/packages/plugin-types/src/index.ts index 780c52ec02c..8d738f7b2ff 100644 --- a/packages/plugin-types/src/index.ts +++ b/packages/plugin-types/src/index.ts @@ -22,3 +22,5 @@ export * from './resource_name_validator.js'; export * from './aws_client_provider.js'; export * from './stack_provider.js'; export * from './amplify_resource_group_name.js'; +export * from './log_level.js'; +export * from './log_retention.js'; diff --git a/packages/plugin-types/src/log_level.ts b/packages/plugin-types/src/log_level.ts new file mode 100644 index 00000000000..5f4d23c0087 --- /dev/null +++ b/packages/plugin-types/src/log_level.ts @@ -0,0 +1 @@ +export type LogLevel = 'info' | 'debug' | 'warn' | 'error' | 'fatal' | 'trace'; diff --git a/packages/plugin-types/src/log_retention.ts b/packages/plugin-types/src/log_retention.ts new file mode 100644 index 00000000000..60210c721ba --- /dev/null +++ b/packages/plugin-types/src/log_retention.ts @@ -0,0 +1,24 @@ +export type LogRetention = + | '1 day' + | '3 days' + | '5 days' + | '1 week' + | '2 weeks' + | '1 month' + | '2 months' + | '3 months' + | '4 months' + | '5 months' + | '6 months' + | '1 year' + | '13 months' + | '18 months' + | '2 years' + | '3 years' + | '5 years' + | '6 years' + | '7 years' + | '8 years' + | '9 years' + | '10 years' + | 'infinite'; From 37d8564014934f681d24c05e86ce00c457ef0932 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Wed, 27 Nov 2024 19:38:52 +0100 Subject: [PATCH 130/199] fix: handle cdk error mapping for more generic invalid credentials (#2279) --- .changeset/fair-ghosts-wave.md | 5 +++++ packages/backend-deployer/src/cdk_error_mapper.test.ts | 9 +++++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .changeset/fair-ghosts-wave.md diff --git a/.changeset/fair-ghosts-wave.md b/.changeset/fair-ghosts-wave.md new file mode 100644 index 00000000000..41ace980f0a --- /dev/null +++ b/.changeset/fair-ghosts-wave.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +handle cdk error mapping for more generic invalid credentials diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index be08eedc179..3850467a359 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -31,6 +31,15 @@ const testErrorMappings = [ expectedDownstreamErrorMessage: 'Error: The security token included in the request is expired', }, + { + errorMessage: + 'InvalidClientTokenId: The security token included in the request is invalid', + expectedTopLevelErrorMessage: + 'The security token included in the request is invalid.', + errorName: 'ExpiredTokenError', + expectedDownstreamErrorMessage: + 'InvalidClientTokenId: The security token included in the request is invalid', + }, { errorMessage: 'Access Denied', expectedTopLevelErrorMessage: diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 67d5159d50f..949a9212f3f 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -99,7 +99,7 @@ export class CdkErrorMapper { }> => [ { errorRegex: - /ExpiredToken|Error: The security token included in the request is expired/, + /ExpiredToken|(Error|InvalidClientTokenId): The security token included in the request is (expired|invalid)/, humanReadableErrorMessage: 'The security token included in the request is invalid.', resolutionMessage: From ec4a7da61f2db4625f8a041f4a139b29258d3330 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Wed, 27 Nov 2024 12:11:54 -0800 Subject: [PATCH 131/199] bump cdk (#2280) --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index c60a804d40e..97fb224519f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19581,9 +19581,9 @@ "license": "0BSD" }, "node_modules/aws-cdk": { - "version": "2.171.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.171.0.tgz", - "integrity": "sha512-tVo4hYS0iAbiCFxUh2/7KoDL6EHEIUAurCJaBs2BOUAB9DfBuUAPp8DGUK4iVF8XzrQQ4f3a5ivN7DteQrGBEQ==", + "version": "2.171.1", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.171.1.tgz", + "integrity": "sha512-IWENyT4F5UcLr1szLsbipUdjIHn8FD3d/RvaIvhs2+qCamkfEV5mqv/ChMvRJ8H2jebhIZ2iz74or9O5Ismp+Q==", "license": "Apache-2.0", "peer": true, "bin": { @@ -19597,9 +19597,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.171.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.171.0.tgz", - "integrity": "sha512-N0O0mWI+S8PAbiED7eV05qVfbJgHkdh+jQS4BOG6CUAc/i2s9U4w+XDRkK8auO0HgTM9+ahEaFfucMuQ4abRWQ==", + "version": "2.171.1", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.171.1.tgz", + "integrity": "sha512-BmXodHmeOWu7EZMwXFA+Mp+SnlZgIwhMxfOmqpdGa5dXF4BWOrs0cm4YgrzcJkg0XK713eXPj5IWGj8YeRIU3g==", "bundleDependencies": [ "@balena/dockerignore", "case", From cfdc8540fddba9df47d702f51fa1192fdc3d3125 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:51:41 +0100 Subject: [PATCH 132/199] fix: return amplify user error as it is from `AmplifyError.fromError` (#2288) * fix: return amplify user error as it is from 'AmplifyError.fromError' * PR Updates --- .changeset/five-comics-beg.md | 5 +++++ packages/platform-core/API.md | 2 +- .../src/errors/amplify_error.test.ts | 9 +++++++++ .../platform-core/src/errors/amplify_error.ts | 15 +++++---------- 4 files changed, 20 insertions(+), 11 deletions(-) create mode 100644 .changeset/five-comics-beg.md diff --git a/.changeset/five-comics-beg.md b/.changeset/five-comics-beg.md new file mode 100644 index 00000000000..10a4c35dd0e --- /dev/null +++ b/.changeset/five-comics-beg.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/platform-core': patch +--- + +return amplify user error as it is from `AmplifyError.fromError` diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index 55997c87478..f40d1c53648 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -33,7 +33,7 @@ export abstract class AmplifyError extends Error { // (undocumented) readonly details?: string; // (undocumented) - static fromError: (error: unknown) => AmplifyError<'UnknownFault' | 'CredentialsError' | 'InsufficientDiskSpaceError' | 'InvalidCommandInputError' | 'DomainNotFoundError' | 'SyntaxError'>; + static fromError: (error: unknown) => AmplifyError; // (undocumented) static fromStderr: (_stderr: string) => AmplifyError | undefined; static isAmplifyError: (error: unknown) => error is AmplifyError; diff --git a/packages/platform-core/src/errors/amplify_error.test.ts b/packages/platform-core/src/errors/amplify_error.test.ts index 94980ab2132..c4f0c974f46 100644 --- a/packages/platform-core/src/errors/amplify_error.test.ts +++ b/packages/platform-core/src/errors/amplify_error.test.ts @@ -205,4 +205,13 @@ void describe('AmplifyError.fromError', async () => { ); }); }); + void it('return amplify user errors as it is', () => { + const error = new AmplifyUserError('DeploymentInProgressError', { + message: 'Deployment already in progress', + resolution: 'wait for it', + }); + const actual = AmplifyError.fromError(error); + assert.deepStrictEqual(error, actual); + assert.strictEqual(actual.resolution, error.resolution); + }); }); diff --git a/packages/platform-core/src/errors/amplify_error.ts b/packages/platform-core/src/errors/amplify_error.ts index 9ea73c8ff1d..314444252e8 100644 --- a/packages/platform-core/src/errors/amplify_error.ts +++ b/packages/platform-core/src/errors/amplify_error.ts @@ -118,16 +118,11 @@ export abstract class AmplifyError extends Error { ); }; - static fromError = ( - error: unknown - ): AmplifyError< - | 'UnknownFault' - | 'CredentialsError' - | 'InsufficientDiskSpaceError' - | 'InvalidCommandInputError' - | 'DomainNotFoundError' - | 'SyntaxError' - > => { + static fromError = (error: unknown): AmplifyError => { + if (AmplifyError.isAmplifyError(error)) { + return error; + } + const errorMessage = error instanceof Error ? `${error.name}: ${error.message}` From 1f56a5f5c4ff94476c1f38de9f4b5dde054c0a7c Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Tue, 3 Dec 2024 10:07:15 -0800 Subject: [PATCH 133/199] Add hotswap to canaries (#2289) * Add hotswap to canaries * try this * try this * try this * try this * try this * change this * pr feedback * pr feedback --- .changeset/twenty-baboons-rule.md | 2 + .eslint_dictionary.json | 2 + .../predicated_action_macros.ts | 6 + .../test-e2e/sandbox/sandbox.test.template.ts | 8 +- .../health_checks.test.ts | 49 +++++- .../README.md | 5 + .../function_code_hotswap.ts | 143 ++++++++++++++++++ .../test-project-setup/test_project_base.ts | 2 +- .../README.md | 5 + .../function-code-hotswap/amplify/backend.ts | 4 + .../amplify/func-src/handler.ts | 6 + .../function-code-hotswap/amplify/function.ts | 7 + .../hotswap-update-files/func-src/handler.ts | 6 + 13 files changed, 242 insertions(+), 3 deletions(-) create mode 100644 .changeset/twenty-baboons-rule.md create mode 100644 packages/integration-tests/src/test-project-setup/live-dependency-health-checks-projects/README.md create mode 100644 packages/integration-tests/src/test-project-setup/live-dependency-health-checks-projects/function_code_hotswap.ts create mode 100644 packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/README.md create mode 100644 packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/amplify/backend.ts create mode 100644 packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/amplify/func-src/handler.ts create mode 100644 packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/amplify/function.ts create mode 100644 packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/hotswap-update-files/func-src/handler.ts diff --git a/.changeset/twenty-baboons-rule.md b/.changeset/twenty-baboons-rule.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/twenty-baboons-rule.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index 4f0f494eee8..71e74fbd53b 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -75,7 +75,9 @@ "homedir", "hotfix", "hotswap", + "hotswappable", "hotswapped", + "hotswapping", "iamv2", "identitypool", "idps", diff --git a/packages/integration-tests/src/process-controller/predicated_action_macros.ts b/packages/integration-tests/src/process-controller/predicated_action_macros.ts index 4fff93a7265..efa6bbc178b 100644 --- a/packages/integration-tests/src/process-controller/predicated_action_macros.ts +++ b/packages/integration-tests/src/process-controller/predicated_action_macros.ts @@ -30,6 +30,12 @@ export const waitForSandboxToBecomeIdle = () => 'Watching for file changes...' ); +/** + * Reusable predicates: Wait for sandbox to indicate that it's executing hotswap deployment, i.e. "hotswapping resources:" + */ +export const waitForSandboxToBeginHotswappingResources = () => + new PredicatedActionBuilder().waitForLineIncludes('hotswapping resources:'); + /** * Reusable predicated action: Wait for sandbox delete to prompt to delete all the resource and respond with yes */ diff --git a/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts b/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts index 9b24b0926d6..599cb4dae82 100644 --- a/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts +++ b/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts @@ -13,6 +13,7 @@ import { interruptSandbox, replaceFiles, waitForConfigUpdateAfterDeployment, + waitForSandboxToBeginHotswappingResources, } from '../../process-controller/predicated_action_macros.js'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; import { testConcurrencyLevel } from '../test_concurrency.js'; @@ -93,7 +94,12 @@ export const defineSandboxTest = (testProjectCreator: TestProjectCreator) => { for (const update of updates) { processController .do(replaceFiles(update.replacements)) - .do(ensureDeploymentTimeLessThan(update.deployThresholdSec)); + .do(waitForSandboxToBeginHotswappingResources()); + if (update.deployThresholdSec) { + processController.do( + ensureDeploymentTimeLessThan(update.deployThresholdSec) + ); + } } // Execute the process. diff --git a/packages/integration-tests/src/test-live-dependency-health-checks/health_checks.test.ts b/packages/integration-tests/src/test-live-dependency-health-checks/health_checks.test.ts index 19b475a9177..998e2a4afa8 100644 --- a/packages/integration-tests/src/test-live-dependency-health-checks/health_checks.test.ts +++ b/packages/integration-tests/src/test-live-dependency-health-checks/health_checks.test.ts @@ -1,7 +1,7 @@ import { afterEach, before, beforeEach, describe, it } from 'node:test'; import fs from 'fs/promises'; import path from 'path'; -import os from 'os'; +import os, { userInfo } from 'os'; import { execa } from 'execa'; import { ampxCli } from '../process-controller/process_controller.js'; import { TestBranch, amplifyAppPool } from '../amplify_app_pool.js'; @@ -14,11 +14,15 @@ import { import { confirmDeleteSandbox, interruptSandbox, + replaceFiles, waitForSandboxDeploymentToPrintTotalTime, + waitForSandboxToBeginHotswappingResources, } from '../process-controller/predicated_action_macros.js'; import { BackendIdentifierConversions } from '@aws-amplify/platform-core'; import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; import { amplifyAtTag } from '../constants.js'; +import { FunctionCodeHotswapTestProjectCreator } from '../test-project-setup/live-dependency-health-checks-projects/function_code_hotswap.js'; +import { BackendIdentifier } from '@aws-amplify/plugin-types'; const cfnClient = new CloudFormationClient(e2eToolingClientConfig); @@ -134,4 +138,47 @@ void describe('Live dependency health checks', { concurrency: true }, () => { .run(); }); }); + + void describe('sandbox hotswap', () => { + let tempDir: string; + + beforeEach(async () => { + tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'test-amplify')); + }); + + afterEach(async () => { + await fs.rm(tempDir, { recursive: true }); + }); + + void it('can hotswap function code', async () => { + const projectCreator = new FunctionCodeHotswapTestProjectCreator(); + const testProject = await projectCreator.createProject(tempDir); + + const sandboxBackendIdentifier: BackendIdentifier = { + type: 'sandbox', + namespace: testProject.name, + name: userInfo().username, + }; + + await testProject.deploy(sandboxBackendIdentifier); + + const processController = ampxCli( + ['sandbox', '--dirToWatch', 'amplify'], + testProject.projectDirPath + ); + const updates = await testProject.getUpdates(); + for (const update of updates) { + processController + .do(replaceFiles(update.replacements)) + .do(waitForSandboxToBeginHotswappingResources()) + .do(waitForSandboxDeploymentToPrintTotalTime()); + } + + // Execute the process. + await processController.do(interruptSandbox()).run(); + + // Clean up + await testProject.tearDown(sandboxBackendIdentifier); + }); + }); }); diff --git a/packages/integration-tests/src/test-project-setup/live-dependency-health-checks-projects/README.md b/packages/integration-tests/src/test-project-setup/live-dependency-health-checks-projects/README.md new file mode 100644 index 00000000000..03e80634532 --- /dev/null +++ b/packages/integration-tests/src/test-project-setup/live-dependency-health-checks-projects/README.md @@ -0,0 +1,5 @@ +Projects in this directory are meant for `live-dependency-health-checks` (aka canaries). + +1. These projects must not be used in e2e tests to provide deep functional coverage. +2. These projects must be lightweight to provide fast runtime and stability. +3. These projects must cover only P0 scenarios we care most. (That are not covered by "getting started" flow, aka `create-amplify`). diff --git a/packages/integration-tests/src/test-project-setup/live-dependency-health-checks-projects/function_code_hotswap.ts b/packages/integration-tests/src/test-project-setup/live-dependency-health-checks-projects/function_code_hotswap.ts new file mode 100644 index 00000000000..7da4b3ffaec --- /dev/null +++ b/packages/integration-tests/src/test-project-setup/live-dependency-health-checks-projects/function_code_hotswap.ts @@ -0,0 +1,143 @@ +import fs from 'fs/promises'; +import { createEmptyAmplifyProject } from '../create_empty_amplify_project.js'; +import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; +import { TestProjectBase, TestProjectUpdate } from '../test_project_base.js'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import path from 'path'; +import { TestProjectCreator } from '../test_project_creator.js'; +import { AmplifyClient } from '@aws-sdk/client-amplify'; +import { e2eToolingClientConfig } from '../../e2e_tooling_client_config.js'; +import { execa } from 'execa'; + +/** + * Creates test projects with function hotswap. + */ +export class FunctionCodeHotswapTestProjectCreator + implements TestProjectCreator +{ + readonly name = 'function-code-hotswap'; + + /** + * Creates project creator. + */ + constructor( + private readonly cfnClient: CloudFormationClient = new CloudFormationClient( + e2eToolingClientConfig + ), + private readonly amplifyClient: AmplifyClient = new AmplifyClient( + e2eToolingClientConfig + ) + ) {} + + createProject = async (e2eProjectDir: string): Promise => { + const { projectName, projectRoot, projectAmplifyDir } = + await createEmptyAmplifyProject(this.name, e2eProjectDir); + + const project = new FunctionCodeHotswapTestTestProject( + projectName, + projectRoot, + projectAmplifyDir, + this.cfnClient, + this.amplifyClient + ); + await fs.cp( + project.sourceProjectAmplifyDirURL, + project.projectAmplifyDirPath, + { + recursive: true, + } + ); + + // we're not starting from create flow. install latest versions of dependencies. + await execa( + 'npm', + [ + 'install', + '@aws-amplify/backend', + '@aws-amplify/backend-cli', + 'aws-cdk@^2', + 'aws-cdk-lib@^2', + 'constructs@^10.0.0', + 'typescript@^5.0.0', + 'tsx', + 'esbuild', + ], + { + cwd: projectRoot, + stdio: 'inherit', + } + ); + + return project; + }; +} + +/** + * Test project with function hotswap. + */ +class FunctionCodeHotswapTestTestProject extends TestProjectBase { + // Note that this is pointing to the non-compiled project directory + // This allows us to test that we are able to deploy js, cjs, ts, etc. without compiling with tsc first + readonly sourceProjectRootPath = + '../../../src/test-projects/live-dependency-health-checks-projects/function-code-hotswap'; + + readonly sourceProjectRootURL: URL = new URL( + this.sourceProjectRootPath, + import.meta.url + ); + + readonly sourceProjectAmplifyDirURL: URL = new URL( + `${this.sourceProjectRootPath}/amplify`, + import.meta.url + ); + + private readonly sourceProjectUpdateDirURL: URL = new URL( + `${this.sourceProjectRootPath}/hotswap-update-files`, + import.meta.url + ); + + /** + * Create a test project instance. + */ + constructor( + name: string, + projectDirPath: string, + projectAmplifyDirPath: string, + cfnClient: CloudFormationClient, + amplifyClient: AmplifyClient + ) { + super( + name, + projectDirPath, + projectAmplifyDirPath, + cfnClient, + amplifyClient + ); + } + + /** + * @inheritdoc + */ + override async getUpdates(): Promise { + return [ + { + replacements: [ + this.getUpdateReplacementDefinition('func-src/handler.ts'), + ], + }, + ]; + } + + private getUpdateReplacementDefinition = (suffix: string) => ({ + source: this.getSourceProjectUpdatePath(suffix), + destination: this.getTestProjectPath(suffix), + }); + + private getSourceProjectUpdatePath = (suffix: string) => + pathToFileURL( + path.join(fileURLToPath(this.sourceProjectUpdateDirURL), suffix) + ); + + private getTestProjectPath = (suffix: string) => + pathToFileURL(path.join(this.projectAmplifyDirPath, suffix)); +} diff --git a/packages/integration-tests/src/test-project-setup/test_project_base.ts b/packages/integration-tests/src/test-project-setup/test_project_base.ts index 706d68114c6..d1de5df76ab 100644 --- a/packages/integration-tests/src/test-project-setup/test_project_base.ts +++ b/packages/integration-tests/src/test-project-setup/test_project_base.ts @@ -45,7 +45,7 @@ export type TestProjectUpdate = { * Windows has a separate threshold because it is consistently slower than other platforms * https://github.com/microsoft/Windows-Dev-Performance/issues/17 */ - deployThresholdSec: PlatformDeploymentThresholds; + deployThresholdSec?: PlatformDeploymentThresholds; }; /** diff --git a/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/README.md b/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/README.md new file mode 100644 index 00000000000..03e80634532 --- /dev/null +++ b/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/README.md @@ -0,0 +1,5 @@ +Projects in this directory are meant for `live-dependency-health-checks` (aka canaries). + +1. These projects must not be used in e2e tests to provide deep functional coverage. +2. These projects must be lightweight to provide fast runtime and stability. +3. These projects must cover only P0 scenarios we care most. (That are not covered by "getting started" flow, aka `create-amplify`). diff --git a/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/amplify/backend.ts b/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/amplify/backend.ts new file mode 100644 index 00000000000..f4e88845bec --- /dev/null +++ b/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/amplify/backend.ts @@ -0,0 +1,4 @@ +import { defineBackend } from '@aws-amplify/backend'; +import { nodeFunc } from './function.js'; + +defineBackend({ nodeFunc }); diff --git a/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/amplify/func-src/handler.ts b/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/amplify/func-src/handler.ts new file mode 100644 index 00000000000..7f011bcc588 --- /dev/null +++ b/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/amplify/func-src/handler.ts @@ -0,0 +1,6 @@ +/** + * Dummy lambda handler. + */ +export const handler = async () => { + return 'Hello'; +}; diff --git a/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/amplify/function.ts b/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/amplify/function.ts new file mode 100644 index 00000000000..d05b942f896 --- /dev/null +++ b/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/amplify/function.ts @@ -0,0 +1,7 @@ +import { defineFunction } from '@aws-amplify/backend'; + +export const nodeFunc = defineFunction({ + name: 'nodeFunction', + entry: './func-src/handler.ts', + timeoutSeconds: 5, +}); diff --git a/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/hotswap-update-files/func-src/handler.ts b/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/hotswap-update-files/func-src/handler.ts new file mode 100644 index 00000000000..2acb92f1a19 --- /dev/null +++ b/packages/integration-tests/src/test-projects/live-dependency-health-checks-projects/function-code-hotswap/hotswap-update-files/func-src/handler.ts @@ -0,0 +1,6 @@ +/** + * Non-functional change to the lambda, but it triggers a sandbox hotswap + */ +export const handler = async () => { + return 'Hello V2'; +}; From 0cf5c26231ee99513af442826a0416e409612de4 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Tue, 3 Dec 2024 19:45:24 +0100 Subject: [PATCH 134/199] fix: add a required input prompt for use in region input (#2292) * fix: add a required input prompt for use in region input * PR Updates * api update --- .changeset/shaggy-pens-sort.md | 6 ++++ packages/cli-core/API.md | 5 ++++ .../cli-core/src/prompter/amplify_prompts.ts | 28 +++++++++++++++---- .../configure_profile_command.test.ts | 12 ++++---- .../configure/configure_profile_command.ts | 1 + 5 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 .changeset/shaggy-pens-sort.md diff --git a/.changeset/shaggy-pens-sort.md b/.changeset/shaggy-pens-sort.md new file mode 100644 index 00000000000..b36496d9a68 --- /dev/null +++ b/.changeset/shaggy-pens-sort.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/cli-core': patch +'@aws-amplify/backend-cli': patch +--- + +add a required input prompt for use in region input diff --git a/packages/cli-core/API.md b/packages/cli-core/API.md index b966cfeca14..616c6aee113 100644 --- a/packages/cli-core/API.md +++ b/packages/cli-core/API.md @@ -13,7 +13,12 @@ import { WriteStream } from 'node:tty'; export class AmplifyPrompter { static input: (options: { message: string; + required?: never; defaultValue?: string; + } | { + message: string; + required: true; + defaultValue?: never; }) => Promise; static secretValue: (promptMessage?: string) => Promise; static yesOrNo: (options: { diff --git a/packages/cli-core/src/prompter/amplify_prompts.ts b/packages/cli-core/src/prompter/amplify_prompts.ts index d398687cbeb..447049ef4e1 100644 --- a/packages/cli-core/src/prompter/amplify_prompts.ts +++ b/packages/cli-core/src/prompter/amplify_prompts.ts @@ -43,16 +43,32 @@ export class AmplifyPrompter { * @param options for displaying the prompt * @param options.message display for the prompt * @param options.defaultValue if user submits without typing anything. Default: "." + * @param options.required if the user input is required, incompatible with options.defaultValue * @returns Promise the user input */ - static input = async (options: { - message: string; - defaultValue?: string; - }): Promise => { - const response = await input({ + static input = async ( + options: + | { + message: string; + required?: never; + defaultValue?: string; + } + | { + message: string; + required: true; + defaultValue?: never; + } + ): Promise => { + if (options.required) { + return input({ + message: options.message, + validate: (val: string) => + val && val.length > 0 ? true : 'Cannot be empty', + }); + } + return input({ message: options.message, default: options.defaultValue ?? '', }); - return response; }; } diff --git a/packages/cli/src/commands/configure/configure_profile_command.test.ts b/packages/cli/src/commands/configure/configure_profile_command.test.ts index f387069205e..1c316875a2a 100644 --- a/packages/cli/src/commands/configure/configure_profile_command.test.ts +++ b/packages/cli/src/commands/configure/configure_profile_command.test.ts @@ -42,7 +42,7 @@ void describe('configure command', () => { emitSuccessMock.mock.resetCalls(); }); - void it('configures a profile with an IAM user', async (contextual) => { + void it('fails to configure a profile with a name that already has a profile', async (contextual) => { const mockProfileExists = mock.method( profileController, 'profileExists', @@ -65,7 +65,7 @@ void describe('configure command', () => { }); }); - void it('configures a profile with an IAM user', async (contextual) => { + void it('configures a profile with an existing IAM user credentials', async (contextual) => { const mockProfileExists = mock.method( profileController, 'profileExists', @@ -84,10 +84,10 @@ void describe('configure command', () => { } ); - const mockInput = contextual.mock.method( + const mockRequiredInput = contextual.mock.method( AmplifyPrompter, 'input', - (options: { message: string; defaultValue?: string }) => { + (options: { message: string; required: true }) => { if (options.message.includes('Enter the AWS region to use')) { return Promise.resolve(testRegion); } @@ -105,7 +105,7 @@ void describe('configure command', () => { assert.equal(mockProfileExists.mock.callCount(), 1); assert.equal(mockSecretValue.mock.callCount(), 2); - assert.equal(mockInput.mock.callCount(), 1); + assert.equal(mockRequiredInput.mock.callCount(), 1); assert.equal(mockHasIAMUser.mock.callCount(), 1); assert.equal(mockAppendAWSFiles.mock.callCount(), 1); assert.deepStrictEqual(mockAppendAWSFiles.mock.calls[0].arguments[0], { @@ -121,7 +121,7 @@ void describe('configure command', () => { }); }); - void it('configures a profile without an IAM user', async (contextual) => { + void it('configures a profile without an existing IAM user', async (contextual) => { const mockProfileExists = mock.method( profileController, 'profileExists', diff --git a/packages/cli/src/commands/configure/configure_profile_command.ts b/packages/cli/src/commands/configure/configure_profile_command.ts index 7be59b0a497..72d3a1d785b 100644 --- a/packages/cli/src/commands/configure/configure_profile_command.ts +++ b/packages/cli/src/commands/configure/configure_profile_command.ts @@ -73,6 +73,7 @@ export class ConfigureProfileCommand const region = await AmplifyPrompter.input({ message: `Enter the AWS region to use with the '${profileName}' profile (eg us-east-1, us-west-2, etc):`, + required: true, }); await this.profileController.createOrAppendAWSFiles({ From d227f96793fd6a3e181af96bbb1ef2f0d3ab7ae4 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Tue, 3 Dec 2024 14:45:14 -0800 Subject: [PATCH 135/199] add validation for environment prop (#2290) * add validation for environment prop * remove redundant length validation * PR feedback --- .changeset/shy-lions-smash.md | 5 + .changeset/weak-nails-change.md | 5 + packages/backend-function/src/factory.test.ts | 141 +++++++++++++++--- packages/backend-function/src/factory.ts | 52 +++++-- 4 files changed, 173 insertions(+), 30 deletions(-) create mode 100644 .changeset/shy-lions-smash.md create mode 100644 .changeset/weak-nails-change.md diff --git a/.changeset/shy-lions-smash.md b/.changeset/shy-lions-smash.md new file mode 100644 index 00000000000..a30b19bca40 --- /dev/null +++ b/.changeset/shy-lions-smash.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-function': patch +--- + +change errors in FunctionFactory to AmplifyUserError diff --git a/.changeset/weak-nails-change.md b/.changeset/weak-nails-change.md new file mode 100644 index 00000000000..72f2675d112 --- /dev/null +++ b/.changeset/weak-nails-change.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-function': patch +--- + +add validation for environment prop diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index 8b964f336c2..df619e0012a 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -19,6 +19,7 @@ import { Runtime } from 'aws-cdk-lib/aws-lambda'; import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; import fsp from 'fs/promises'; import path from 'node:path'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; const createStackAndSetContext = (): Stack => { const app = new App(); @@ -207,9 +208,10 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', timeoutSeconds: 0, }).getInstance(getInstanceProps), - new Error( - 'timeoutSeconds must be a whole number between 1 and 900 inclusive' - ) + new AmplifyUserError('InvalidTimeoutError', { + message: `Invalid function timeout of 0`, + resolution: `timeoutSeconds must be a whole number between 1 and 900 inclusive`, + }) ); }); @@ -220,9 +222,10 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', timeoutSeconds: 901, }).getInstance(getInstanceProps), - new Error( - 'timeoutSeconds must be a whole number between 1 and 900 inclusive' - ) + new AmplifyUserError('InvalidTimeoutError', { + message: `Invalid function timeout of 901`, + resolution: `timeoutSeconds must be a whole number between 1 and 900 inclusive`, + }) ); }); @@ -233,9 +236,10 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', timeoutSeconds: 10.5, }).getInstance(getInstanceProps), - new Error( - 'timeoutSeconds must be a whole number between 1 and 900 inclusive' - ) + new AmplifyUserError('InvalidTimeoutError', { + message: `Invalid function timeout of 10.5`, + resolution: `timeoutSeconds must be a whole number between 1 and 900 inclusive`, + }) ); }); }); @@ -271,9 +275,10 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', memoryMB: 127, }).getInstance(getInstanceProps), - new Error( - 'memoryMB must be a whole number between 128 and 10240 inclusive' - ) + new AmplifyUserError('InvalidMemoryMBError', { + message: `Invalid function memoryMB of 127`, + resolution: `memoryMB must be a whole number between 128 and 10240 inclusive`, + }) ); }); @@ -284,9 +289,10 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', memoryMB: 10241, }).getInstance(getInstanceProps), - new Error( - 'memoryMB must be a whole number between 128 and 10240 inclusive' - ) + new AmplifyUserError('InvalidMemoryMBError', { + message: `Invalid function memoryMB of 10241`, + resolution: `memoryMB must be a whole number between 128 and 10240 inclusive`, + }) ); }); @@ -297,9 +303,103 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', memoryMB: 256.2, }).getInstance(getInstanceProps), - new Error( - 'memoryMB must be a whole number between 128 and 10240 inclusive' - ) + new AmplifyUserError('InvalidMemoryMBError', { + message: `Invalid function memoryMB of 256.2`, + resolution: `memoryMB must be a whole number between 128 and 10240 inclusive`, + }) + ); + }); + }); + + void describe('environment property', () => { + void it('sets valid environment', () => { + const functionFactory = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'myCoolLambda', + environment: { + TEST_ENV: 'testValue', + }, + }); + const lambda = functionFactory.getInstance(getInstanceProps); + const stack = lambda.stack; + const template = Template.fromStack(stack); + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('AWS::Lambda::Function', { + Environment: { + Variables: { + TEST_ENV: 'testValue', + }, + }, + }); + }); + + void it('sets default environment', () => { + const functionFactory = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'myCoolLambda', + }); + const lambda = functionFactory.getInstance(getInstanceProps); + const stack = lambda.stack; + const template = Template.fromStack(stack); + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('AWS::Lambda::Function', { + Environment: {}, + }); + }); + + void it('throws when adding environment variables with invalid key', () => { + assert.throws( + () => + defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'myCoolLambda', + environment: { + 'this.is.wrong': 'testValue', + }, + }).getInstance(getInstanceProps), + new AmplifyUserError('InvalidEnvironmentKeyError', { + message: `Invalid function environment key(s): this.is.wrong`, + resolution: + 'Environment keys must match [a-zA-Z]([a-zA-Z0-9_])+ and be at least 2 characters', + }) + ); + }); + + void it('throws when adding environment variables with key less than 2 characters', () => { + assert.throws( + () => + defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'myCoolLambda', + environment: { + A: 'testValue', + }, + }).getInstance(getInstanceProps), + new AmplifyUserError('InvalidEnvironmentKeyError', { + message: `Invalid function environment key(s): A`, + resolution: + 'Environment keys must match [a-zA-Z]([a-zA-Z0-9_])+ and be at least 2 characters', + }) + ); + }); + + void it('throws when multiple environment variables are invalid', () => { + assert.throws( + () => + defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'lambdaWithMultipleEnvVars', + environment: { + A: 'testValueA', + TEST_ENV: 'envValue', + 'this.is.wrong': 'testValue', + }, + }).getInstance(getInstanceProps), + new AmplifyUserError('InvalidEnvironmentKeyError', { + message: `Invalid function environment key(s): A, this.is.wrong`, + resolution: + 'Environment keys must match [a-zA-Z]([a-zA-Z0-9_])+ and be at least 2 characters', + }) ); }); }); @@ -335,7 +435,10 @@ void describe('AmplifyFunctionFactory', () => { entry: './test-assets/default-lambda/handler.ts', runtime: 14 as NodeVersion, }).getInstance(getInstanceProps), - new Error('runtime must be one of the following: 16, 18, 20, 22') + new AmplifyUserError('InvalidRuntimeError', { + message: `Invalid function runtime of 14`, + resolution: 'runtime must be one of the following: 16, 18, 20, 22', + }) ); }); diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index e752b46d44e..81769e7d6ac 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -232,7 +232,7 @@ class FunctionFactory implements ConstructFactory { entry: this.resolveEntry(), timeoutSeconds: this.resolveTimeout(), memoryMB: this.resolveMemory(), - environment: this.props.environment ?? {}, + environment: this.resolveEnvironment(), runtime: this.resolveRuntime(), schedule: this.resolveSchedule(), bundling: this.resolveBundling(), @@ -294,9 +294,10 @@ class FunctionFactory implements ConstructFactory { timeoutMax ) ) { - throw new Error( - `timeoutSeconds must be a whole number between ${timeoutMin} and ${timeoutMax} inclusive` - ); + throw new AmplifyUserError('InvalidTimeoutError', { + message: `Invalid function timeout of ${this.props.timeoutSeconds}`, + resolution: `timeoutSeconds must be a whole number between ${timeoutMin} and ${timeoutMax} inclusive`, + }); } return this.props.timeoutSeconds; }; @@ -311,13 +312,41 @@ class FunctionFactory implements ConstructFactory { if ( !isWholeNumberBetweenInclusive(this.props.memoryMB, memoryMin, memoryMax) ) { - throw new Error( - `memoryMB must be a whole number between ${memoryMin} and ${memoryMax} inclusive` - ); + throw new AmplifyUserError('InvalidMemoryMBError', { + message: `Invalid function memoryMB of ${this.props.memoryMB}`, + resolution: `memoryMB must be a whole number between ${memoryMin} and ${memoryMax} inclusive`, + }); } return this.props.memoryMB; }; + private resolveEnvironment = () => { + if (this.props.environment === undefined) { + return {}; + } + + const invalidKeys: string[] = []; + + Object.keys(this.props.environment).forEach((key) => { + // validate using key pattern from https://docs.aws.amazon.com/lambda/latest/api/API_Environment.html + if (!key.match(/^[a-zA-Z]([a-zA-Z0-9_])+$/)) { + invalidKeys.push(key); + } + }); + + if (invalidKeys.length > 0) { + throw new AmplifyUserError('InvalidEnvironmentKeyError', { + message: `Invalid function environment key(s): ${invalidKeys.join( + ', ' + )}`, + resolution: + 'Environment keys must match [a-zA-Z]([a-zA-Z0-9_])+ and be at least 2 characters', + }); + } + + return this.props.environment; + }; + private resolveRuntime = () => { const runtimeDefault = 18; @@ -327,11 +356,12 @@ class FunctionFactory implements ConstructFactory { } if (!(this.props.runtime in nodeVersionMap)) { - throw new Error( - `runtime must be one of the following: ${Object.keys( + throw new AmplifyUserError('InvalidRuntimeError', { + message: `Invalid function runtime of ${this.props.runtime}`, + resolution: `runtime must be one of the following: ${Object.keys( nodeVersionMap - ).join(', ')}` - ); + ).join(', ')}`, + }); } return this.props.runtime; From 6015595b29807f451cbba37cade0e68c8d43ef83 Mon Sep 17 00:00:00 2001 From: Leena <81589006+ShadowCat567@users.noreply.github.com> Date: Wed, 4 Dec 2024 12:28:26 -0500 Subject: [PATCH 136/199] cdk error mapping EPERM errors relating to synth.lock (#2293) * catch a form of EPERM error * update to stack in failed state * added changeset * updates to regex and resolution message * adjusted resolution message --------- Co-authored-by: Vieltojarvi --- .changeset/purple-news-do.md | 5 +++++ packages/backend-deployer/src/cdk_error_mapper.test.ts | 6 ++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 10 +++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 .changeset/purple-news-do.md diff --git a/.changeset/purple-news-do.md b/.changeset/purple-news-do.md new file mode 100644 index 00000000000..4c64d168bb2 --- /dev/null +++ b/.changeset/purple-news-do.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +Catches most common EPERM error and update to resolution message for stack in failed state diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 3850467a359..a4f9d4952be 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -359,6 +359,12 @@ const testErrorMappings = [ errorName: 'FilePermissionsError', expectedDownstreamErrorMessage: `EACCES: permission denied, unlink '.amplify/artifacts/cdk.out/synth.lock'`, }, + { + errorMessage: `EPERM: operation not permitted, rename 'C:/Users/someUser/amplify/artifacts/cdk.out/synth.lock.6785_1' → 'C:/Users/someUser/amplify/artifacts/cdk.out/synth.lock'`, + expectedTopLevelErrorMessage: `Not permitted to rename file: 'C:/Users/someUser/amplify/artifacts/cdk.out/synth.lock.6785_1'`, + errorName: 'FilePermissionsError', + expectedDownstreamErrorMessage: undefined, + }, { errorMessage: `This CDK CLI is not compatible with the CDK library used by your application. Please upgrade the CLI to the latest version. (Cloud assembly schema version mismatch: Maximum schema version supported is 36.0.0, but found 36.1.1)`, diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 949a9212f3f..5b121ea6731 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -184,6 +184,14 @@ export class CdkErrorMapper { errorName: 'FilePermissionsError', classification: 'ERROR', }, + { + errorRegex: + /EPERM: operation not permitted, rename (?(.*)\/synth\.lock\.\S+) → '(.*)\/synth\.lock'/, + humanReadableErrorMessage: 'Not permitted to rename file: {fileName}', + resolutionMessage: `Try running the command again and ensure that only one instance of sandbox is running. If it still doesn't work check the permissions of '.amplify' folder`, + errorName: 'FilePermissionsError', + classification: 'ERROR', + }, { errorRegex: new RegExp( `\\[ERR_MODULE_NOT_FOUND\\]:(.*)${this.multiLineEolRegex}|Error: Cannot find module (.*)` @@ -220,7 +228,7 @@ export class CdkErrorMapper { humanReadableErrorMessage: 'The CloudFormation deletion failed due to {stackName} being in DELETE_FAILED state. Ensure all your resources are able to be deleted', resolutionMessage: - 'The following resource(s) failed to delete: {resources}. Ensure they are in a state where they can be deleted. Find more information in the CloudFormation AWS Console for this stack.', + 'The following resource(s) failed to delete: {resources}. Check the error message for more details and ensure your resources are in a state where they can be deleted. Check the CloudFormation AWS Console for this stack to find additional information.', errorName: 'CloudFormationDeletionError', classification: 'ERROR', }, From 5cbe318dd2fce54aa129dcbed3bb754a85a4dc31 Mon Sep 17 00:00:00 2001 From: "Aaron S." <94858815+stocaaro@users.noreply.github.com> Date: Wed, 4 Dec 2024 12:17:39 -0600 Subject: [PATCH 137/199] feat: Add lamda data client support (#2224) --------- Co-authored-by: Kamil Sobol --- .changeset/brave-cheetahs-repeat.md | 7 + package-lock.json | 17 +- packages/backend-data/package.json | 7 +- .../src/app_sync_policy_generator.ts | 29 ++- packages/backend-data/src/factory.test.ts | 53 +++++- packages/backend-data/src/factory.ts | 41 +++- packages/backend-function/API.md | 77 ++++++++ packages/backend-function/api-extractor.json | 3 +- packages/backend-function/package.json | 7 + .../src/function_env_type_generator.test.ts | 3 - .../backend-function/src/index.internal.ts | 10 + .../get_amplify_clients_configuration.test.ts | 132 +++++++++++++ .../get_amplify_clients_configuration.ts | 179 ++++++++++++++++++ .../backend-function/src/runtime/index.ts | 10 + packages/backend/API.md | 8 + packages/backend/package.json | 7 +- .../backend/src/function/runtime/index.ts | 1 + packages/backend/src/index.internal.ts | 3 + packages/integration-tests/package.json | 2 +- ...s_from_function_project.deployment.test.ts | 4 + ...cess_from_function_project.sandbox.test.ts | 4 + .../data_access_from_function_project.ts | 164 ++++++++++++++++ .../amplify/backend.ts | 6 + .../amplify/data/resource.ts | 42 ++++ .../functions/customer-s3-import/handler.ts | 22 +++ .../functions/customer-s3-import/resource.ts | 7 + .../amplify/functions/todo-count/handler.ts | 20 ++ .../amplify/functions/todo-count/resource.ts | 7 + 28 files changed, 845 insertions(+), 27 deletions(-) create mode 100644 .changeset/brave-cheetahs-repeat.md create mode 100644 packages/backend-function/src/index.internal.ts create mode 100644 packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts create mode 100644 packages/backend-function/src/runtime/get_amplify_clients_configuration.ts create mode 100644 packages/backend-function/src/runtime/index.ts create mode 100644 packages/backend/src/function/runtime/index.ts create mode 100644 packages/integration-tests/src/test-e2e/deployment/data_access_from_function_project.deployment.test.ts create mode 100644 packages/integration-tests/src/test-e2e/sandbox/data_access_from_function_project.sandbox.test.ts create mode 100644 packages/integration-tests/src/test-project-setup/data_access_from_function_project.ts create mode 100644 packages/integration-tests/src/test-projects/data_access_from_function/amplify/backend.ts create mode 100644 packages/integration-tests/src/test-projects/data_access_from_function/amplify/data/resource.ts create mode 100644 packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/customer-s3-import/handler.ts create mode 100644 packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/customer-s3-import/resource.ts create mode 100644 packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/todo-count/handler.ts create mode 100644 packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/todo-count/resource.ts diff --git a/.changeset/brave-cheetahs-repeat.md b/.changeset/brave-cheetahs-repeat.md new file mode 100644 index 00000000000..d74a469e25a --- /dev/null +++ b/.changeset/brave-cheetahs-repeat.md @@ -0,0 +1,7 @@ +--- +'@aws-amplify/backend': minor +'@aws-amplify/backend-function': minor +'@aws-amplify/backend-data': patch +--- + +Add lambda data client diff --git a/package-lock.json b/package-lock.json index 97fb224519f..6a55c53ed8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2838,9 +2838,9 @@ } }, "node_modules/@aws-amplify/data-schema": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema/-/data-schema-1.5.1.tgz", - "integrity": "sha512-hFDqqwHqdoFazmvGOApCX8kqrdoum9YJikmAQN5tP2sgnCT++lqznFw2F4PPqDJRxhQP1AYuwhbbRBvGLMbs/w==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema/-/data-schema-1.16.1.tgz", + "integrity": "sha512-ThEiEoDbGfU03a2wVpdW4VORLrUkrlWMb9Xc6kI6I296+Gk0DHKNmQUFov4nlqxUIBe3lntJUcZSCMWJZTq4ZQ==", "license": "Apache-2.0", "dependencies": { "@aws-amplify/data-schema-types": "*", @@ -31562,7 +31562,7 @@ "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.3", "@aws-amplify/client-config": "^1.5.2", - "@aws-amplify/data-schema": "^1.0.0", + "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/platform-core": "^1.2.1", "@aws-amplify/plugin-types": "^1.5.0", "@aws-sdk/client-amplify": "^3.624.0", @@ -31627,11 +31627,12 @@ "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/data-construct": "^1.10.1", "@aws-amplify/data-schema-types": "^1.2.0", - "@aws-amplify/plugin-types": "^1.5.0" + "@aws-amplify/graphql-generator": "^0.5.1", + "@aws-amplify/plugin-types": "^1.4.0" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/data-schema": "^1.0.0", + "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/platform-core": "^1.2.1" }, "peerDependencies": { @@ -31663,11 +31664,13 @@ "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/plugin-types": "^1.5.0", + "@aws-sdk/client-s3": "^3.624.0", "execa": "^8.0.1" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", "@aws-amplify/platform-core": "^1.2.1", + "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", "uuid": "^9.0.1" @@ -32107,7 +32110,7 @@ "@aws-amplify/backend-ai": "^1.0.0", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/client-config": "^1.5.1", - "@aws-amplify/data-schema": "^1.0.0", + "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.3.1", diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index 0ec2b12a4c8..9afce361886 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -19,7 +19,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-amplify/data-schema": "^1.0.0", + "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/backend-platform-test-stubs": "^0.3.6", "@aws-amplify/platform-core": "^1.2.1" }, @@ -31,7 +31,8 @@ "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/data-construct": "^1.10.1", - "@aws-amplify/plugin-types": "^1.5.0", - "@aws-amplify/data-schema-types": "^1.2.0" + "@aws-amplify/data-schema-types": "^1.2.0", + "@aws-amplify/graphql-generator": "^0.5.1", + "@aws-amplify/plugin-types": "^1.4.0" } } diff --git a/packages/backend-data/src/app_sync_policy_generator.ts b/packages/backend-data/src/app_sync_policy_generator.ts index 9962d1922ea..5db2eaea3d7 100644 --- a/packages/backend-data/src/app_sync_policy_generator.ts +++ b/packages/backend-data/src/app_sync_policy_generator.ts @@ -14,7 +14,10 @@ export class AppSyncPolicyGenerator { /** * Initialize with the GraphqlAPI that the policies will be scoped to */ - constructor(private readonly graphqlApi: IGraphqlApi) { + constructor( + private readonly graphqlApi: IGraphqlApi, + private readonly modelIntrospectionSchemaArn?: string + ) { this.stack = Stack.of(graphqlApi); } /** @@ -29,13 +32,25 @@ export class AppSyncPolicyGenerator { .map((action) => actionToTypeMap[action]) // convert Type to resourceName .map((type) => [this.graphqlApi.arn, 'types', type, '*'].join('/')); - return new Policy(this.stack, `${this.policyPrefix}${this.policyCount++}`, { - statements: [ + + const statements = [ + new PolicyStatement({ + actions: ['appsync:GraphQL'], + resources, + }), + ]; + + if (this.modelIntrospectionSchemaArn) { + statements.push( new PolicyStatement({ - actions: ['appsync:GraphQL'], - resources, - }), - ], + actions: ['s3:GetObject'], + resources: [this.modelIntrospectionSchemaArn], + }) + ); + } + + return new Policy(this.stack, `${this.policyPrefix}${this.policyCount++}`, { + statements, }); } } diff --git a/packages/backend-data/src/factory.test.ts b/packages/backend-data/src/factory.test.ts index 51e2aabf458..91e1fdeff6c 100644 --- a/packages/backend-data/src/factory.test.ts +++ b/packages/backend-data/src/factory.test.ts @@ -85,7 +85,6 @@ const createConstructContainerWithUserPoolAuthRegistered = ( authenticatedUserIamRole: new Role(stack, 'testAuthRole', { assumedBy: new ServicePrincipal('test.amazon.com'), }), - identityPoolId: 'identityPoolId', cfnResources: { cfnUserPool: new CfnUserPool(stack, 'CfnUserPool', {}), cfnUserPoolClient: new CfnUserPoolClient(stack, 'CfnUserPoolClient', { @@ -101,6 +100,7 @@ const createConstructContainerWithUserPoolAuthRegistered = ( ), }, groups: {}, + identityPoolId: 'identityPool', }, }), }); @@ -567,6 +567,23 @@ void describe('DataFactory', () => { }, ], }, + { + Action: 's3:GetObject', + Resource: { + 'Fn::Join': [ + '', + [ + { + 'Fn::GetAtt': [ + 'modelIntrospectionSchemaBucketF566B665', + 'Arn', + ], + }, + '/modelIntrospectionSchema.json', + ], + ], + }, + }, ], }, Roles: [ @@ -675,6 +692,23 @@ void describe('DataFactory', () => { ], }, }, + { + Action: 's3:GetObject', + Resource: { + 'Fn::Join': [ + '', + [ + { + 'Fn::GetAtt': [ + 'modelIntrospectionSchemaBucketF566B665', + 'Arn', + ], + }, + '/modelIntrospectionSchema.json', + ], + ], + }, + }, ], }, Roles: [ @@ -701,6 +735,23 @@ void describe('DataFactory', () => { ], }, }, + { + Action: 's3:GetObject', + Resource: { + 'Fn::Join': [ + '', + [ + { + 'Fn::GetAtt': [ + 'modelIntrospectionSchemaBucketF566B665', + 'Arn', + ], + }, + '/modelIntrospectionSchema.json', + ], + ], + }, + }, ], }, Roles: [ diff --git a/packages/backend-data/src/factory.ts b/packages/backend-data/src/factory.ts index 92436ec6b17..98d48b0667e 100644 --- a/packages/backend-data/src/factory.ts +++ b/packages/backend-data/src/factory.ts @@ -18,6 +18,7 @@ import { TranslationBehavior, } from '@aws-amplify/data-construct'; import { GraphqlOutput } from '@aws-amplify/backend-output-schemas'; +import { generateModelsSync } from '@aws-amplify/graphql-generator'; import * as path from 'path'; import { AmplifyDataError, DataProps } from './types.js'; import { @@ -40,13 +41,17 @@ import { CDKContextKey, TagName, } from '@aws-amplify/platform-core'; -import { Aspects, IAspect, Tags } from 'aws-cdk-lib'; +import { Aspects, IAspect, RemovalPolicy, Tags } from 'aws-cdk-lib'; import { convertJsResolverDefinition } from './convert_js_resolvers.js'; import { AppSyncPolicyGenerator } from './app_sync_policy_generator.js'; import { FunctionSchemaAccess, JsResolver, } from '@aws-amplify/data-schema-types'; +import { Bucket } from 'aws-cdk-lib/aws-s3'; +import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment'; + +const modelIntrospectionSchemaKey = 'modelIntrospectionSchema.json'; /** * Singleton factory for AmplifyGraphqlApi constructs that can be used in Amplify project files. @@ -233,14 +238,21 @@ class DataGenerator implements ConstructContainerEntryGenerator { ...schemasLambdaFunctions, }); let amplifyApi = undefined; + let modelIntrospectionSchema: string | undefined = undefined; const isSandboxDeployment = scope.node.tryGetContext(CDKContextKey.DEPLOYMENT_TYPE) === 'sandbox'; try { + const combinedSchema = combineCDKSchemas(amplifyGraphqlDefinitions); + modelIntrospectionSchema = generateModelsSync({ + schema: combinedSchema.schema, + target: 'introspection', + })['model-introspection.json']; + amplifyApi = new AmplifyData(scope, this.name, { apiName: this.name, - definition: combineCDKSchemas(amplifyGraphqlDefinitions), + definition: combinedSchema, authorizationModes, outputStorageStrategy: this.outputStorageStrategy, functionNameMap, @@ -265,6 +277,24 @@ class DataGenerator implements ConstructContainerEntryGenerator { ); } + const modelIntrospectionSchemaBucket = new Bucket( + scope, + 'modelIntrospectionSchemaBucket', + { + enforceSSL: true, + autoDeleteObjects: true, + removalPolicy: RemovalPolicy.DESTROY, + } + ); + new BucketDeployment(scope, 'modelIntrospectionSchemaBucketDeployment', { + // See https://github.com/aws-amplify/amplify-category-api/pull/1939 + memoryLimit: 1536, + destinationBucket: modelIntrospectionSchemaBucket, + sources: [ + Source.data(modelIntrospectionSchemaKey, modelIntrospectionSchema), + ], + }); + Tags.of(amplifyApi).add(TagName.FRIENDLY_NAME, this.name); /**; @@ -281,10 +311,15 @@ class DataGenerator implements ConstructContainerEntryGenerator { ssmEnvironmentEntriesGenerator.generateSsmEnvironmentEntries({ [`${this.name}_GRAPHQL_ENDPOINT`]: amplifyApi.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl, + [`${this.name}_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME`]: + modelIntrospectionSchemaBucket.bucketName, + [`${this.name}_MODEL_INTROSPECTION_SCHEMA_KEY`]: + modelIntrospectionSchemaKey, }); const policyGenerator = new AppSyncPolicyGenerator( - amplifyApi.resources.graphqlApi + amplifyApi.resources.graphqlApi, + `${modelIntrospectionSchemaBucket.bucketArn}/${modelIntrospectionSchemaKey}` ); schemasFunctionSchemaAccess.forEach((accessDefinition) => { diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index 500972914db..fed504b411d 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -12,8 +12,23 @@ import { LogLevel } from '@aws-amplify/plugin-types'; import { LogRetention } from '@aws-amplify/plugin-types'; import { ResourceAccessAcceptorFactory } from '@aws-amplify/plugin-types'; import { ResourceProvider } from '@aws-amplify/plugin-types'; +import { S3Client } from '@aws-sdk/client-s3'; import { StackProvider } from '@aws-amplify/plugin-types'; +declare namespace __export__runtime { + export { + getAmplifyDataClientConfig, + DataClientConfig, + DataClientEnv, + DataClientError, + DataClientReturn, + InvalidConfig, + LibraryOptions, + ResourceConfig + } +} +export { __export__runtime } + // @public (undocumented) export type AddEnvironmentFactory = { addEnvironment: (key: string, value: string | BackendSecret) => void; @@ -22,6 +37,32 @@ export type AddEnvironmentFactory = { // @public (undocumented) export type CronSchedule = `${string} ${string} ${string} ${string} ${string}` | `${string} ${string} ${string} ${string} ${string} ${string}`; +// @public (undocumented) +type DataClientConfig = { + resourceConfig: ResourceConfig; + libraryOptions: LibraryOptions; +}; + +// @public (undocumented) +type DataClientEnv = { + AMPLIFY_DATA_GRAPHQL_ENDPOINT: string; + AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME: string; + AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY: string; + AWS_ACCESS_KEY_ID: string; + AWS_SECRET_ACCESS_KEY: string; + AWS_SESSION_TOKEN: string; + AWS_REGION: string; +}; + +// @public (undocumented) +type DataClientError = { + resourceConfig: InvalidConfig; + libraryOptions: InvalidConfig; +}; + +// @public (undocumented) +type DataClientReturn = T extends DataClientEnv ? DataClientConfig : DataClientError; + // @public export const defineFunction: (props?: FunctionProps) => ConstructFactory & ResourceAccessAcceptorFactory & AddEnvironmentFactory & StackProvider>; @@ -64,9 +105,45 @@ export type FunctionProps = { // @public (undocumented) export type FunctionSchedule = TimeInterval | CronSchedule; +// @public +const getAmplifyDataClientConfig: (env: T, s3Client?: S3Client) => Promise>; + +// @public (undocumented) +type InvalidConfig = unknown & { + invalidType: 'This function needs to be granted `authorization((allow) => [allow.resource(fcn)])` on the data schema.'; +}; + +// @public (undocumented) +type LibraryOptions = { + Auth: { + credentialsProvider: { + getCredentialsAndIdentityId: () => Promise<{ + credentials: { + accessKeyId: string; + secretAccessKey: string; + sessionToken: string; + }; + }>; + clearCredentialsAndIdentityId: () => void; + }; + }; +}; + // @public (undocumented) export type NodeVersion = 16 | 18 | 20 | 22; +// @public (undocumented) +type ResourceConfig = { + API: { + GraphQL: { + endpoint: string; + region: string; + defaultAuthMode: 'iam'; + modelIntrospection: any; + }; + }; +}; + // @public (undocumented) export type TimeInterval = `every ${number}m` | `every ${number}h` | `every day` | `every week` | `every month` | `every year`; diff --git a/packages/backend-function/api-extractor.json b/packages/backend-function/api-extractor.json index 0f56de03f66..cc2ebea8cf9 100644 --- a/packages/backend-function/api-extractor.json +++ b/packages/backend-function/api-extractor.json @@ -1,3 +1,4 @@ { - "extends": "../../api-extractor.base.json" + "extends": "../../api-extractor.base.json", + "mainEntryPointFilePath": "/lib/index.internal.d.ts" } diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index 212f8567ed9..de43cf23183 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -10,6 +10,11 @@ "types": "./lib/index.d.ts", "import": "./lib/index.js", "require": "./lib/index.js" + }, + "./runtime": { + "types": "./lib/runtime/index.d.ts", + "import": "./lib/runtime/index.js", + "require": "./lib/runtime/index.js" } }, "main": "lib/index.js", @@ -22,11 +27,13 @@ "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/plugin-types": "^1.5.0", + "@aws-sdk/client-s3": "^3.624.0", "execa": "^8.0.1" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", "@aws-amplify/platform-core": "^1.2.1", + "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", "uuid": "^9.0.1" diff --git a/packages/backend-function/src/function_env_type_generator.test.ts b/packages/backend-function/src/function_env_type_generator.test.ts index 64d5588bce5..0865afa8183 100644 --- a/packages/backend-function/src/function_env_type_generator.test.ts +++ b/packages/backend-function/src/function_env_type_generator.test.ts @@ -66,7 +66,6 @@ void describe('FunctionEnvironmentTypeGenerator', () => { }); void it('generated type definition file has valid syntax', async () => { - const targetDirectory = await fsp.mkdtemp('func_env_type_gen_test'); const functionEnvironmentTypeGenerator = new FunctionEnvironmentTypeGenerator('testFunction'); const filePath = `${process.cwd()}/.amplify/generated/env/testFunction.ts`; @@ -75,8 +74,6 @@ void describe('FunctionEnvironmentTypeGenerator', () => { // import to validate syntax of type definition file await import(pathToFileURL(filePath).toString()); - - await fsp.rm(targetDirectory, { recursive: true, force: true }); }); void it('does not generate duplicate environment variables', () => { diff --git a/packages/backend-function/src/index.internal.ts b/packages/backend-function/src/index.internal.ts new file mode 100644 index 00000000000..cc29b4a317a --- /dev/null +++ b/packages/backend-function/src/index.internal.ts @@ -0,0 +1,10 @@ +export * from './index.js'; +// eslint-disable-next-line @typescript-eslint/naming-convention +import * as __export__runtime from './runtime/index.js'; + +/* + Api-extractor does not ([yet](https://github.com/microsoft/rushstack/issues/1596)) support multiple package entry points + Because this package has a submodule export, we are working around this issue by including that export here and directing api-extract to this entry point instead + This allows api-extractor to pick up the submodule exports in its analysis + */ +export { __export__runtime }; diff --git a/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts b/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts new file mode 100644 index 00000000000..e2e5d45407f --- /dev/null +++ b/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts @@ -0,0 +1,132 @@ +import { beforeEach, describe, it, mock } from 'node:test'; +import assert from 'assert'; +import { NoSuchKey, S3, S3ServiceException } from '@aws-sdk/client-s3'; + +import { getAmplifyDataClientConfig } from './get_amplify_clients_configuration.js'; + +const validEnv = { + AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME: + 'TEST_VALUE for AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME', + AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY: + 'TEST_VALUE for AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY', + AWS_ACCESS_KEY_ID: 'TEST_VALUE for AWS_ACCESS_KEY_ID', + AWS_SECRET_ACCESS_KEY: 'TEST_VALUE for AWS_SECRET_ACCESS_KEY', + AWS_SESSION_TOKEN: 'TEST_VALUE for AWS_SESSION_TOKEN', + AWS_REGION: 'TEST_VALUE for AWS_REGION', + AMPLIFY_DATA_GRAPHQL_ENDPOINT: 'TEST_VALUE for AMPLIFY_DATA_GRAPHQL_ENDPOINT', +}; + +let mockS3Client: S3; + +void describe('getAmplifyDataClientConfig', () => { + beforeEach(() => { + mockS3Client = new S3(); + }); + + Object.keys(validEnv).forEach((envFieldToExclude) => { + void it(`returns empty config objects when ${envFieldToExclude} is not included`, async () => { + const env = { ...validEnv } as Record; + delete env[envFieldToExclude]; + assert.deepEqual(await getAmplifyDataClientConfig(env), { + resourceConfig: {}, + libraryOptions: {}, + }); + }); + + void it(`returns empty config objects when ${envFieldToExclude} is not a string`, async () => { + const env = { ...validEnv } as Record; + env[envFieldToExclude] = 123; + assert.deepEqual(await getAmplifyDataClientConfig(env), { + resourceConfig: {}, + libraryOptions: {}, + }); + }); + }); + + void it('raises a custom error message when the model introspection schema is missing from the s3 bucket', async () => { + const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { + throw new NoSuchKey({ message: 'TEST_ERROR', $metadata: {} }); + }); + mock.method(mockS3Client, 'send', s3ClientSendMock); + + await assert.rejects( + async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), + new Error( + 'Error retrieving the schema from S3. Please confirm that your project has a `defineData` included in the `defineBackend` definition.' + ) + ); + }); + + void it('raises a custom error message when there is a S3ServiceException error retrieving the model introspection schema from the s3 bucket', async () => { + const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { + throw new S3ServiceException({ + name: 'TEST_ERROR', + message: 'TEST_MESSAGE', + $fault: 'server', + $metadata: {}, + }); + }); + mock.method(mockS3Client, 'send', s3ClientSendMock); + + await assert.rejects( + async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), + new Error( + 'Error retrieving the schema from S3. You may need to grant this function authorization on the schema. TEST_ERROR: TEST_MESSAGE.' + ) + ); + }); + + void it('re-raises a non-S3 error received when retrieving the model introspection schema from the s3 bucket', async () => { + const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { + throw new Error('Test Error'); + }); + mock.method(mockS3Client, 'send', s3ClientSendMock); + + await assert.rejects( + async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), + new Error('Test Error') + ); + }); + + void it('returns the expected libraryOptions and resourceConfig values in the happy case', async () => { + const s3ClientSendMock = mock.method(mockS3Client, 'send', () => { + return Promise.resolve({ + Body: { + transformToString: () => JSON.stringify({ testSchema: 'TESTING' }), + }, + }); + }); + mock.method(mockS3Client, 'send', s3ClientSendMock); + + const { resourceConfig, libraryOptions } = await getAmplifyDataClientConfig( + validEnv, + mockS3Client + ); + + assert.deepEqual( + await libraryOptions.Auth.credentialsProvider.getCredentialsAndIdentityId?.(), + { + credentials: { + accessKeyId: 'TEST_VALUE for AWS_ACCESS_KEY_ID', + secretAccessKey: 'TEST_VALUE for AWS_SECRET_ACCESS_KEY', + sessionToken: 'TEST_VALUE for AWS_SESSION_TOKEN', + }, + } + ); + assert.deepEqual( + await libraryOptions.Auth.credentialsProvider.clearCredentialsAndIdentityId?.(), + undefined + ); + + assert.deepEqual(resourceConfig, { + API: { + GraphQL: { + endpoint: 'TEST_VALUE for AMPLIFY_DATA_GRAPHQL_ENDPOINT', + region: 'TEST_VALUE for AWS_REGION', + defaultAuthMode: 'iam', + modelIntrospection: { testSchema: 'TESTING' }, + }, + }, + }); + }); +}); diff --git a/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts new file mode 100644 index 00000000000..2f377fe3492 --- /dev/null +++ b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts @@ -0,0 +1,179 @@ +import { + GetObjectCommand, + NoSuchKey, + S3Client, + S3ServiceException, +} from '@aws-sdk/client-s3'; + +export type DataClientEnv = { + /* eslint-disable @typescript-eslint/naming-convention */ + AMPLIFY_DATA_GRAPHQL_ENDPOINT: string; + AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME: string; + AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY: string; + AWS_ACCESS_KEY_ID: string; + AWS_SECRET_ACCESS_KEY: string; + AWS_SESSION_TOKEN: string; + AWS_REGION: string; + /* eslint-enable @typescript-eslint/naming-convention */ +}; + +const isDataClientEnv = (env: unknown): env is DataClientEnv => { + return ( + env !== null && + typeof env === 'object' && + 'AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME' in env && + 'AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY' in env && + 'AWS_ACCESS_KEY_ID' in env && + 'AWS_SECRET_ACCESS_KEY' in env && + 'AWS_SESSION_TOKEN' in env && + 'AWS_REGION' in env && + 'AMPLIFY_DATA_GRAPHQL_ENDPOINT' in env && + typeof env.AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME === + 'string' && + typeof env.AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY === 'string' && + typeof env.AWS_ACCESS_KEY_ID === 'string' && + typeof env.AWS_SECRET_ACCESS_KEY === 'string' && + typeof env.AWS_SESSION_TOKEN === 'string' && + typeof env.AWS_REGION === 'string' && + typeof env.AMPLIFY_DATA_GRAPHQL_ENDPOINT === 'string' + ); +}; + +/* eslint-disable @typescript-eslint/naming-convention */ +export type ResourceConfig = { + API: { + GraphQL: { + endpoint: string; + region: string; + defaultAuthMode: 'iam'; + // Using `any` to avoid reproducing 100+ lines of typing to match the expected shape defined in aws-amplify: + // https://github.com/aws-amplify/amplify-js/blob/main/packages/core/src/singleton/API/types.ts#L143-L153 + // eslint-disable-next-line @typescript-eslint/no-explicit-any + modelIntrospection: any; + }; + }; +}; +/* eslint-enable @typescript-eslint/naming-convention */ + +const getResourceConfig = ( + env: DataClientEnv, + modelIntrospectionSchema: object +): ResourceConfig => { + return { + API: { + GraphQL: { + endpoint: env.AMPLIFY_DATA_GRAPHQL_ENDPOINT, + region: env.AWS_REGION, + defaultAuthMode: 'iam' as const, + + modelIntrospection: modelIntrospectionSchema, + }, + }, + }; +}; + +export type LibraryOptions = { + /* eslint-disable-next-line @typescript-eslint/naming-convention */ + Auth: { + credentialsProvider: { + getCredentialsAndIdentityId: () => Promise<{ + credentials: { + accessKeyId: string; + secretAccessKey: string; + sessionToken: string; + }; + }>; + clearCredentialsAndIdentityId: () => void; + }; + }; +}; + +const getLibraryOptions = (env: DataClientEnv): LibraryOptions => { + return { + Auth: { + credentialsProvider: { + getCredentialsAndIdentityId: async () => ({ + credentials: { + accessKeyId: env.AWS_ACCESS_KEY_ID, + secretAccessKey: env.AWS_SECRET_ACCESS_KEY, + sessionToken: env.AWS_SESSION_TOKEN, + }, + }), + clearCredentialsAndIdentityId: () => { + /* noop */ + }, + }, + }, + }; +}; + +export type InvalidConfig = unknown & { + invalidType: 'This function needs to be granted `authorization((allow) => [allow.resource(fcn)])` on the data schema.'; +}; + +export type DataClientError = { + resourceConfig: InvalidConfig; + libraryOptions: InvalidConfig; +}; + +export type DataClientConfig = { + resourceConfig: ResourceConfig; + libraryOptions: LibraryOptions; +}; + +export type DataClientReturn = T extends DataClientEnv + ? DataClientConfig + : DataClientError; + +/** + * Generate the `resourceConfig` and `libraryOptions` need to configure + * Amplify for the data client in a lambda. + * + * Your function needs to be granted resource access on your schema for this to work + * `a.schema(...).authorization((allow) => [a.resource(myFunction)])` + * @param env - The environment variables for the data client + * @returns An object containing the `resourceConfig` and `libraryOptions` + */ +export const getAmplifyDataClientConfig = async ( + env: T, + s3Client?: S3Client +): Promise> => { + if (!s3Client) { + s3Client = new S3Client(); + } + + if (!isDataClientEnv(env)) { + return { resourceConfig: {}, libraryOptions: {} } as DataClientReturn; + } + let modelIntrospectionSchema: object; + + try { + const response = await s3Client.send( + new GetObjectCommand({ + Bucket: env.AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME, + Key: env.AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY, + }) + ); + const modelIntrospectionSchemaJson = + await response.Body?.transformToString(); + modelIntrospectionSchema = JSON.parse(modelIntrospectionSchemaJson ?? '{}'); + } catch (caught) { + if (caught instanceof NoSuchKey) { + throw new Error( + 'Error retrieving the schema from S3. Please confirm that your project has a `defineData` included in the `defineBackend` definition.' + ); + } else if (caught instanceof S3ServiceException) { + throw new Error( + `Error retrieving the schema from S3. You may need to grant this function authorization on the schema. ${caught.name}: ${caught.message}.` + ); + } else { + throw caught; + } + } + + const libraryOptions = getLibraryOptions(env); + + const resourceConfig = getResourceConfig(env, modelIntrospectionSchema); + + return { resourceConfig, libraryOptions } as DataClientReturn; +}; diff --git a/packages/backend-function/src/runtime/index.ts b/packages/backend-function/src/runtime/index.ts new file mode 100644 index 00000000000..07edc4c883b --- /dev/null +++ b/packages/backend-function/src/runtime/index.ts @@ -0,0 +1,10 @@ +export { + getAmplifyDataClientConfig, + DataClientConfig, + DataClientEnv, + DataClientError, + DataClientReturn, + InvalidConfig, + LibraryOptions, + ResourceConfig, +} from './get_amplify_clients_configuration.js'; diff --git a/packages/backend/API.md b/packages/backend/API.md index c9d790427f0..d677b05e814 100644 --- a/packages/backend/API.md +++ b/packages/backend/API.md @@ -25,6 +25,7 @@ import { defineFunction } from '@aws-amplify/backend-function'; import { defineStorage } from '@aws-amplify/backend-storage'; import { FunctionResources } from '@aws-amplify/plugin-types'; import { GenerateContainerEntryProps } from '@aws-amplify/plugin-types'; +import { getAmplifyDataClientConfig } from '@aws-amplify/backend-function/runtime'; import { ImportPathVerifier } from '@aws-amplify/plugin-types'; import { referenceAuth } from '@aws-amplify/backend-auth'; import { ResourceAccessAcceptorFactory } from '@aws-amplify/plugin-types'; @@ -33,6 +34,13 @@ import { SsmEnvironmentEntriesGenerator } from '@aws-amplify/plugin-types'; import { SsmEnvironmentEntry } from '@aws-amplify/plugin-types'; import { Stack } from 'aws-cdk-lib'; +declare namespace __export__function__runtime { + export { + getAmplifyDataClientConfig + } +} +export { __export__function__runtime } + export { a } export { AuthCfnResources } diff --git a/packages/backend/package.json b/packages/backend/package.json index 106acaac429..60261d7a984 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -11,6 +11,11 @@ "import": "./lib/index.js", "require": "./lib/index.js" }, + "./function/runtime": { + "types": "./lib/function/runtime/index.d.ts", + "import": "./lib/function/runtime/index.js", + "require": "./lib/function/runtime/index.js" + }, "./types/platform": { "types": "./lib/types/platform.d.ts" } @@ -25,7 +30,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/data-schema": "^1.0.0", + "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/backend-auth": "^1.4.1", "@aws-amplify/backend-function": "^1.8.0", "@aws-amplify/backend-data": "^1.2.1", diff --git a/packages/backend/src/function/runtime/index.ts b/packages/backend/src/function/runtime/index.ts new file mode 100644 index 00000000000..a82f939faa6 --- /dev/null +++ b/packages/backend/src/function/runtime/index.ts @@ -0,0 +1 @@ +export { getAmplifyDataClientConfig } from '@aws-amplify/backend-function/runtime'; diff --git a/packages/backend/src/index.internal.ts b/packages/backend/src/index.internal.ts index edcc5711d60..dcb3dad53c3 100644 --- a/packages/backend/src/index.internal.ts +++ b/packages/backend/src/index.internal.ts @@ -1,4 +1,6 @@ export * from './index.js'; +// eslint-disable-next-line @typescript-eslint/naming-convention +import * as __export__function__runtime from './function/runtime/index.js'; /* Api-extractor does not ([yet](https://github.com/microsoft/rushstack/issues/1596)) support multiple package entry points @@ -7,3 +9,4 @@ export * from './index.js'; */ export * from './types/platform.js'; +export { __export__function__runtime }; diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 9943548811e..a50b9bd1bf4 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -11,7 +11,7 @@ "@aws-amplify/backend-ai": "^1.0.0", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/client-config": "^1.5.1", - "@aws-amplify/data-schema": "^1.0.0", + "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/platform-core": "^1.1.0", "@aws-amplify/plugin-types": "^1.3.1", diff --git a/packages/integration-tests/src/test-e2e/deployment/data_access_from_function_project.deployment.test.ts b/packages/integration-tests/src/test-e2e/deployment/data_access_from_function_project.deployment.test.ts new file mode 100644 index 00000000000..14bd0fe025f --- /dev/null +++ b/packages/integration-tests/src/test-e2e/deployment/data_access_from_function_project.deployment.test.ts @@ -0,0 +1,4 @@ +import { defineDeploymentTest } from './deployment.test.template.js'; +import { DataAccessFromFunctionTestProjectCreator } from '../../test-project-setup/data_access_from_function_project.js'; + +defineDeploymentTest(new DataAccessFromFunctionTestProjectCreator()); diff --git a/packages/integration-tests/src/test-e2e/sandbox/data_access_from_function_project.sandbox.test.ts b/packages/integration-tests/src/test-e2e/sandbox/data_access_from_function_project.sandbox.test.ts new file mode 100644 index 00000000000..c71b261b0ef --- /dev/null +++ b/packages/integration-tests/src/test-e2e/sandbox/data_access_from_function_project.sandbox.test.ts @@ -0,0 +1,4 @@ +import { defineSandboxTest } from './sandbox.test.template.js'; +import { DataAccessFromFunctionTestProjectCreator } from '../../test-project-setup/data_access_from_function_project.js'; + +defineSandboxTest(new DataAccessFromFunctionTestProjectCreator()); diff --git a/packages/integration-tests/src/test-project-setup/data_access_from_function_project.ts b/packages/integration-tests/src/test-project-setup/data_access_from_function_project.ts new file mode 100644 index 00000000000..b85a67f026e --- /dev/null +++ b/packages/integration-tests/src/test-project-setup/data_access_from_function_project.ts @@ -0,0 +1,164 @@ +import { TestProjectBase } from './test_project_base.js'; +import fs from 'fs/promises'; +import { createEmptyAmplifyProject } from './create_empty_amplify_project.js'; +import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; +import { TestProjectCreator } from './test_project_creator.js'; +import { AmplifyClient } from '@aws-sdk/client-amplify'; +import { BackendIdentifier } from '@aws-amplify/plugin-types'; +import { generateClientConfig } from '@aws-amplify/client-config'; +import { + ApolloClient, + ApolloLink, + HttpLink, + InMemoryCache, +} from '@apollo/client/core'; +import { AUTH_TYPE, createAuthLink } from 'aws-appsync-auth-link'; +import { gql } from 'graphql-tag'; +import assert from 'assert'; +import { NormalizedCacheObject } from '@apollo/client'; +import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js'; + +/** + * Creates the data and function test project. + */ +export class DataAccessFromFunctionTestProjectCreator + implements TestProjectCreator +{ + readonly name = 'data-access-from-function'; + + /** + * Creates project creator. + */ + constructor( + private readonly cfnClient: CloudFormationClient = new CloudFormationClient( + e2eToolingClientConfig + ), + private readonly amplifyClient: AmplifyClient = new AmplifyClient( + e2eToolingClientConfig + ) + ) {} + + createProject = async (e2eProjectDir: string): Promise => { + const { projectName, projectRoot, projectAmplifyDir } = + await createEmptyAmplifyProject(this.name, e2eProjectDir); + + const project = new DataAccessFromFunctionTestProject( + projectName, + projectRoot, + projectAmplifyDir, + this.cfnClient, + this.amplifyClient + ); + await fs.cp( + project.sourceProjectAmplifyDirURL, + project.projectAmplifyDirPath, + { + recursive: true, + } + ); + return project; + }; +} + +/** + * The data and function test project. + */ +class DataAccessFromFunctionTestProject extends TestProjectBase { + readonly sourceProjectDirPath = + '../../src/test-projects/data_access_from_function'; + + readonly sourceProjectAmplifyDirSuffix = `${this.sourceProjectDirPath}/amplify`; + + readonly sourceProjectAmplifyDirURL: URL = new URL( + this.sourceProjectAmplifyDirSuffix, + import.meta.url + ); + + /** + * Create a test project instance. + */ + constructor( + name: string, + projectDirPath: string, + projectAmplifyDirPath: string, + cfnClient: CloudFormationClient, + amplifyClient: AmplifyClient + ) { + super( + name, + projectDirPath, + projectAmplifyDirPath, + cfnClient, + amplifyClient + ); + } + + override async assertPostDeployment( + backendId: BackendIdentifier + ): Promise { + await super.assertPostDeployment(backendId); + + const clientConfig = await generateClientConfig(backendId, '1.1'); + if (!clientConfig.data?.url) { + throw new Error('Data and function project must include data'); + } + if (!clientConfig.data.api_key) { + throw new Error('Data and function project must include api_key'); + } + + const httpLink = new HttpLink({ uri: clientConfig.data.url }); + const link = ApolloLink.from([ + createAuthLink({ + url: clientConfig.data.url, + region: clientConfig.data.aws_region, + auth: { + type: AUTH_TYPE.API_KEY, + apiKey: clientConfig.data.api_key, + }, + }), + // see https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/473#issuecomment-543029072 + httpLink, + ]); + const apolloClient = new ApolloClient({ + link, + cache: new InMemoryCache(), + }); + + await this.assertDataFunctionCallSucceeds(apolloClient); + await this.assertNoopWithImportCallSucceeds(apolloClient); + } + + private assertDataFunctionCallSucceeds = async ( + apolloClient: ApolloClient + ): Promise => { + // The todoCount query calls the todoCount lambda + const response = await apolloClient.query({ + query: gql` + query todoCount { + todoCount + } + `, + variables: {}, + }); + + // Assert the expected lambda call return + assert.deepEqual(response.data, { todoCount: 0 }); + }; + + private assertNoopWithImportCallSucceeds = async ( + apolloClient: ApolloClient + ): Promise => { + // The noopImport query calls the noopImport lambda + const response = await apolloClient.query({ + query: gql` + query noopImport { + noopImport + } + `, + variables: {}, + }); + + // Assert the expected lambda call return + assert.deepEqual(response.data, { noopImport: 'STATIC TEST RESPONSE' }); + }; +} diff --git a/packages/integration-tests/src/test-projects/data_access_from_function/amplify/backend.ts b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/backend.ts new file mode 100644 index 00000000000..bd208c8043f --- /dev/null +++ b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/backend.ts @@ -0,0 +1,6 @@ +import { defineBackend } from '@aws-amplify/backend'; +import { data } from './data/resource.js'; +import { todoCount } from './functions/todo-count/resource.js'; +import { customerS3Import } from './functions/customer-s3-import/resource.js'; + +const backend = defineBackend({ data, todoCount, customerS3Import }); diff --git a/packages/integration-tests/src/test-projects/data_access_from_function/amplify/data/resource.ts b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/data/resource.ts new file mode 100644 index 00000000000..4c3442bdc72 --- /dev/null +++ b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/data/resource.ts @@ -0,0 +1,42 @@ +import { a, ClientSchema, defineData } from '@aws-amplify/backend'; +import { todoCount } from '../functions/todo-count/resource.js'; +import { customerS3Import } from '../functions/customer-s3-import/resource.js'; + +const schema = a + .schema({ + Todo: a + .model({ + title: a.string().required(), + done: a.boolean().default(false), // default value is false + }) + .authorization((allow) => [allow.publicApiKey()]), + todoCount: a + .query() + .arguments({}) + .returns(a.integer()) + .handler(a.handler.function(todoCount)) + .authorization((allow) => [allow.publicApiKey()]), + noopImport: a + .query() + .arguments({}) + .returns(a.string()) + .handler(a.handler.function(customerS3Import)) + .authorization((allow) => [allow.publicApiKey()]), + }) + .authorization((allow) => [ + allow.resource(todoCount), + allow.resource(customerS3Import), + ]); + +export type Schema = ClientSchema; + +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: 'apiKey', + // API Key is used for a.allow.public() rules + apiKeyAuthorizationMode: { + expiresInDays: 30, + }, + }, +}); diff --git a/packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/customer-s3-import/handler.ts b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/customer-s3-import/handler.ts new file mode 100644 index 00000000000..fc4b448637f --- /dev/null +++ b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/customer-s3-import/handler.ts @@ -0,0 +1,22 @@ +import type { Handler } from 'aws-lambda'; +import { Amplify } from 'aws-amplify'; +import { generateClient } from 'aws-amplify/data'; +import type { Schema } from '../../data/resource.js'; +import { getAmplifyDataClientConfig } from '@aws-amplify/backend/function/runtime'; +// @ts-ignore +import { env } from '$amplify/env/customer-s3-import.js'; +import { S3Client } from '@aws-sdk/client-s3'; + +const { resourceConfig, libraryOptions } = await getAmplifyDataClientConfig( + env +); + +Amplify.configure(resourceConfig, libraryOptions); + +const client = generateClient(); + +export const handler: Handler = async () => { + const _s3Client = new S3Client(); + const _todos = await client.models.Todo.list(); + return 'STATIC TEST RESPONSE'; +}; diff --git a/packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/customer-s3-import/resource.ts b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/customer-s3-import/resource.ts new file mode 100644 index 00000000000..c985d40794d --- /dev/null +++ b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/customer-s3-import/resource.ts @@ -0,0 +1,7 @@ +import { defineFunction } from '@aws-amplify/backend'; + +export const customerS3Import = defineFunction({ + name: 'customer-s3-import', + entry: './handler.ts', + timeoutSeconds: 30, +}); diff --git a/packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/todo-count/handler.ts b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/todo-count/handler.ts new file mode 100644 index 00000000000..0383ebf092d --- /dev/null +++ b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/todo-count/handler.ts @@ -0,0 +1,20 @@ +import type { Handler } from 'aws-lambda'; +import { Amplify } from 'aws-amplify'; +import { generateClient } from 'aws-amplify/data'; +import type { Schema } from '../../data/resource.js'; +import { getAmplifyDataClientConfig } from '@aws-amplify/backend/function/runtime'; +// @ts-ignore +import { env } from '$amplify/env/todo-count.js'; + +const { resourceConfig, libraryOptions } = await getAmplifyDataClientConfig( + env +); + +Amplify.configure(resourceConfig, libraryOptions); + +const client = generateClient(); + +export const handler: Handler = async () => { + const todos = await client.models.Todo.list(); + return todos.data.length; +}; diff --git a/packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/todo-count/resource.ts b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/todo-count/resource.ts new file mode 100644 index 00000000000..6fa45f4b81f --- /dev/null +++ b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/functions/todo-count/resource.ts @@ -0,0 +1,7 @@ +import { defineFunction } from '@aws-amplify/backend'; + +export const todoCount = defineFunction({ + name: 'todo-count', + entry: './handler.ts', + timeoutSeconds: 30, +}); From daaedb656526da08448a0ad3eb2080c4a2633d93 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Wed, 4 Dec 2024 19:33:26 +0100 Subject: [PATCH 138/199] fix: add cdk error mapping for error 'command cdk not found' (#2294) * fix: add cdk error mapping for error 'command cdk not found' * PR Updates --- .changeset/rude-moles-attack.md | 5 +++++ packages/backend-deployer/src/cdk_error_mapper.test.ts | 6 ++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 9 +++++++++ 3 files changed, 20 insertions(+) create mode 100644 .changeset/rude-moles-attack.md diff --git a/.changeset/rude-moles-attack.md b/.changeset/rude-moles-attack.md new file mode 100644 index 00000000000..695e49e9cb0 --- /dev/null +++ b/.changeset/rude-moles-attack.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +add cdk error mapping for error "cdk command not found" diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index a4f9d4952be..f20049121ec 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -374,6 +374,12 @@ const testErrorMappings = [ expectedDownstreamErrorMessage: `This CDK CLI is not compatible with the CDK library used by your application. Please upgrade the CLI to the latest version. (Cloud assembly schema version mismatch: Maximum schema version supported is 36.0.0, but found 36.1.1)`, }, + { + errorMessage: `error Command cdk not found. Did you mean cdl?`, + expectedTopLevelErrorMessage: 'Unable to detect cdk installation', + errorName: 'CDKNotFoundError', + expectedDownstreamErrorMessage: `error Command cdk not found. Did you mean cdl?`, + }, { errorMessage: `[31m amplify-some-stack failed: ValidationError: Stack:stack-arn is in UPDATE_ROLLBACK_FAILED state and can not be updated.`, expectedTopLevelErrorMessage: diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 5b121ea6731..5044b430ab5 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -156,6 +156,14 @@ export class CdkErrorMapper { errorName: 'CDKVersionMismatchError', classification: 'ERROR', }, + { + errorRegex: /Command cdk not found/, + humanReadableErrorMessage: 'Unable to detect cdk installation', + resolutionMessage: + "Ensure dependencies in your project are installed with your package manager. For example, by running 'yarn install' or 'npm install'", + errorName: 'CDKNotFoundError', + classification: 'ERROR', + }, { errorRegex: new RegExp( `(SyntaxError|ReferenceError|TypeError)( \\[[A-Z_]+])?:((?:.|${this.multiLineEolRegex})*?at .*)` @@ -468,6 +476,7 @@ export type CDKDeploymentError = | 'BootstrapDetectionError' | 'BootstrapOutdatedError' | 'CDKAssetPublishError' + | 'CDKNotFoundError' | 'CDKResolveAWSAccountError' | 'CDKVersionMismatchError' | 'CFNUpdateNotSupportedError' From 5a47d214172f093b8d29392175e10bcc364d38ee Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Wed, 4 Dec 2024 12:41:02 -0800 Subject: [PATCH 139/199] add error mapping for lambda exceeding max size (#2295) * add error mapping for lambda exceeding max size * update resolution message --- .changeset/new-mails-speak.md | 5 +++++ .../backend-deployer/src/cdk_error_mapper.test.ts | 12 ++++++++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 12 +++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 .changeset/new-mails-speak.md diff --git a/.changeset/new-mails-speak.md b/.changeset/new-mails-speak.md new file mode 100644 index 00000000000..1ef0aa5de91 --- /dev/null +++ b/.changeset/new-mails-speak.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +add error mapping for lambda exceeding max size diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index f20049121ec..a7b447ffbb0 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -621,6 +621,18 @@ npm error enoent`, errorName: 'CloudFormationDeletionError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: `Error: some-stack failed: InvalidParameterValueException: Unzipped size must be smaller than 262144000 bytes`, + expectedTopLevelErrorMessage: 'Maximum Lambda size exceeded', + errorName: 'LambdaMaxSizeExceededError', + expectedDownstreamErrorMessage: undefined, + }, + { + errorMessage: `Error: some-stack failed: InvalidParameterValueException: Function code combined with layers exceeds the maximum allowed size of 262144000 bytes. The actual size is 306703523 bytes.`, + expectedTopLevelErrorMessage: 'Maximum Lambda size exceeded', + errorName: 'LambdaMaxSizeExceededError', + expectedDownstreamErrorMessage: undefined, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 5044b430ab5..666e4026269 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -220,6 +220,15 @@ export class CdkErrorMapper { errorName: 'MultipleSandboxInstancesError', classification: 'ERROR', }, + { + errorRegex: + /InvalidParameterValueException:(.*) (size must be smaller than|exceeds the maximum allowed size of) (?\d+) bytes/, + humanReadableErrorMessage: 'Maximum Lambda size exceeded', + resolutionMessage: + 'Make sure your Lambda bundled packages with layers and dependencies is smaller than {maxSize} bytes unzipped.', + errorName: 'LambdaMaxSizeExceededError', + classification: 'ERROR', + }, { errorRegex: /User:(.*) is not authorized to perform: lambda:GetLayerVersion on resource:(.*) because no resource-based policy allows the lambda:GetLayerVersion action/, @@ -493,4 +502,5 @@ export type CDKDeploymentError = | 'InvalidPackageJsonError' | 'SecretNotSetError' | 'SyntaxError' - | 'GetLambdaLayerVersionError'; + | 'GetLambdaLayerVersionError' + | 'LambdaMaxSizeExceededError'; From e0e62bd17614ebefc131493aa7e9f73e72a58605 Mon Sep 17 00:00:00 2001 From: awsluja <110861985+awsluja@users.noreply.github.com> Date: Wed, 4 Dec 2024 13:55:56 -0800 Subject: [PATCH 140/199] fix: consolidate backend secret custom resources (#2011) * fix: use a single custom resource for fetching secrets * chore: update package-lock.json * chore: add changeset * chore: refactor changes * chore: cleanup * chore: cleanup * chore: cleanup * chore: cleanup * chore: rename resource id * feed pr base sha and ref into envs before scripts (#2168) * feed pr base sha and ref into envs before scripts * removing empty file --------- Co-authored-by: Roshane Pascual --- .changeset/purple-otters-poke.md | 5 ++ .changeset/spicy-rules-speak.md | 2 + .../engine/backend-secret/backend_secret.ts | 10 +-- .../backend_secret_fetcher_factory.test.ts | 23 ++--- .../backend_secret_fetcher_factory.ts | 60 ++++++++++--- .../lambda/backend_secret_fetcher.test.ts | 8 +- .../lambda/backend_secret_fetcher.ts | 89 ++++++++++--------- .../lambda/backend_secret_fetcher_types.ts | 2 +- 8 files changed, 119 insertions(+), 80 deletions(-) create mode 100644 .changeset/purple-otters-poke.md create mode 100644 .changeset/spicy-rules-speak.md diff --git a/.changeset/purple-otters-poke.md b/.changeset/purple-otters-poke.md new file mode 100644 index 00000000000..2c4c4f6bd57 --- /dev/null +++ b/.changeset/purple-otters-poke.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend': patch +--- + +Backend Secrets now use a single custom resource to reduce concurrent lambda executions. diff --git a/.changeset/spicy-rules-speak.md b/.changeset/spicy-rules-speak.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/spicy-rules-speak.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/backend/src/engine/backend-secret/backend_secret.ts b/packages/backend/src/engine/backend-secret/backend_secret.ts index bb12316859a..af08742d3b5 100644 --- a/packages/backend/src/engine/backend-secret/backend_secret.ts +++ b/packages/backend/src/engine/backend-secret/backend_secret.ts @@ -16,7 +16,7 @@ export class CfnTokenBackendSecret implements BackendSecret { * The name of the secret to fetch. */ constructor( - private readonly name: string, + private readonly secretName: string, private readonly secretResourceFactory: BackendSecretFetcherFactory ) {} /** @@ -28,11 +28,11 @@ export class CfnTokenBackendSecret implements BackendSecret { ): SecretValue => { const secretResource = this.secretResourceFactory.getOrCreate( scope, - this.name, + this.secretName, backendIdentifier ); - const val = secretResource.getAttString('secretValue'); + const val = secretResource.getAttString(`${this.secretName}`); return SecretValue.unsafePlainText(val); // safe since 'val' is a cdk token. }; @@ -43,11 +43,11 @@ export class CfnTokenBackendSecret implements BackendSecret { return { branchSecretPath: ParameterPathConversions.toParameterFullPath( backendIdentifier, - this.name + this.secretName ), sharedSecretPath: ParameterPathConversions.toParameterFullPath( backendIdentifier.namespace, - this.name + this.secretName ), }; }; diff --git a/packages/backend/src/engine/backend-secret/backend_secret_fetcher_factory.test.ts b/packages/backend/src/engine/backend-secret/backend_secret_fetcher_factory.test.ts index b2d5cd1bb9a..b9b95006bac 100644 --- a/packages/backend/src/engine/backend-secret/backend_secret_fetcher_factory.test.ts +++ b/packages/backend/src/engine/backend-secret/backend_secret_fetcher_factory.test.ts @@ -9,7 +9,7 @@ import { } from './backend_secret_fetcher_factory.js'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; -const secretResourceType = 'Custom::SecretFetcherResource'; +const secretResourceType = 'Custom::AmplifySecretFetcherResource'; const namespace = 'testId'; const name = 'testBranch'; const secretName1 = 'testSecretName1'; @@ -33,26 +33,18 @@ void describe('getOrCreate', () => { resourceFactory.getOrCreate(stack, secretName2, backendId); const template = Template.fromStack(stack); - template.resourceCountIs(secretResourceType, 2); - let customResources = template.findResources(secretResourceType, { + // only one custom resource is created that fetches all secrets + template.resourceCountIs(secretResourceType, 1); + const customResources = template.findResources(secretResourceType, { Properties: { namespace, name, - secretName: secretName1, + secretNames: [secretName1, secretName2], secretLastUpdated, }, }); assert.equal(Object.keys(customResources).length, 1); - customResources = template.findResources(secretResourceType, { - Properties: { - namespace, - name, - secretName: secretName2, - }, - }); - assert.equal(Object.keys(customResources).length, 1); - // only 1 secret fetcher lambda and 1 resource provider lambda are created. const providers = template.findResources('AWS::Lambda::Function'); const names = Object.keys(providers); @@ -67,6 +59,8 @@ void describe('getOrCreate', () => { void it('does not create duplicate resource for the same secret name', () => { const app = new App(); const stack = new Stack(app); + + // ensure only 1 resource is created even if this is called twice resourceFactory.getOrCreate(stack, secretName1, backendId); resourceFactory.getOrCreate(stack, secretName1, backendId); @@ -78,6 +72,7 @@ void describe('getOrCreate', () => { const body = customResources[resourceName]['Properties']; assert.strictEqual(body['namespace'], namespace); assert.strictEqual(body['name'], name); - assert.strictEqual(body['secretName'], secretName1); + assert.equal(body['secretNames'].length, 1); + assert.equal(body['secretNames'][0], secretName1); }); }); diff --git a/packages/backend/src/engine/backend-secret/backend_secret_fetcher_factory.ts b/packages/backend/src/engine/backend-secret/backend_secret_fetcher_factory.ts index f3fd0a2164e..097a6d36d37 100644 --- a/packages/backend/src/engine/backend-secret/backend_secret_fetcher_factory.ts +++ b/packages/backend/src/engine/backend-secret/backend_secret_fetcher_factory.ts @@ -1,18 +1,38 @@ import { Construct } from 'constructs'; import { BackendSecretFetcherProviderFactory } from './backend_secret_fetcher_provider_factory.js'; -import { CustomResource } from 'aws-cdk-lib'; +import { CustomResource, CustomResourceProps, Lazy } from 'aws-cdk-lib'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; import { SecretResourceProps } from './lambda/backend_secret_fetcher_types.js'; /** * Resource provider ID for the backend secret resource. */ -export const SECRET_RESOURCE_PROVIDER_ID = 'SecretFetcherResourceProvider'; +export const SECRET_RESOURCE_PROVIDER_ID = + 'AmplifySecretFetcherResourceProvider'; + +class SecretFetcherCustomResource extends CustomResource { + private secrets: Set; + constructor( + scope: Construct, + id: string, + props: CustomResourceProps, + secrets: Set + ) { + super(scope, id, { + ...props, + }); + this.secrets = secrets; + } + + public addSecret = (secretName: string) => { + this.secrets.add(secretName); + }; +} /** * Type of the backend custom CFN resource. */ -const SECRET_RESOURCE_TYPE = `Custom::SecretFetcherResource`; +const SECRET_RESOURCE_TYPE = `Custom::AmplifySecretFetcherResource`; /** * The factory to create backend secret-fetcher resource. @@ -33,15 +53,18 @@ export class BackendSecretFetcherFactory { scope: Construct, secretName: string, backendIdentifier: BackendIdentifier - ): CustomResource => { - const secretResourceId = `${secretName}SecretFetcherResource`; + ): SecretFetcherCustomResource => { + const secretResourceId = `AmplifySecretFetcherResource`; const existingResource = scope.node.tryFindChild( secretResourceId - ) as CustomResource; + ) as SecretFetcherCustomResource; if (existingResource) { + existingResource.addSecret(secretName); return existingResource; } + const secrets: Set = new Set(); + secrets.add(secretName); const provider = this.secretProviderFactory.getOrCreateInstance( scope, @@ -59,16 +82,25 @@ export class BackendSecretFetcherFactory { namespace: backendIdentifier.namespace, name: backendIdentifier.name, type: backendIdentifier.type, - secretName: secretName, + secretNames: Lazy.list({ + produce: () => { + return Array.from(secrets); + }, + }), }; - return new CustomResource(scope, secretResourceId, { - serviceToken: provider.serviceToken, - properties: { - ...customResourceProps, - secretLastUpdated, // this property is only to trigger resource update event. + return new SecretFetcherCustomResource( + scope, + secretResourceId, + { + serviceToken: provider.serviceToken, + properties: { + ...customResourceProps, + secretLastUpdated, // this property is only to trigger resource update event. + }, + resourceType: SECRET_RESOURCE_TYPE, }, - resourceType: SECRET_RESOURCE_TYPE, - }); + secrets + ); }; } diff --git a/packages/backend/src/engine/backend-secret/lambda/backend_secret_fetcher.test.ts b/packages/backend/src/engine/backend-secret/lambda/backend_secret_fetcher.test.ts index 0cb0773446e..71c4e6cb500 100644 --- a/packages/backend/src/engine/backend-secret/lambda/backend_secret_fetcher.test.ts +++ b/packages/backend/src/engine/backend-secret/lambda/backend_secret_fetcher.test.ts @@ -43,7 +43,7 @@ const customResourceEventCommon = { ResourceType: 'AWS::CloudFormation::CustomResource', ResourceProperties: { ...testBackendIdentifier, - secretName: testSecretName, + secretNames: [testSecretName], ServiceToken: 'token', }, OldResourceProperties: {}, @@ -84,7 +84,7 @@ void describe('handleCreateUpdateEvent', () => { Promise.resolve(testSecret) ); const val = await handleCreateUpdateEvent(secretHandler, createCfnEvent); - assert.equal(val, testSecretValue); + assert.equal(val[testSecretName], testSecretValue); assert.equal(mockGetSecret.mock.callCount(), 1); assert.deepStrictEqual(mockGetSecret.mock.calls[0].arguments, [ @@ -120,7 +120,7 @@ void describe('handleCreateUpdateEvent', () => { ); const val = await handleCreateUpdateEvent(secretHandler, createCfnEvent); - assert.equal(val, testSecretValue); + assert.equal(val[testSecretName], testSecretValue); assert.equal(mockGetSecret.mock.callCount(), 2); assert.deepStrictEqual(mockGetSecret.mock.calls[0].arguments, [ @@ -145,7 +145,7 @@ void describe('handleCreateUpdateEvent', () => { } ); const val = await handleCreateUpdateEvent(secretHandler, createCfnEvent); - assert.equal(val, testSecretValue); + assert.equal(val[testSecretName], testSecretValue); assert.equal(mockGetSecret.mock.callCount(), 2); assert.deepStrictEqual(mockGetSecret.mock.calls[0].arguments, [ diff --git a/packages/backend/src/engine/backend-secret/lambda/backend_secret_fetcher.ts b/packages/backend/src/engine/backend-secret/lambda/backend_secret_fetcher.ts index 6c434c211d4..f240eb45dbe 100644 --- a/packages/backend/src/engine/backend-secret/lambda/backend_secret_fetcher.ts +++ b/packages/backend/src/engine/backend-secret/lambda/backend_secret_fetcher.ts @@ -22,11 +22,11 @@ export const handler = async ( const physicalId = event.RequestType === 'Create' ? randomUUID() : event.PhysicalResourceId; - let data: { secretValue: string } | undefined = undefined; + let data: Record | undefined = undefined; if (event.RequestType === 'Update' || event.RequestType === 'Create') { - const val = await handleCreateUpdateEvent(secretClient, event); + const secretMap = await handleCreateUpdateEvent(secretClient, event); data = { - secretValue: val, + ...secretMap, }; } @@ -47,54 +47,59 @@ export const handler = async ( export const handleCreateUpdateEvent = async ( secretClient: SecretClient, event: CloudFormationCustomResourceEvent -): Promise => { +): Promise> => { const props = event.ResourceProperties as unknown as SecretResourceProps; - let secret: string | undefined; + const secretMap: Record = {}; + for (const secretName of props.secretNames) { + let secretValue: string | undefined = undefined; + try { + const resp = await secretClient.getSecret( + { + namespace: props.namespace, + name: props.name, + type: props.type, + }, + { + name: secretName, + } + ); + secretValue = resp.value; + } catch (err) { + const secretErr = err as SecretError; + if (secretErr.httpStatusCode && secretErr.httpStatusCode >= 500) { + throw new Error( + `Failed to retrieve backend secret '${secretName}' for '${ + props.namespace + }/${props.name}'. Reason: ${JSON.stringify(err)}` + ); + } + } - try { - const resp = await secretClient.getSecret( - { - namespace: props.namespace, - name: props.name, - type: props.type, - }, - { - name: props.secretName, + // if the secret is not available in branch path, try retrieving it at the app-level. + if (!secretValue) { + try { + const resp = await secretClient.getSecret(props.namespace, { + name: secretName, + }); + secretValue = resp.value; + } catch (err) { + throw new Error( + `Failed to retrieve backend secret '${secretName}' for '${ + props.namespace + }'. Reason: ${JSON.stringify(err)}` + ); } - ); - secret = resp?.value; - } catch (err) { - const secretErr = err as SecretError; - if (secretErr.httpStatusCode && secretErr.httpStatusCode >= 500) { - throw new Error( - `Failed to retrieve backend secret '${props.secretName}' for '${ - props.namespace - }/${props.name}'. Reason: ${JSON.stringify(err)}` - ); } - } - // if the secret is not available in branch path, try retrieving it at the app-level. - if (!secret) { - try { - const resp = await secretClient.getSecret(props.namespace, { - name: props.secretName, - }); - secret = resp?.value; - } catch (err) { + if (!secretValue) { throw new Error( - `Failed to retrieve backend secret '${props.secretName}' for '${ - props.namespace - }'. Reason: ${JSON.stringify(err)}` + `Unable to find backend secret for backend '${props.namespace}', branch '${props.name}', name '${secretName}'` ); } - } - if (!secret) { - throw new Error( - `Unable to find backend secret for backend '${props.namespace}', branch '${props.name}', name '${props.secretName}'` - ); + // store the secret->secretValue pair in the secret map + secretMap[secretName] = secretValue; } - return secret; + return secretMap; }; diff --git a/packages/backend/src/engine/backend-secret/lambda/backend_secret_fetcher_types.ts b/packages/backend/src/engine/backend-secret/lambda/backend_secret_fetcher_types.ts index a5cb72b4de3..3e625238b53 100644 --- a/packages/backend/src/engine/backend-secret/lambda/backend_secret_fetcher_types.ts +++ b/packages/backend/src/engine/backend-secret/lambda/backend_secret_fetcher_types.ts @@ -1,5 +1,5 @@ import { BackendIdentifier } from '@aws-amplify/plugin-types'; export type SecretResourceProps = Omit & { - secretName: string; + secretNames: string[]; }; From a40626360254a4206656a45fad667c5d818755a9 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Thu, 5 Dec 2024 18:02:29 +0100 Subject: [PATCH 141/199] fix: narrow the error parsing for ExpiredToken regex (#2298) --- .changeset/curly-trainers-look.md | 5 +++++ packages/backend-deployer/src/cdk_error_mapper.test.ts | 6 ++++-- packages/backend-deployer/src/cdk_error_mapper.ts | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 .changeset/curly-trainers-look.md diff --git a/.changeset/curly-trainers-look.md b/.changeset/curly-trainers-look.md new file mode 100644 index 00000000000..eb591227ab0 --- /dev/null +++ b/.changeset/curly-trainers-look.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +Narrow the error parsing for ExpiredToken regex diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index a7b447ffbb0..8aa1e1d50b0 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -16,11 +16,13 @@ const testErrorMappings = [ expectedDownstreamErrorMessage: undefined, }, { - errorMessage: 'ExpiredToken', + errorMessage: + 'ExpiredToken: The security token included in the request is expired', expectedTopLevelErrorMessage: 'The security token included in the request is invalid.', errorName: 'ExpiredTokenError', - expectedDownstreamErrorMessage: 'ExpiredToken', + expectedDownstreamErrorMessage: + 'ExpiredToken: The security token included in the request is expired', }, { errorMessage: diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 666e4026269..2927ad1b339 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -99,7 +99,7 @@ export class CdkErrorMapper { }> => [ { errorRegex: - /ExpiredToken|(Error|InvalidClientTokenId): The security token included in the request is (expired|invalid)/, + /ExpiredToken: .*|(Error|InvalidClientTokenId): The security token included in the request is (expired|invalid)/, humanReadableErrorMessage: 'The security token included in the request is invalid.', resolutionMessage: From d66ab17c8fe81836bccf5d3518bef36c45ee2649 Mon Sep 17 00:00:00 2001 From: Leena <81589006+ShadowCat567@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:10:19 -0500 Subject: [PATCH 142/199] Mapping for more EPERM errors (#2296) * catching more EPERM errors * adjust error message to be closer to actual message in test * changeset added --------- Co-authored-by: Vieltojarvi --- .changeset/fuzzy-squids-raise.md | 5 +++++ .../src/cdk_error_mapper.test.ts | 22 +++++++++++++++++-- .../backend-deployer/src/cdk_error_mapper.ts | 16 ++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 .changeset/fuzzy-squids-raise.md diff --git a/.changeset/fuzzy-squids-raise.md b/.changeset/fuzzy-squids-raise.md new file mode 100644 index 00000000000..1d809423be8 --- /dev/null +++ b/.changeset/fuzzy-squids-raise.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +added mapping for additional EPERM errors diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 8aa1e1d50b0..aaec39ae451 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -362,8 +362,26 @@ const testErrorMappings = [ expectedDownstreamErrorMessage: `EACCES: permission denied, unlink '.amplify/artifacts/cdk.out/synth.lock'`, }, { - errorMessage: `EPERM: operation not permitted, rename 'C:/Users/someUser/amplify/artifacts/cdk.out/synth.lock.6785_1' → 'C:/Users/someUser/amplify/artifacts/cdk.out/synth.lock'`, - expectedTopLevelErrorMessage: `Not permitted to rename file: 'C:/Users/someUser/amplify/artifacts/cdk.out/synth.lock.6785_1'`, + errorMessage: `[31mEPERM: operation not permitted, rename 'C:/Users/someUser/.amplify/artifacts/cdk.out/synth.lock.6785_1' → 'C:/Users/someUser/amplify/artifacts/cdk.out/synth.lock' [31m`, + expectedTopLevelErrorMessage: `Not permitted to rename file: 'C:/Users/someUser/.amplify/artifacts/cdk.out/synth.lock.6785_1'`, + errorName: 'FilePermissionsError', + expectedDownstreamErrorMessage: undefined, + }, + { + errorMessage: `[31mEPERM: operation not permitted, unlink '.amplify/artifacts/cdk.out/read.4276_1.lock' [31m`, + expectedTopLevelErrorMessage: `Operation not permitted on file: '.amplify/artifacts/cdk.out/read.4276_1.lock'`, + errorName: 'FilePermissionsError', + expectedDownstreamErrorMessage: undefined, + }, + { + errorMessage: `[31mEPERM: operation not permitted, open '.amplify/artifacts/cdk.out/synth.lock.6785_1' [31m`, + expectedTopLevelErrorMessage: `Operation not permitted on file: '.amplify/artifacts/cdk.out/synth.lock.6785_1'`, + errorName: 'FilePermissionsError', + expectedDownstreamErrorMessage: undefined, + }, + { + errorMessage: `Could not create output directory .amplify/artifacts/cdk.out (EPERM: operation not permitted, mkdir '.amplify/artifacts/cdk.out')`, + expectedTopLevelErrorMessage: `Not permitted to create the directory '.amplify/artifacts/cdk.out'`, errorName: 'FilePermissionsError', expectedDownstreamErrorMessage: undefined, }, diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 2927ad1b339..7c785f21326 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -200,6 +200,22 @@ export class CdkErrorMapper { errorName: 'FilePermissionsError', classification: 'ERROR', }, + { + errorRegex: + /EPERM: operation not permitted, mkdir '(.*).amplify\/artifacts\/cdk.out'/, + humanReadableErrorMessage: `Not permitted to create the directory '.amplify/artifacts/cdk.out'`, + resolutionMessage: `Check the permissions of '.amplify' folder and try running the command again`, + errorName: 'FilePermissionsError', + classification: 'ERROR', + }, + { + errorRegex: + /EPERM: operation not permitted, (unlink|open) (?(.*)\.lock\S+)/, + humanReadableErrorMessage: 'Operation not permitted on file: {fileName}', + resolutionMessage: `Check the permissions of '.amplify' folder and try running the command again`, + errorName: 'FilePermissionsError', + classification: 'ERROR', + }, { errorRegex: new RegExp( `\\[ERR_MODULE_NOT_FOUND\\]:(.*)${this.multiLineEolRegex}|Error: Cannot find module (.*)` From 1593ce8f07257886c834ab7120ae1a54f8133c11 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Thu, 5 Dec 2024 10:22:30 -0800 Subject: [PATCH 143/199] add error mapping for lambda bundling into an empty zip (#2297) * add error mapping for lambda bundling into an empty zip * change error to fault * update test --- .changeset/brave-walls-thank.md | 5 +++++ packages/backend-deployer/src/cdk_error_mapper.test.ts | 6 ++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 9 +++++++++ 3 files changed, 20 insertions(+) create mode 100644 .changeset/brave-walls-thank.md diff --git a/.changeset/brave-walls-thank.md b/.changeset/brave-walls-thank.md new file mode 100644 index 00000000000..34fbdd22a21 --- /dev/null +++ b/.changeset/brave-walls-thank.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +add error mapping for lambda bundling into an empty zip diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index aaec39ae451..8860260173e 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -653,6 +653,12 @@ npm error enoent`, errorName: 'LambdaMaxSizeExceededError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: `Error: some-stack failed: InvalidParameterValueException: Uploaded file must be a non-empty zip`, + expectedTopLevelErrorMessage: 'Lambda bundled into an empty zip', + errorName: 'LambdaEmptyZipFault', + expectedDownstreamErrorMessage: undefined, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 7c785f21326..4418fc3275b 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -245,6 +245,14 @@ export class CdkErrorMapper { errorName: 'LambdaMaxSizeExceededError', classification: 'ERROR', }, + { + errorRegex: + /InvalidParameterValueException: Uploaded file must be a non-empty zip/, + humanReadableErrorMessage: 'Lambda bundled into an empty zip', + resolutionMessage: `Try removing '.amplify/artifacts' then running the command again. If it still doesn't work, see https://github.com/aws/aws-cdk/issues/18459 for more methods.`, + errorName: 'LambdaEmptyZipFault', + classification: 'FAULT', + }, { errorRegex: /User:(.*) is not authorized to perform: lambda:GetLayerVersion on resource:(.*) because no resource-based policy allows the lambda:GetLayerVersion action/, @@ -519,4 +527,5 @@ export type CDKDeploymentError = | 'SecretNotSetError' | 'SyntaxError' | 'GetLambdaLayerVersionError' + | 'LambdaEmptyZipFault' | 'LambdaMaxSizeExceededError'; From fc7e4d4c91b85a61408d157295ccf19d7f503807 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:33:35 +0100 Subject: [PATCH 144/199] fix: validate input stack name for generate commands (#2299) * fix: validate input stack name for generate commands * lint fix --- .changeset/shy-ladybugs-buy.md | 5 +++++ .../src/commands/generate/generate_command.ts | 15 +++++++++++++++ .../generate/generate_command_factory.test.ts | 19 ++++++++++++++++++- 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 .changeset/shy-ladybugs-buy.md diff --git a/.changeset/shy-ladybugs-buy.md b/.changeset/shy-ladybugs-buy.md new file mode 100644 index 00000000000..50cc8f701e9 --- /dev/null +++ b/.changeset/shy-ladybugs-buy.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-cli': patch +--- + +validate input stack name for generate commands diff --git a/packages/cli/src/commands/generate/generate_command.ts b/packages/cli/src/commands/generate/generate_command.ts index 4596da971b6..a84157a5481 100644 --- a/packages/cli/src/commands/generate/generate_command.ts +++ b/packages/cli/src/commands/generate/generate_command.ts @@ -4,6 +4,7 @@ import { GenerateFormsCommand } from './forms/generate_forms_command.js'; import { GenerateGraphqlClientCodeCommand } from './graphql-client-code/generate_graphql_client_code_command.js'; import { CommandMiddleware } from '../../command_middleware.js'; import { GenerateSchemaCommand } from './schema-from-database/generate_schema_command.js'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; /** * An entry point for generate command. @@ -62,6 +63,20 @@ export class GenerateCommand implements CommandModule { type: 'string', array: false, }) + .check(async (argv) => { + const stackNameRegex = + /^[a-zA-Z][-a-zA-Z0-9]{1,127}$|^arn:[-a-zA-Z0-9:/._+]$/; + if (argv['stack'] && typeof argv['stack'] === 'string') { + if (!argv.stack.match(stackNameRegex)) { + throw new AmplifyUserError('InvalidCommandInputError', { + message: `Invalid --stack name provided: ${argv.stack}`, + resolution: + 'Check the value of the stack name provided and try again.', + }); + } + } + return true; + }) .middleware([this.commandMiddleware.ensureAwsCredentialAndRegion]) ); }; diff --git a/packages/cli/src/commands/generate/generate_command_factory.test.ts b/packages/cli/src/commands/generate/generate_command_factory.test.ts index 4b7108ed844..fe340fb4eed 100644 --- a/packages/cli/src/commands/generate/generate_command_factory.test.ts +++ b/packages/cli/src/commands/generate/generate_command_factory.test.ts @@ -1,7 +1,10 @@ import yargs from 'yargs'; import { describe, it } from 'node:test'; import assert from 'node:assert'; -import { TestCommandRunner } from '../../test-utils/command_runner.js'; +import { + TestCommandError, + TestCommandRunner, +} from '../../test-utils/command_runner.js'; import { createGenerateCommand } from './generate_command_factory.js'; /** @@ -31,6 +34,20 @@ void describe('top level generate command', () => { assert.match(output, /Not enough non-option arguments/); }); + void it('fails if stack argument provided is invalid', async () => { + await assert.rejects( + () => commandRunner.runCommand('generate outputs --stack 3-Invalid'), + (error: TestCommandError) => { + assert.strictEqual(error.error.name, 'InvalidCommandInputError'); + assert.strictEqual( + error.error.message, + 'Invalid --stack name provided: 3-Invalid' + ); + return true; + } + ); + }); + void it('should throw if top level command handler is ever called', () => { assert.throws( () => generateCommand.handler({ $0: '', _: [] }), From 7c5abe2a037904ffd8dc9db13876a8b7210d61af Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 6 Dec 2024 12:04:45 -0800 Subject: [PATCH 145/199] validate branch and app id inputs for pipeline-deploy command (#2301) * validate branch and app id inputs for pipeline-deploy command * update error * update logic for falling back to sandbox resolver for backend id --- .changeset/flat-donkeys-wonder.md | 5 ++++ .changeset/olive-singers-tease.md | 5 ++++ ...d_identifier_with_sandbox_fallback.test.ts | 27 ++++++++++++++++++ ...ackend_identifier_with_sandbox_fallback.ts | 12 ++++++-- .../pipeline_deploy_command.test.ts | 28 +++++++++++++++++++ .../pipeline_deploy_command.ts | 8 ++++++ 6 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 .changeset/flat-donkeys-wonder.md create mode 100644 .changeset/olive-singers-tease.md diff --git a/.changeset/flat-donkeys-wonder.md b/.changeset/flat-donkeys-wonder.md new file mode 100644 index 00000000000..9b04f107e4b --- /dev/null +++ b/.changeset/flat-donkeys-wonder.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-cli': patch +--- + +validate branch and app id inputs for pipeline-deploy command diff --git a/.changeset/olive-singers-tease.md b/.changeset/olive-singers-tease.md new file mode 100644 index 00000000000..11c13c01de2 --- /dev/null +++ b/.changeset/olive-singers-tease.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-cli': patch +--- + +update logic for falling back to sandbox resolver for backend id diff --git a/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.test.ts b/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.test.ts index 0509b4e80b9..4e56b5c40e6 100644 --- a/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.test.ts +++ b/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.test.ts @@ -79,3 +79,30 @@ void it('does not use sandbox id if the default identifier resolver fails and th }); assert.deepEqual(resolvedId, undefined); }); + +// stack, appId and branch can be empty string if option is added to command but no value is present (eg. 'ampx generate outputs --stack') +// this shows intent for deployed backend id so we should not fallback to sandbox id +void it('does not use sandbox id if the default identifier resolver fails and stack, appId or branch are empty strings', async () => { + const appName = 'testAppName'; + const namespaceResolver = { + resolve: () => Promise.resolve(appName), + }; + + const defaultResolver = new AppBackendIdentifierResolver(namespaceResolver); + const username = 'test-user'; + const sandboxResolver = new SandboxBackendIdResolver( + namespaceResolver, + () => + ({ + username, + } as never) + ); + const backendIdResolver = new BackendIdentifierResolverWithFallback( + defaultResolver, + sandboxResolver + ); + const resolvedId = await backendIdResolver.resolveDeployedBackendIdentifier({ + stack: '', + }); + assert.deepEqual(resolvedId, undefined); +}); diff --git a/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.ts b/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.ts index f1bcbd2808a..be7b49646e0 100644 --- a/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.ts +++ b/packages/cli/src/backend-identifier/backend_identifier_with_sandbox_fallback.ts @@ -23,7 +23,11 @@ export class BackendIdentifierResolverWithFallback resolveDeployedBackendIdentifier = async ( args: BackendIdentifierParameters ) => { - if (args.stack || args.appId || args.branch) { + if ( + args.stack !== undefined || + args.appId !== undefined || + args.branch !== undefined + ) { return this.defaultResolver.resolveDeployedBackendIdentifier(args); } @@ -33,7 +37,11 @@ export class BackendIdentifierResolverWithFallback * Resolves deployed backend id to backend id, falling back to the sandbox id if stack or appId and branch inputs are not provided */ resolveBackendIdentifier = async (args: BackendIdentifierParameters) => { - if (args.stack || args.appId || args.branch) { + if ( + args.stack !== undefined || + args.appId !== undefined || + args.branch !== undefined + ) { return this.defaultResolver.resolveBackendIdentifier(args); } diff --git a/packages/cli/src/commands/pipeline-deploy/pipeline_deploy_command.test.ts b/packages/cli/src/commands/pipeline-deploy/pipeline_deploy_command.test.ts index cfc591858f5..823c3bb6600 100644 --- a/packages/cli/src/commands/pipeline-deploy/pipeline_deploy_command.test.ts +++ b/packages/cli/src/commands/pipeline-deploy/pipeline_deploy_command.test.ts @@ -238,4 +238,32 @@ void describe('deploy command', () => { ClientConfigFormat.DART, ]); }); + + void it('throws when --branch argument has no input', async () => { + await assert.rejects( + async () => + await getCommandRunner(true).runCommand( + 'pipeline-deploy --app-id abc --branch' + ), + (error: TestCommandError) => { + assert.strictEqual(error.error.name, 'InvalidCommandInputError'); + assert.strictEqual(error.error.message, 'Invalid --branch or --app-id'); + return true; + } + ); + }); + + void it('throws when --app-id argument has no input', async () => { + await assert.rejects( + async () => + await getCommandRunner(true).runCommand( + 'pipeline-deploy --app-id --branch testBranch' + ), + (error: TestCommandError) => { + assert.strictEqual(error.error.name, 'InvalidCommandInputError'); + assert.strictEqual(error.error.message, 'Invalid --branch or --app-id'); + return true; + } + ); + }); }); diff --git a/packages/cli/src/commands/pipeline-deploy/pipeline_deploy_command.ts b/packages/cli/src/commands/pipeline-deploy/pipeline_deploy_command.ts index 503006f3c74..b52e73b60b3 100644 --- a/packages/cli/src/commands/pipeline-deploy/pipeline_deploy_command.ts +++ b/packages/cli/src/commands/pipeline-deploy/pipeline_deploy_command.ts @@ -119,6 +119,14 @@ export class PipelineDeployCommand type: 'string', array: false, choices: Object.values(ClientConfigFormat), + }) + .check(async (argv) => { + if (argv['branch'].length === 0 || argv['app-id'].length === 0) { + throw new AmplifyUserError('InvalidCommandInputError', { + message: 'Invalid --branch or --app-id', + resolution: '--branch and --app-id must be at least 1 character', + }); + } }); }; } From ecf89fac3bcc04efd60dce3b5d3757637e654165 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 6 Dec 2024 13:57:17 -0800 Subject: [PATCH 146/199] fix pipeline-deploy check (#2304) * fix pipeline-deploy check * update changeset --- .changeset/perfect-grapes-lay.md | 2 ++ .../cli/src/commands/pipeline-deploy/pipeline_deploy_command.ts | 1 + 2 files changed, 3 insertions(+) create mode 100644 .changeset/perfect-grapes-lay.md diff --git a/.changeset/perfect-grapes-lay.md b/.changeset/perfect-grapes-lay.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/perfect-grapes-lay.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/cli/src/commands/pipeline-deploy/pipeline_deploy_command.ts b/packages/cli/src/commands/pipeline-deploy/pipeline_deploy_command.ts index b52e73b60b3..413b94f54c1 100644 --- a/packages/cli/src/commands/pipeline-deploy/pipeline_deploy_command.ts +++ b/packages/cli/src/commands/pipeline-deploy/pipeline_deploy_command.ts @@ -127,6 +127,7 @@ export class PipelineDeployCommand resolution: '--branch and --app-id must be at least 1 character', }); } + return true; }); }; } From 7bebe90531ea467a4d80c8e462d6ed862c9c5d55 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 6 Dec 2024 14:24:35 -0800 Subject: [PATCH 147/199] empty change (#2305) --- .changeset/fresh-carrots-sleep.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changeset/fresh-carrots-sleep.md diff --git a/.changeset/fresh-carrots-sleep.md b/.changeset/fresh-carrots-sleep.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/fresh-carrots-sleep.md @@ -0,0 +1,2 @@ +--- +--- From f6ba240f440cce80301aa39f1ea80b2f21cb499b Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Fri, 6 Dec 2024 15:05:07 -0800 Subject: [PATCH 148/199] Upgrade execa (#2303) * Upgrade execa * use our own types * use our own types * api * api * chs --- .changeset/stale-pillows-run.md | 10 + package-lock.json | 983 +++++++++++++++--- packages/backend-deployer/package.json | 2 +- packages/backend-function/package.json | 2 +- packages/cli-core/package.json | 2 +- .../execute_with_debugger_logger.ts | 7 +- .../package_manager_controller_base.ts | 10 +- packages/cli/package.json | 2 +- packages/create-amplify/package.json | 2 +- packages/integration-tests/package.json | 2 +- .../execa_process_killer.ts | 6 +- .../process-controller/predicated_action.ts | 6 +- .../predicated_action_queue_builder.ts | 4 +- .../process-controller/process_controller.ts | 4 +- packages/plugin-types/API.md | 27 +- packages/plugin-types/package.json | 3 - .../src/package_manager_controller.ts | 29 +- scripts/check_dependencies.ts | 16 +- 18 files changed, 924 insertions(+), 193 deletions(-) create mode 100644 .changeset/stale-pillows-run.md diff --git a/.changeset/stale-pillows-run.md b/.changeset/stale-pillows-run.md new file mode 100644 index 00000000000..57eec7a7f5d --- /dev/null +++ b/.changeset/stale-pillows-run.md @@ -0,0 +1,10 @@ +--- +'@aws-amplify/backend-deployer': patch +'@aws-amplify/backend-function': patch +'create-amplify': patch +'@aws-amplify/plugin-types': minor +'@aws-amplify/cli-core': patch +'@aws-amplify/backend-cli': patch +--- + +Upgrade execa diff --git a/package-lock.json b/package-lock.json index 6a55c53ed8b..53ab71e34ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16625,6 +16625,12 @@ "string-argv": "~0.3.1" } }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "license": "MIT" + }, "node_modules/@shopify/eslint-plugin": { "version": "43.0.0", "resolved": "https://registry.npmjs.org/@shopify/eslint-plugin/-/eslint-plugin-43.0.0.tgz", @@ -16708,6 +16714,18 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -23410,6 +23428,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", @@ -24113,6 +24132,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, "license": "MIT", "engines": { "node": ">=16" @@ -24592,6 +24612,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=16.17.0" @@ -25198,6 +25219,18 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", @@ -25355,6 +25388,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-upper-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz", @@ -27564,6 +27609,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -28164,6 +28221,21 @@ "node": ">=6.0.0" } }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -30587,6 +30659,18 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "license": "MIT" }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/universal-user-agent": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", @@ -31465,6 +31549,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yoctocolors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", + "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/yup": { "version": "0.32.11", "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", @@ -31647,7 +31743,7 @@ "dependencies": { "@aws-amplify/platform-core": "^1.2.2", "@aws-amplify/plugin-types": "^1.4.0", - "execa": "^8.0.1", + "execa": "^9.5.1", "strip-ansi": "^6.0.1", "tsx": "^4.6.1" }, @@ -31656,6 +31752,124 @@ "typescript": "^5.0.0" } }, + "packages/backend-deployer/node_modules/execa": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.1.tgz", + "integrity": "sha512-QY5PPtSonnGwhhHDNI7+3RvY285c7iuJFFB+lU+oEzMY/gEGJ808owqJsrr8Otd1E/x07po1LkUBmdAc5duPAg==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "packages/backend-deployer/node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/backend-deployer/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/backend-deployer/node_modules/human-signals": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", + "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "packages/backend-deployer/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/backend-deployer/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/backend-deployer/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/backend-deployer/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/backend-function": { "name": "@aws-amplify/backend-function", "version": "1.8.0", @@ -31665,7 +31879,7 @@ "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/plugin-types": "^1.5.0", "@aws-sdk/client-s3": "^3.624.0", - "execa": "^8.0.1" + "execa": "^9.5.1" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", @@ -31680,6 +31894,124 @@ "constructs": "^10.0.0" } }, + "packages/backend-function/node_modules/execa": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.1.tgz", + "integrity": "sha512-QY5PPtSonnGwhhHDNI7+3RvY285c7iuJFFB+lU+oEzMY/gEGJ808owqJsrr8Otd1E/x07po1LkUBmdAc5duPAg==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "packages/backend-function/node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/backend-function/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/backend-function/node_modules/human-signals": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", + "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "packages/backend-function/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/backend-function/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/backend-function/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/backend-function/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/backend-function/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -31785,7 +32117,7 @@ "@smithy/node-config-provider": "^2.1.3", "@smithy/shared-ini-file-loader": "^2.2.5", "envinfo": "^7.11.0", - "execa": "^8.0.1", + "execa": "^9.5.1", "is-ci": "^3.0.1", "open": "^9.1.0", "yargs": "^17.7.2", @@ -31813,39 +32145,263 @@ "dependencies": { "@aws-amplify/platform-core": "^1.0.5", "@inquirer/prompts": "^3.0.0", - "execa": "^8.0.1", + "execa": "^9.5.1", "kleur": "^4.1.5" } }, - "packages/cli/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", + "packages/cli-core/node_modules/execa": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.1.tgz", + "integrity": "sha512-QY5PPtSonnGwhhHDNI7+3RvY285c7iuJFFB+lU+oEzMY/gEGJ808owqJsrr8Otd1E/x07po1LkUBmdAc5duPAg==", + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" }, "engines": { - "node": ">=12" + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "packages/cli/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "packages/cli/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "packages/cli-core/node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", "license": "MIT", - "engines": { + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/cli-core/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/cli-core/node_modules/human-signals": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", + "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "packages/cli-core/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/cli-core/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/cli-core/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/cli-core/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/cli/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "packages/cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "packages/cli/node_modules/execa": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.1.tgz", + "integrity": "sha512-QY5PPtSonnGwhhHDNI7+3RvY285c7iuJFFB+lU+oEzMY/gEGJ808owqJsrr8Otd1E/x07po1LkUBmdAc5duPAg==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "packages/cli/node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/cli/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/cli/node_modules/human-signals": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", + "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "packages/cli/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { "node": ">=8" } }, + "packages/cli/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/cli/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/cli/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/cli/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -31860,6 +32416,18 @@ "node": ">=8" } }, + "packages/cli/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/cli/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -31943,7 +32511,7 @@ "@aws-amplify/cli-core": "^1.1.3", "@aws-amplify/platform-core": "^1.0.3", "@aws-amplify/plugin-types": "^1.2.2", - "execa": "^8.0.1", + "execa": "^9.5.1", "kleur": "^4.1.5", "yargs": "^17.7.2" }, @@ -31974,6 +32542,72 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, + "packages/create-amplify/node_modules/execa": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.1.tgz", + "integrity": "sha512-QY5PPtSonnGwhhHDNI7+3RvY285c7iuJFFB+lU+oEzMY/gEGJ808owqJsrr8Otd1E/x07po1LkUBmdAc5duPAg==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "packages/create-amplify/node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/create-amplify/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/create-amplify/node_modules/human-signals": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", + "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, "packages/create-amplify/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -31983,6 +32617,46 @@ "node": ">=8" } }, + "packages/create-amplify/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/create-amplify/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/create-amplify/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/create-amplify/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -31997,6 +32671,18 @@ "node": ">=8" } }, + "packages/create-amplify/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/create-amplify/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -32134,7 +32820,7 @@ "aws-appsync-auth-link": "^3.0.7", "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0", - "execa": "^8.0.1", + "execa": "^9.5.1", "fs-extra": "^11.1.1", "glob": "^10.2.7", "graphql-tag": "^2.12.6", @@ -32145,6 +32831,132 @@ "uuid": "^9.0.1" } }, + "packages/integration-tests/node_modules/execa": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.1.tgz", + "integrity": "sha512-QY5PPtSonnGwhhHDNI7+3RvY285c7iuJFFB+lU+oEzMY/gEGJ808owqJsrr8Otd1E/x07po1LkUBmdAc5duPAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "packages/integration-tests/node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/integration-tests/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/integration-tests/node_modules/human-signals": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", + "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "packages/integration-tests/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/integration-tests/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/integration-tests/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/integration-tests/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/integration-tests/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -32221,131 +33033,12 @@ "name": "@aws-amplify/plugin-types", "version": "1.5.0", "license": "Apache-2.0", - "devDependencies": { - "execa": "^5.1.1" - }, "peerDependencies": { "@aws-sdk/types": "^3.609.0", "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } }, - "packages/plugin-types/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "packages/plugin-types/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/plugin-types/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "packages/plugin-types/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/plugin-types/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "packages/plugin-types/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "packages/plugin-types/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/plugin-types/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "packages/plugin-types/node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "packages/sandbox": { "name": "@aws-amplify/sandbox", "version": "1.2.6", diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index 3a57923c2bd..b8bf0a88b5c 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -21,7 +21,7 @@ "dependencies": { "@aws-amplify/platform-core": "^1.2.2", "@aws-amplify/plugin-types": "^1.4.0", - "execa": "^8.0.1", + "execa": "^9.5.1", "tsx": "^4.6.1", "strip-ansi": "^6.0.1" }, diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index de43cf23183..eec914ab835 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -28,7 +28,7 @@ "@aws-amplify/backend-output-storage": "^1.1.3", "@aws-amplify/plugin-types": "^1.5.0", "@aws-sdk/client-s3": "^3.624.0", - "execa": "^8.0.1" + "execa": "^9.5.1" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.6", diff --git a/packages/cli-core/package.json b/packages/cli-core/package.json index 0d1e6f022da..801125ece4a 100644 --- a/packages/cli-core/package.json +++ b/packages/cli-core/package.json @@ -21,7 +21,7 @@ "dependencies": { "@aws-amplify/platform-core": "^1.0.5", "@inquirer/prompts": "^3.0.0", - "execa": "^8.0.1", + "execa": "^9.5.1", "kleur": "^4.1.5" } } diff --git a/packages/cli-core/src/package-manager-controller/execute_with_debugger_logger.ts b/packages/cli-core/src/package-manager-controller/execute_with_debugger_logger.ts index 2df5626e423..5df16c849dc 100644 --- a/packages/cli-core/src/package-manager-controller/execute_with_debugger_logger.ts +++ b/packages/cli-core/src/package-manager-controller/execute_with_debugger_logger.ts @@ -1,6 +1,7 @@ import { LogLevel } from '../printer/printer.js'; -import { type Options, execa as _execa } from 'execa'; +import { ExecaMethod, execa as _execa } from 'execa'; import { printer } from '../printer.js'; +import { ExecaOptions } from '@aws-amplify/plugin-types'; /** * Abstracts the execution of a command and pipes outputs/errors to `Printer.debug` @@ -10,8 +11,8 @@ export const executeWithDebugLogger = ( executable: string, args?: Readonly, execa = _execa, - options?: Options<'utf8'> -) => { + options?: ExecaOptions +): ReturnType => { try { const childProcess = execa(executable, args, { stdin: 'inherit', diff --git a/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts b/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts index 4d9786b3910..642c8a54bf8 100644 --- a/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts +++ b/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts @@ -1,8 +1,12 @@ import { existsSync as _existsSync } from 'fs'; import _fsp from 'fs/promises'; -import { type ExecaChildProcess, type Options, execa as _execa } from 'execa'; +import { execa as _execa } from 'execa'; import * as _path from 'path'; -import { type PackageManagerController } from '@aws-amplify/plugin-types'; +import { + ExecaChildProcess, + ExecaOptions, + type PackageManagerController, +} from '@aws-amplify/plugin-types'; import { LogLevel } from '../printer/printer.js'; import { printer } from '../printer.js'; import { executeWithDebugLogger as _executeWithDebugLogger } from './execute_with_debugger_logger.js'; @@ -121,7 +125,7 @@ export abstract class PackageManagerControllerBase runWithPackageManager( args: string[] = [], dir: string, - options?: Options<'utf8'> + options?: ExecaOptions ): ExecaChildProcess { return this.executeWithDebugLogger( dir, diff --git a/packages/cli/package.json b/packages/cli/package.json index fb5fc6913a1..0fbbb503d2f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -52,7 +52,7 @@ "@smithy/node-config-provider": "^2.1.3", "@smithy/shared-ini-file-loader": "^2.2.5", "envinfo": "^7.11.0", - "execa": "^8.0.1", + "execa": "^9.5.1", "is-ci": "^3.0.1", "open": "^9.1.0", "yargs": "^17.7.2", diff --git a/packages/create-amplify/package.json b/packages/create-amplify/package.json index 7cbff22777e..c1e461823a3 100644 --- a/packages/create-amplify/package.json +++ b/packages/create-amplify/package.json @@ -20,7 +20,7 @@ "@aws-amplify/cli-core": "^1.1.3", "@aws-amplify/platform-core": "^1.0.3", "@aws-amplify/plugin-types": "^1.2.2", - "execa": "^8.0.1", + "execa": "^9.5.1", "kleur": "^4.1.5", "yargs": "^17.7.2" } diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index a50b9bd1bf4..aa9d14fca14 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -35,7 +35,7 @@ "aws-appsync-auth-link": "^3.0.7", "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0", - "execa": "^8.0.1", + "execa": "^9.5.1", "fs-extra": "^11.1.1", "glob": "^10.2.7", "graphql-tag": "^2.12.6", diff --git a/packages/integration-tests/src/process-controller/execa_process_killer.ts b/packages/integration-tests/src/process-controller/execa_process_killer.ts index 945b3ba38dd..90c9e9d5898 100644 --- a/packages/integration-tests/src/process-controller/execa_process_killer.ts +++ b/packages/integration-tests/src/process-controller/execa_process_killer.ts @@ -1,10 +1,12 @@ -import { ExecaChildProcess, execa } from 'execa'; +import { ExecaMethod, execa } from 'execa'; /** * Kills the given process (equivalent of sending CTRL-C) * @param processInstance an instance of execa child process */ -export const killExecaProcess = async (processInstance: ExecaChildProcess) => { +export const killExecaProcess = async ( + processInstance: ReturnType +) => { if (process.platform.startsWith('win')) { if (typeof processInstance.pid !== 'number') { throw new Error('Cannot kill the process that does not have pid'); diff --git a/packages/integration-tests/src/process-controller/predicated_action.ts b/packages/integration-tests/src/process-controller/predicated_action.ts index c63316970cf..82fe61f73b0 100644 --- a/packages/integration-tests/src/process-controller/predicated_action.ts +++ b/packages/integration-tests/src/process-controller/predicated_action.ts @@ -1,4 +1,4 @@ -import { ExecaChildProcess } from 'execa'; +import { ExecaMethod } from 'execa'; /** * Type of actions a user can take with their app. @@ -12,11 +12,11 @@ export enum ActionType { type SendInputToProcessAction = { actionType: ActionType.SEND_INPUT_TO_PROCESS; - action: (execaProcess: ExecaChildProcess) => Promise; + action: (execaProcess: ReturnType) => Promise; }; type KillProcess = { actionType: ActionType.KILL_PROCESS; - action: (execaProcess: ExecaChildProcess) => Promise; + action: (execaProcess: ReturnType) => Promise; }; type UpdateFileContentAction = { actionType: ActionType.UPDATE_FILE_CONTENT; diff --git a/packages/integration-tests/src/process-controller/predicated_action_queue_builder.ts b/packages/integration-tests/src/process-controller/predicated_action_queue_builder.ts index 0b5506db6a7..6b0e3572ab5 100644 --- a/packages/integration-tests/src/process-controller/predicated_action_queue_builder.ts +++ b/packages/integration-tests/src/process-controller/predicated_action_queue_builder.ts @@ -7,7 +7,7 @@ import os from 'os'; import fs from 'fs/promises'; import { killExecaProcess } from './execa_process_killer.js'; -import { ExecaChildProcess } from 'execa'; +import { ExecaMethod } from 'execa'; import { CopyDefinition } from './types.js'; export const CONTROL_C = '\x03'; @@ -54,7 +54,7 @@ export class PredicatedActionBuilder { str === CONTROL_C ? ActionType.KILL_PROCESS : ActionType.SEND_INPUT_TO_PROCESS, - action: async (execaProcess: ExecaChildProcess) => { + action: async (execaProcess: ReturnType) => { if (str === CONTROL_C) { await killExecaProcess(execaProcess); } else { diff --git a/packages/integration-tests/src/process-controller/process_controller.ts b/packages/integration-tests/src/process-controller/process_controller.ts index 6df33bf7669..f98d628a806 100644 --- a/packages/integration-tests/src/process-controller/process_controller.ts +++ b/packages/integration-tests/src/process-controller/process_controller.ts @@ -58,11 +58,11 @@ export class ProcessController { } if (process.stdout) { - void execaProcess.pipeStdout?.(process.stdout); + execaProcess.stdout.pipe(process.stdout); } if (process.stderr) { - void execaProcess.pipeStderr?.(process.stderr); + execaProcess.stderr.pipe(process.stderr); } if (!execaProcess.stdout) { diff --git a/packages/plugin-types/API.md b/packages/plugin-types/API.md index 832ba6c782d..ab33f98e346 100644 --- a/packages/plugin-types/API.md +++ b/packages/plugin-types/API.md @@ -4,6 +4,8 @@ ```ts +/// + import { CfnFunction } from 'aws-cdk-lib/aws-lambda'; import { CfnIdentityPool } from 'aws-cdk-lib/aws-cognito'; import { CfnIdentityPoolRoleAttachment } from 'aws-cdk-lib/aws-cognito'; @@ -12,14 +14,13 @@ import { CfnUserPoolClient } from 'aws-cdk-lib/aws-cognito'; import { CfnUserPoolGroup } from 'aws-cdk-lib/aws-cognito'; import { Client } from '@aws-sdk/types'; import { Construct } from 'constructs'; -import { ExecaChildProcess } from 'execa'; import { IFunction } from 'aws-cdk-lib/aws-lambda'; import { IRole } from 'aws-cdk-lib/aws-iam'; import { IUserPool } from 'aws-cdk-lib/aws-cognito'; import { IUserPoolClient } from 'aws-cdk-lib/aws-cognito'; import { MetadataBearer } from '@aws-sdk/types'; -import { Options } from 'execa'; import { Policy } from 'aws-cdk-lib/aws-iam'; +import { Readable } from 'node:stream'; import { SecretValue } from 'aws-cdk-lib'; import { Stack } from 'aws-cdk-lib'; @@ -154,6 +155,26 @@ export type DeepPartialAmplifyGeneratedConfigs = { // @public export type DeploymentType = 'branch' | 'sandbox'; +// @public (undocumented) +export type ExecaChildProcess = { + stdout: Readable | null; + stderr: Readable | null; +} & Promise; + +// @public (undocumented) +export type ExecaChildProcessResult = { + exitCode?: number | undefined; +}; + +// @public (undocumented) +export type ExecaOptions = { + stdin?: 'inherit'; + stdout?: 'pipe'; + stderr?: 'pipe'; + extendEnv?: boolean; + env?: Record; +}; + // @public (undocumented) export type FunctionResources = { lambda: IFunction; @@ -196,7 +217,7 @@ export type PackageManagerController = { initializeProject: () => Promise; initializeTsConfig: (targetDir: string) => Promise; installDependencies: (packageNames: string[], type: 'dev' | 'prod') => Promise; - runWithPackageManager: (args: string[] | undefined, dir: string, options?: Options<'utf8'>) => ExecaChildProcess; + runWithPackageManager: (args: string[] | undefined, dir: string, options?: ExecaOptions) => ExecaChildProcess; getCommand: (args: string[]) => string; allowsSignalPropagation: () => boolean; }; diff --git a/packages/plugin-types/package.json b/packages/plugin-types/package.json index b5aa9f69956..34ecb44a736 100644 --- a/packages/plugin-types/package.json +++ b/packages/plugin-types/package.json @@ -17,8 +17,5 @@ }, "imports": { "#package.json": "./package.json" - }, - "devDependencies": { - "execa": "^5.1.1" } } diff --git a/packages/plugin-types/src/package_manager_controller.ts b/packages/plugin-types/src/package_manager_controller.ts index 67189f185fb..8e53789641e 100644 --- a/packages/plugin-types/src/package_manager_controller.ts +++ b/packages/plugin-types/src/package_manager_controller.ts @@ -1,10 +1,27 @@ -/** - * TODO: use the latest execa. - * Issue: https://github.com/aws-amplify/amplify-backend/issues/962 - * execa v8 doesn't support commonjs, so we need to use the types from v5 +import { Readable } from 'node:stream'; + +/* + * Execa v6 and onwards doesn't support commonjs, so we need to define our own types + * to match execa functionalities we use. * https://github.com/sindresorhus/execa/issues/489#issuecomment-1109983390 */ -import { type ExecaChildProcess, type Options } from 'execa'; + +export type ExecaOptions = { + stdin?: 'inherit'; + stdout?: 'pipe'; + stderr?: 'pipe'; + extendEnv?: boolean; + env?: Record; +}; + +export type ExecaChildProcessResult = { + exitCode?: number | undefined; +}; + +export type ExecaChildProcess = { + stdout: Readable | null; + stderr: Readable | null; +} & Promise; export type PackageManagerController = { initializeProject: () => Promise; @@ -16,7 +33,7 @@ export type PackageManagerController = { runWithPackageManager: ( args: string[] | undefined, dir: string, - options?: Options<'utf8'> + options?: ExecaOptions ) => ExecaChildProcess; getCommand: (args: string[]) => string; allowsSignalPropagation: () => boolean; diff --git a/scripts/check_dependencies.ts b/scripts/check_dependencies.ts index 80d26889d4b..52828f4419f 100644 --- a/scripts/check_dependencies.ts +++ b/scripts/check_dependencies.ts @@ -22,19 +22,5 @@ await new DependenciesValidator( }, }, [['aws-cdk', 'aws-cdk-lib']], - [ - { - // @aws-amplify/plugin-types can depend on execa@^5.1.1 as a workaround for https://github.com/aws-amplify/amplify-backend/issues/962 - // all other packages must depend on execa@^8.0.1 - // this can be removed once execa is patched - dependencyName: 'execa', - globalDependencyVersion: '^8.0.1', - exceptions: [ - { - packageName: '@aws-amplify/plugin-types', - dependencyVersion: '^5.1.1', - }, - ], - }, - ] + [] ).validate(); From 1d035b82ad68333c50fdfec506e2e0d8513f312e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 16:03:03 +0000 Subject: [PATCH 149/199] Version Packages (#2272) Co-authored-by: github-actions[bot] --- .changeset/brave-cheetahs-repeat.md | 7 ---- .changeset/brave-walls-thank.md | 5 --- .changeset/curly-trainers-look.md | 5 --- .changeset/fair-ghosts-wave.md | 5 --- .changeset/five-comics-beg.md | 5 --- .changeset/flat-donkeys-wonder.md | 5 --- .changeset/forty-bulldogs-end.md | 8 ----- .changeset/fresh-carrots-sleep.md | 2 -- .changeset/fuzzy-squids-raise.md | 5 --- .changeset/new-mails-speak.md | 5 --- .changeset/new-rings-suffer.md | 20 ------------ .changeset/olive-singers-tease.md | 5 --- .changeset/perfect-grapes-lay.md | 2 -- .changeset/poor-moons-refuse.md | 9 ------ .changeset/poor-phones-attend.md | 5 --- .changeset/purple-news-do.md | 5 --- .changeset/purple-otters-poke.md | 5 --- .changeset/rude-moles-attack.md | 5 --- .changeset/shaggy-pens-sort.md | 6 ---- .changeset/shy-ladybugs-buy.md | 5 --- .changeset/shy-lions-smash.md | 5 --- .changeset/spicy-rules-speak.md | 2 -- .changeset/stale-pillows-run.md | 10 ------ .changeset/twenty-baboons-rule.md | 2 -- .changeset/weak-nails-change.md | 5 --- packages/ai-constructs/CHANGELOG.md | 16 ++++++++++ packages/ai-constructs/package.json | 8 ++--- packages/auth-construct/CHANGELOG.md | 10 ++++++ packages/auth-construct/package.json | 6 ++-- packages/backend-ai/CHANGELOG.md | 18 +++++++++++ packages/backend-ai/package.json | 10 +++--- packages/backend-auth/CHANGELOG.md | 11 +++++++ packages/backend-auth/package.json | 12 +++---- packages/backend-data/CHANGELOG.md | 11 +++++++ packages/backend-data/package.json | 10 +++--- packages/backend-deployer/CHANGELOG.md | 21 ++++++++++++ packages/backend-deployer/package.json | 6 ++-- packages/backend-function/CHANGELOG.md | 22 +++++++++++++ packages/backend-function/package.json | 10 +++--- packages/backend-output-storage/CHANGELOG.md | 12 +++++++ packages/backend-output-storage/package.json | 6 ++-- .../backend-platform-test-stubs/CHANGELOG.md | 9 ++++++ .../backend-platform-test-stubs/package.json | 4 +-- packages/backend-storage/CHANGELOG.md | 10 ++++++ packages/backend-storage/package.json | 10 +++--- packages/backend/CHANGELOG.md | 32 +++++++++++++++++++ packages/backend/package.json | 18 +++++------ packages/cli-core/CHANGELOG.md | 10 ++++++ packages/cli-core/package.json | 4 +-- packages/cli/CHANGELOG.md | 30 +++++++++++++++++ packages/cli/package.json | 16 +++++----- packages/client-config/CHANGELOG.md | 12 +++++++ packages/client-config/package.json | 6 ++-- packages/create-amplify/CHANGELOG.md | 14 ++++++++ packages/create-amplify/package.json | 8 ++--- packages/integration-tests/CHANGELOG.md | 6 ++++ packages/integration-tests/package.json | 16 +++++----- packages/platform-core/CHANGELOG.md | 13 ++++++++ packages/platform-core/package.json | 4 +-- packages/plugin-types/CHANGELOG.md | 10 ++++++ packages/plugin-types/package.json | 2 +- packages/sandbox/CHANGELOG.md | 24 ++++++++++++++ packages/sandbox/package.json | 12 +++---- packages/schema-generator/CHANGELOG.md | 9 ++++++ packages/schema-generator/package.json | 4 +-- 65 files changed, 386 insertions(+), 229 deletions(-) delete mode 100644 .changeset/brave-cheetahs-repeat.md delete mode 100644 .changeset/brave-walls-thank.md delete mode 100644 .changeset/curly-trainers-look.md delete mode 100644 .changeset/fair-ghosts-wave.md delete mode 100644 .changeset/five-comics-beg.md delete mode 100644 .changeset/flat-donkeys-wonder.md delete mode 100644 .changeset/forty-bulldogs-end.md delete mode 100644 .changeset/fresh-carrots-sleep.md delete mode 100644 .changeset/fuzzy-squids-raise.md delete mode 100644 .changeset/new-mails-speak.md delete mode 100644 .changeset/new-rings-suffer.md delete mode 100644 .changeset/olive-singers-tease.md delete mode 100644 .changeset/perfect-grapes-lay.md delete mode 100644 .changeset/poor-moons-refuse.md delete mode 100644 .changeset/poor-phones-attend.md delete mode 100644 .changeset/purple-news-do.md delete mode 100644 .changeset/purple-otters-poke.md delete mode 100644 .changeset/rude-moles-attack.md delete mode 100644 .changeset/shaggy-pens-sort.md delete mode 100644 .changeset/shy-ladybugs-buy.md delete mode 100644 .changeset/shy-lions-smash.md delete mode 100644 .changeset/spicy-rules-speak.md delete mode 100644 .changeset/stale-pillows-run.md delete mode 100644 .changeset/twenty-baboons-rule.md delete mode 100644 .changeset/weak-nails-change.md diff --git a/.changeset/brave-cheetahs-repeat.md b/.changeset/brave-cheetahs-repeat.md deleted file mode 100644 index d74a469e25a..00000000000 --- a/.changeset/brave-cheetahs-repeat.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@aws-amplify/backend': minor -'@aws-amplify/backend-function': minor -'@aws-amplify/backend-data': patch ---- - -Add lambda data client diff --git a/.changeset/brave-walls-thank.md b/.changeset/brave-walls-thank.md deleted file mode 100644 index 34fbdd22a21..00000000000 --- a/.changeset/brave-walls-thank.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -add error mapping for lambda bundling into an empty zip diff --git a/.changeset/curly-trainers-look.md b/.changeset/curly-trainers-look.md deleted file mode 100644 index eb591227ab0..00000000000 --- a/.changeset/curly-trainers-look.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -Narrow the error parsing for ExpiredToken regex diff --git a/.changeset/fair-ghosts-wave.md b/.changeset/fair-ghosts-wave.md deleted file mode 100644 index 41ace980f0a..00000000000 --- a/.changeset/fair-ghosts-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -handle cdk error mapping for more generic invalid credentials diff --git a/.changeset/five-comics-beg.md b/.changeset/five-comics-beg.md deleted file mode 100644 index 10a4c35dd0e..00000000000 --- a/.changeset/five-comics-beg.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/platform-core': patch ---- - -return amplify user error as it is from `AmplifyError.fromError` diff --git a/.changeset/flat-donkeys-wonder.md b/.changeset/flat-donkeys-wonder.md deleted file mode 100644 index 9b04f107e4b..00000000000 --- a/.changeset/flat-donkeys-wonder.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-cli': patch ---- - -validate branch and app id inputs for pipeline-deploy command diff --git a/.changeset/forty-bulldogs-end.md b/.changeset/forty-bulldogs-end.md deleted file mode 100644 index 99aa5a81e2f..00000000000 --- a/.changeset/forty-bulldogs-end.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@aws-amplify/backend-function': minor -'@aws-amplify/backend': minor ---- - -Add support to `@aws-amplify/backend-function` for Node 22 - -Add support to `@aws-amplify/backend-function` for Node 22, which is a [supported Lambda runtime](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtime-deprecation-levels) that was added in [`aws-cdk-lib/aws-lambda` version `2.168.0`](https://github.com/aws/aws-cdk/releases/tag/v2.168.0) on November 20th, 2024 diff --git a/.changeset/fresh-carrots-sleep.md b/.changeset/fresh-carrots-sleep.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/fresh-carrots-sleep.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/fuzzy-squids-raise.md b/.changeset/fuzzy-squids-raise.md deleted file mode 100644 index 1d809423be8..00000000000 --- a/.changeset/fuzzy-squids-raise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -added mapping for additional EPERM errors diff --git a/.changeset/new-mails-speak.md b/.changeset/new-mails-speak.md deleted file mode 100644 index 1ef0aa5de91..00000000000 --- a/.changeset/new-mails-speak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -add error mapping for lambda exceeding max size diff --git a/.changeset/new-rings-suffer.md b/.changeset/new-rings-suffer.md deleted file mode 100644 index f3df724642e..00000000000 --- a/.changeset/new-rings-suffer.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -'@aws-amplify/backend-platform-test-stubs': patch -'@aws-amplify/backend-output-storage': patch -'@aws-amplify/integration-tests': patch -'@aws-amplify/backend-deployer': patch -'@aws-amplify/backend-function': patch -'@aws-amplify/schema-generator': patch -'@aws-amplify/backend-storage': patch -'@aws-amplify/auth-construct': patch -'@aws-amplify/ai-constructs': patch -'@aws-amplify/client-config': patch -'@aws-amplify/backend-auth': patch -'@aws-amplify/backend-data': patch -'@aws-amplify/plugin-types': patch -'@aws-amplify/backend-ai': patch -'@aws-amplify/backend': patch -'@aws-amplify/sandbox': patch ---- - -update aws-cdk lib to ^2.168.0 diff --git a/.changeset/olive-singers-tease.md b/.changeset/olive-singers-tease.md deleted file mode 100644 index 11c13c01de2..00000000000 --- a/.changeset/olive-singers-tease.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-cli': patch ---- - -update logic for falling back to sandbox resolver for backend id diff --git a/.changeset/perfect-grapes-lay.md b/.changeset/perfect-grapes-lay.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/perfect-grapes-lay.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/poor-moons-refuse.md b/.changeset/poor-moons-refuse.md deleted file mode 100644 index 4da456357c7..00000000000 --- a/.changeset/poor-moons-refuse.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@aws-amplify/ai-constructs': minor -'@aws-amplify/backend-ai': minor -'@aws-amplify/backend-function': minor -'@aws-amplify/backend': minor -'@aws-amplify/platform-core': minor ---- - -Add options to control log settings diff --git a/.changeset/poor-phones-attend.md b/.changeset/poor-phones-attend.md deleted file mode 100644 index b3dbdabe6ad..00000000000 --- a/.changeset/poor-phones-attend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -extract generic cdk asset publish failures diff --git a/.changeset/purple-news-do.md b/.changeset/purple-news-do.md deleted file mode 100644 index 4c64d168bb2..00000000000 --- a/.changeset/purple-news-do.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -Catches most common EPERM error and update to resolution message for stack in failed state diff --git a/.changeset/purple-otters-poke.md b/.changeset/purple-otters-poke.md deleted file mode 100644 index 2c4c4f6bd57..00000000000 --- a/.changeset/purple-otters-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend': patch ---- - -Backend Secrets now use a single custom resource to reduce concurrent lambda executions. diff --git a/.changeset/rude-moles-attack.md b/.changeset/rude-moles-attack.md deleted file mode 100644 index 695e49e9cb0..00000000000 --- a/.changeset/rude-moles-attack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -add cdk error mapping for error "cdk command not found" diff --git a/.changeset/shaggy-pens-sort.md b/.changeset/shaggy-pens-sort.md deleted file mode 100644 index b36496d9a68..00000000000 --- a/.changeset/shaggy-pens-sort.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/cli-core': patch -'@aws-amplify/backend-cli': patch ---- - -add a required input prompt for use in region input diff --git a/.changeset/shy-ladybugs-buy.md b/.changeset/shy-ladybugs-buy.md deleted file mode 100644 index 50cc8f701e9..00000000000 --- a/.changeset/shy-ladybugs-buy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-cli': patch ---- - -validate input stack name for generate commands diff --git a/.changeset/shy-lions-smash.md b/.changeset/shy-lions-smash.md deleted file mode 100644 index a30b19bca40..00000000000 --- a/.changeset/shy-lions-smash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-function': patch ---- - -change errors in FunctionFactory to AmplifyUserError diff --git a/.changeset/spicy-rules-speak.md b/.changeset/spicy-rules-speak.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/spicy-rules-speak.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/stale-pillows-run.md b/.changeset/stale-pillows-run.md deleted file mode 100644 index 57eec7a7f5d..00000000000 --- a/.changeset/stale-pillows-run.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch -'@aws-amplify/backend-function': patch -'create-amplify': patch -'@aws-amplify/plugin-types': minor -'@aws-amplify/cli-core': patch -'@aws-amplify/backend-cli': patch ---- - -Upgrade execa diff --git a/.changeset/twenty-baboons-rule.md b/.changeset/twenty-baboons-rule.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/twenty-baboons-rule.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/weak-nails-change.md b/.changeset/weak-nails-change.md deleted file mode 100644 index 72f2675d112..00000000000 --- a/.changeset/weak-nails-change.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-function': patch ---- - -add validation for environment prop diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index 757855305c4..bddc654159b 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,21 @@ # @aws-amplify/ai-constructs +## 1.1.0 + +### Minor Changes + +- 65abf6a: Add options to control log settings + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- Updated dependencies [cfdc854] +- Updated dependencies [72b2fe0] +- Updated dependencies [65abf6a] +- Updated dependencies [f6ba240] + - @aws-amplify/platform-core@1.3.0 + - @aws-amplify/plugin-types@1.6.0 + ## 1.0.0 ### Major Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index 584d3d361c2..93f1966ed21 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "1.0.0", + "version": "1.1.0", "type": "commonjs", "publishConfig": { "access": "public" @@ -27,14 +27,14 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-bedrock-runtime": "^3.622.0", "@smithy/types": "^3.3.0", "json-schema-to-ts": "^3.1.1" }, "devDependencies": { - "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/backend-output-storage": "^1.1.4", "typescript": "^5.0.0" }, "peerDependencies": { diff --git a/packages/auth-construct/CHANGELOG.md b/packages/auth-construct/CHANGELOG.md index d4205498d77..57296dcb7aa 100644 --- a/packages/auth-construct/CHANGELOG.md +++ b/packages/auth-construct/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/auth-construct +## 1.5.1 + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- Updated dependencies [72b2fe0] +- Updated dependencies [f6ba240] + - @aws-amplify/backend-output-storage@1.1.4 + - @aws-amplify/plugin-types@1.6.0 + ## 1.5.0 ### Minor Changes diff --git a/packages/auth-construct/package.json b/packages/auth-construct/package.json index cb70f430740..340a0311956 100644 --- a/packages/auth-construct/package.json +++ b/packages/auth-construct/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/auth-construct", - "version": "1.5.0", + "version": "1.5.1", "type": "commonjs", "publishConfig": { "access": "public" @@ -20,8 +20,8 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/backend-output-storage": "^1.1.4", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/util-arn-parser": "^3.568.0" }, "peerDependencies": { diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index 86f57f0bfbe..9e5bac6381b 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,23 @@ # @aws-amplify/backend-ai +## 1.1.0 + +### Minor Changes + +- 65abf6a: Add options to control log settings + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- Updated dependencies [cfdc854] +- Updated dependencies [72b2fe0] +- Updated dependencies [65abf6a] +- Updated dependencies [f6ba240] + - @aws-amplify/platform-core@1.3.0 + - @aws-amplify/backend-output-storage@1.1.4 + - @aws-amplify/ai-constructs@1.1.0 + - @aws-amplify/plugin-types@1.6.0 + ## 1.0.1 ### Patch Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 1cf30fb297e..40507169dc1 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "1.0.1", + "version": "1.1.0", "type": "module", "publishConfig": { "access": "public" @@ -22,12 +22,12 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^1.0.0", + "@aws-amplify/ai-constructs": "^1.1.0", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/data-schema-types": "^1.2.0", - "@aws-amplify/platform-core": "^1.2.1", - "@aws-amplify/plugin-types": "^1.5.0" + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", diff --git a/packages/backend-auth/CHANGELOG.md b/packages/backend-auth/CHANGELOG.md index 712a426ff33..1566c93dfc1 100644 --- a/packages/backend-auth/CHANGELOG.md +++ b/packages/backend-auth/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend-auth +## 1.4.2 + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- Updated dependencies [72b2fe0] +- Updated dependencies [f6ba240] + - @aws-amplify/backend-output-storage@1.1.4 + - @aws-amplify/auth-construct@1.5.1 + - @aws-amplify/plugin-types@1.6.0 + ## 1.4.1 ### Patch Changes diff --git a/packages/backend-auth/package.json b/packages/backend-auth/package.json index 054d782dc6c..5976e3d6688 100644 --- a/packages/backend-auth/package.json +++ b/packages/backend-auth/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-auth", - "version": "1.4.1", + "version": "1.4.2", "type": "module", "publishConfig": { "access": "public" @@ -19,14 +19,14 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/auth-construct": "^1.5.0", + "@aws-amplify/auth-construct": "^1.5.1", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.5.0" + "@aws-amplify/backend-output-storage": "^1.1.4", + "@aws-amplify/plugin-types": "^1.6.0" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.2.1", + "@aws-amplify/backend-platform-test-stubs": "^0.3.7", + "@aws-amplify/platform-core": "^1.3.0", "@aws-sdk/client-cognito-identity-provider": "^3.624.0", "@aws-sdk/client-cognito-identity": "^3.624.0", "@types/aws-lambda": "^8.10.119", diff --git a/packages/backend-data/CHANGELOG.md b/packages/backend-data/CHANGELOG.md index 2e415bac267..cec24087071 100644 --- a/packages/backend-data/CHANGELOG.md +++ b/packages/backend-data/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend-data +## 1.2.2 + +### Patch Changes + +- 5cbe318: Add lambda data client +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- Updated dependencies [72b2fe0] +- Updated dependencies [f6ba240] + - @aws-amplify/backend-output-storage@1.1.4 + - @aws-amplify/plugin-types@1.6.0 + ## 1.2.1 ### Patch Changes diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index 9afce361886..1fd1c2d5877 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-data", - "version": "1.2.1", + "version": "1.2.2", "type": "module", "publishConfig": { "access": "public" @@ -20,19 +20,19 @@ "license": "Apache-2.0", "devDependencies": { "@aws-amplify/data-schema": "^1.13.4", - "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.2.1" + "@aws-amplify/backend-platform-test-stubs": "^0.3.7", + "@aws-amplify/platform-core": "^1.3.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" }, "dependencies": { - "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/data-construct": "^1.10.1", "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/graphql-generator": "^0.5.1", - "@aws-amplify/plugin-types": "^1.4.0" + "@aws-amplify/plugin-types": "^1.6.0" } } diff --git a/packages/backend-deployer/CHANGELOG.md b/packages/backend-deployer/CHANGELOG.md index cdf90c774d2..69edc3b2a80 100644 --- a/packages/backend-deployer/CHANGELOG.md +++ b/packages/backend-deployer/CHANGELOG.md @@ -1,5 +1,26 @@ # @aws-amplify/backend-deployer +## 1.1.11 + +### Patch Changes + +- 1593ce8: add error mapping for lambda bundling into an empty zip +- a406263: Narrow the error parsing for ExpiredToken regex +- 37d8564: handle cdk error mapping for more generic invalid credentials +- d66ab17: added mapping for additional EPERM errors +- 5a47d21: add error mapping for lambda exceeding max size +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- 0a360fb: extract generic cdk asset publish failures +- 6015595: Catches most common EPERM error and update to resolution message for stack in failed state +- daaedb6: add cdk error mapping for error "cdk command not found" +- f6ba240: Upgrade execa +- Updated dependencies [cfdc854] +- Updated dependencies [72b2fe0] +- Updated dependencies [65abf6a] +- Updated dependencies [f6ba240] + - @aws-amplify/platform-core@1.3.0 + - @aws-amplify/plugin-types@1.6.0 + ## 1.1.10 ### Patch Changes diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index b8bf0a88b5c..57b868e301c 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-deployer", - "version": "1.1.10", + "version": "1.1.11", "type": "module", "publishConfig": { "access": "public" @@ -19,8 +19,8 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.2.2", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "execa": "^9.5.1", "tsx": "^4.6.1", "strip-ansi": "^6.0.1" diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index 12240777bd9..2d9ba784917 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,27 @@ # @aws-amplify/backend-function +## 1.9.0 + +### Minor Changes + +- 5cbe318: Add lambda data client +- 72b2fe0: Add support to `@aws-amplify/backend-function` for Node 22 + + Add support to `@aws-amplify/backend-function` for Node 22, which is a [supported Lambda runtime](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtime-deprecation-levels) that was added in [`aws-cdk-lib/aws-lambda` version `2.168.0`](https://github.com/aws/aws-cdk/releases/tag/v2.168.0) on November 20th, 2024 + +- 65abf6a: Add options to control log settings + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- d227f96: change errors in FunctionFactory to AmplifyUserError +- f6ba240: Upgrade execa +- d227f96: add validation for environment prop +- Updated dependencies [72b2fe0] +- Updated dependencies [f6ba240] + - @aws-amplify/backend-output-storage@1.1.4 + - @aws-amplify/plugin-types@1.6.0 + ## 1.8.0 ### Minor Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index eec914ab835..4275be7f47a 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.8.0", + "version": "1.9.0", "type": "module", "publishConfig": { "access": "public" @@ -25,14 +25,14 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.5.0", + "@aws-amplify/backend-output-storage": "^1.1.4", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-s3": "^3.624.0", "execa": "^9.5.1" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.2.1", + "@aws-amplify/backend-platform-test-stubs": "^0.3.7", + "@aws-amplify/platform-core": "^1.3.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", diff --git a/packages/backend-output-storage/CHANGELOG.md b/packages/backend-output-storage/CHANGELOG.md index 89eac4993f0..d0990132cfd 100644 --- a/packages/backend-output-storage/CHANGELOG.md +++ b/packages/backend-output-storage/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/backend-output-storage +## 1.1.4 + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- Updated dependencies [cfdc854] +- Updated dependencies [72b2fe0] +- Updated dependencies [65abf6a] +- Updated dependencies [f6ba240] + - @aws-amplify/platform-core@1.3.0 + - @aws-amplify/plugin-types@1.6.0 + ## 1.1.3 ### Patch Changes diff --git a/packages/backend-output-storage/package.json b/packages/backend-output-storage/package.json index 0269f794b62..e7489845c9f 100644 --- a/packages/backend-output-storage/package.json +++ b/packages/backend-output-storage/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-output-storage", - "version": "1.1.3", + "version": "1.1.4", "type": "commonjs", "publishConfig": { "access": "public" @@ -20,8 +20,8 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.3.1" + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0" diff --git a/packages/backend-platform-test-stubs/CHANGELOG.md b/packages/backend-platform-test-stubs/CHANGELOG.md index 436f8411ba5..792c3c28ca9 100644 --- a/packages/backend-platform-test-stubs/CHANGELOG.md +++ b/packages/backend-platform-test-stubs/CHANGELOG.md @@ -1,5 +1,14 @@ # @aws-amplify/backend-platform-test-stubs +## 0.3.7 + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- Updated dependencies [72b2fe0] +- Updated dependencies [f6ba240] + - @aws-amplify/plugin-types@1.6.0 + ## 0.3.6 ### Patch Changes diff --git a/packages/backend-platform-test-stubs/package.json b/packages/backend-platform-test-stubs/package.json index 8d86327dc9a..b9eea93ef71 100644 --- a/packages/backend-platform-test-stubs/package.json +++ b/packages/backend-platform-test-stubs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-platform-test-stubs", - "version": "0.3.6", + "version": "0.3.7", "type": "module", "private": true, "exports": { @@ -16,7 +16,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.6.0", "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } diff --git a/packages/backend-storage/CHANGELOG.md b/packages/backend-storage/CHANGELOG.md index e1f09d6eb53..24e36df757f 100644 --- a/packages/backend-storage/CHANGELOG.md +++ b/packages/backend-storage/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/backend-storage +## 1.2.4 + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- Updated dependencies [72b2fe0] +- Updated dependencies [f6ba240] + - @aws-amplify/backend-output-storage@1.1.4 + - @aws-amplify/plugin-types@1.6.0 + ## 1.2.3 ### Patch Changes diff --git a/packages/backend-storage/package.json b/packages/backend-storage/package.json index 051a4c975d7..d824e9c4749 100644 --- a/packages/backend-storage/package.json +++ b/packages/backend-storage/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-storage", - "version": "1.2.3", + "version": "1.2.4", "type": "module", "publishConfig": { "access": "public" @@ -20,12 +20,12 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.1", - "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.5.0" + "@aws-amplify/backend-output-storage": "^1.1.4", + "@aws-amplify/plugin-types": "^1.6.0" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.2.1" + "@aws-amplify/backend-platform-test-stubs": "^0.3.7", + "@aws-amplify/platform-core": "^1.3.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 4add5492b28..5a57e825ed8 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,37 @@ # @aws-amplify/backend +## 1.9.0 + +### Minor Changes + +- 5cbe318: Add lambda data client +- 72b2fe0: Add support to `@aws-amplify/backend-function` for Node 22 + + Add support to `@aws-amplify/backend-function` for Node 22, which is a [supported Lambda runtime](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtime-deprecation-levels) that was added in [`aws-cdk-lib/aws-lambda` version `2.168.0`](https://github.com/aws/aws-cdk/releases/tag/v2.168.0) on November 20th, 2024 + +- 65abf6a: Add options to control log settings + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- e0e62bd: Backend Secrets now use a single custom resource to reduce concurrent lambda executions. +- Updated dependencies [5cbe318] +- Updated dependencies [cfdc854] +- Updated dependencies [72b2fe0] +- Updated dependencies [72b2fe0] +- Updated dependencies [65abf6a] +- Updated dependencies [d227f96] +- Updated dependencies [f6ba240] +- Updated dependencies [d227f96] + - @aws-amplify/backend-function@1.9.0 + - @aws-amplify/backend-data@1.2.2 + - @aws-amplify/platform-core@1.3.0 + - @aws-amplify/backend-output-storage@1.1.4 + - @aws-amplify/backend-storage@1.2.4 + - @aws-amplify/client-config@1.5.3 + - @aws-amplify/backend-auth@1.4.2 + - @aws-amplify/plugin-types@1.6.0 + ## 1.8.0 ### Minor Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 60261d7a984..7ef401f5aa2 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.8.0", + "version": "1.9.0", "type": "module", "publishConfig": { "access": "public" @@ -31,16 +31,16 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/data-schema": "^1.13.4", - "@aws-amplify/backend-auth": "^1.4.1", - "@aws-amplify/backend-function": "^1.8.0", - "@aws-amplify/backend-data": "^1.2.1", + "@aws-amplify/backend-auth": "^1.4.2", + "@aws-amplify/backend-function": "^1.9.0", + "@aws-amplify/backend-data": "^1.2.2", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/backend-secret": "^1.1.4", - "@aws-amplify/backend-storage": "^1.2.3", - "@aws-amplify/client-config": "^1.5.2", - "@aws-amplify/platform-core": "^1.2.1", - "@aws-amplify/plugin-types": "^1.5.0", + "@aws-amplify/backend-storage": "^1.2.4", + "@aws-amplify/client-config": "^1.5.3", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-amplify": "^3.624.0", "lodash.snakecase": "^4.1.1" }, diff --git a/packages/cli-core/CHANGELOG.md b/packages/cli-core/CHANGELOG.md index 290f6fb5c1a..9ced34d88d5 100644 --- a/packages/cli-core/CHANGELOG.md +++ b/packages/cli-core/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/cli-core +## 1.2.1 + +### Patch Changes + +- 0cf5c26: add a required input prompt for use in region input +- f6ba240: Upgrade execa +- Updated dependencies [cfdc854] +- Updated dependencies [65abf6a] + - @aws-amplify/platform-core@1.3.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/cli-core/package.json b/packages/cli-core/package.json index 801125ece4a..a735285a12f 100644 --- a/packages/cli-core/package.json +++ b/packages/cli-core/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/cli-core", - "version": "1.2.0", + "version": "1.2.1", "type": "module", "publishConfig": { "access": "public" @@ -19,7 +19,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.0.5", + "@aws-amplify/platform-core": "^1.3.0", "@inquirer/prompts": "^3.0.0", "execa": "^9.5.1", "kleur": "^4.1.5" diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 8c97e85b34f..54fa3beac60 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,35 @@ # @aws-amplify/backend-cli +## 1.4.3 + +### Patch Changes + +- 7c5abe2: validate branch and app id inputs for pipeline-deploy command +- 7c5abe2: update logic for falling back to sandbox resolver for backend id +- 0cf5c26: add a required input prompt for use in region input +- fc7e4d4: validate input stack name for generate commands +- f6ba240: Upgrade execa +- Updated dependencies [1593ce8] +- Updated dependencies [a406263] +- Updated dependencies [37d8564] +- Updated dependencies [cfdc854] +- Updated dependencies [d66ab17] +- Updated dependencies [5a47d21] +- Updated dependencies [72b2fe0] +- Updated dependencies [65abf6a] +- Updated dependencies [0a360fb] +- Updated dependencies [6015595] +- Updated dependencies [daaedb6] +- Updated dependencies [0cf5c26] +- Updated dependencies [f6ba240] + - @aws-amplify/backend-deployer@1.1.11 + - @aws-amplify/platform-core@1.3.0 + - @aws-amplify/schema-generator@1.2.6 + - @aws-amplify/client-config@1.5.3 + - @aws-amplify/plugin-types@1.6.0 + - @aws-amplify/sandbox@1.2.7 + - @aws-amplify/cli-core@1.2.1 + ## 1.4.2 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 0fbbb503d2f..1a0c6aa8b5b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-cli", - "version": "1.4.2", + "version": "1.4.3", "description": "Command line interface for various Amplify tools", "bin": { "ampx": "lib/ampx.js", @@ -31,18 +31,18 @@ }, "homepage": "https://github.com/aws-amplify/amplify-backend#readme", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.9", + "@aws-amplify/backend-deployer": "^1.1.11", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/cli-core": "^1.2.0", - "@aws-amplify/client-config": "^1.5.1", + "@aws-amplify/cli-core": "^1.2.1", + "@aws-amplify/client-config": "^1.5.3", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/form-generator": "^1.0.3", "@aws-amplify/model-generator": "^1.0.9", - "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.4.0", - "@aws-amplify/sandbox": "^1.2.5", - "@aws-amplify/schema-generator": "^1.2.5", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/sandbox": "^1.2.7", + "@aws-amplify/schema-generator": "^1.2.6", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", diff --git a/packages/client-config/CHANGELOG.md b/packages/client-config/CHANGELOG.md index 558d349fdbd..e04bbebf631 100644 --- a/packages/client-config/CHANGELOG.md +++ b/packages/client-config/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/client-config +## 1.5.3 + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- Updated dependencies [cfdc854] +- Updated dependencies [72b2fe0] +- Updated dependencies [65abf6a] +- Updated dependencies [f6ba240] + - @aws-amplify/platform-core@1.3.0 + - @aws-amplify/plugin-types@1.6.0 + ## 1.5.2 ### Patch Changes diff --git a/packages/client-config/package.json b/packages/client-config/package.json index 9310fa16178..4decaaf10ac 100644 --- a/packages/client-config/package.json +++ b/packages/client-config/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/client-config", - "version": "1.5.2", + "version": "1.5.3", "type": "module", "publishConfig": { "access": "public" @@ -27,8 +27,8 @@ "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/model-generator": "^1.0.7", - "@aws-amplify/platform-core": "^1.0.7", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "zod": "^3.22.2" }, "devDependencies": { diff --git a/packages/create-amplify/CHANGELOG.md b/packages/create-amplify/CHANGELOG.md index 7881bbcdbfb..89f428585f3 100644 --- a/packages/create-amplify/CHANGELOG.md +++ b/packages/create-amplify/CHANGELOG.md @@ -1,5 +1,19 @@ # create-amplify +## 1.0.7 + +### Patch Changes + +- f6ba240: Upgrade execa +- Updated dependencies [cfdc854] +- Updated dependencies [72b2fe0] +- Updated dependencies [65abf6a] +- Updated dependencies [0cf5c26] +- Updated dependencies [f6ba240] + - @aws-amplify/platform-core@1.3.0 + - @aws-amplify/plugin-types@1.6.0 + - @aws-amplify/cli-core@1.2.1 + ## 1.0.6 ### Patch Changes diff --git a/packages/create-amplify/package.json b/packages/create-amplify/package.json index c1e461823a3..2bfad8b81c8 100644 --- a/packages/create-amplify/package.json +++ b/packages/create-amplify/package.json @@ -1,6 +1,6 @@ { "name": "create-amplify", - "version": "1.0.6", + "version": "1.0.7", "type": "module", "main": "lib/index.js", "publishConfig": { @@ -17,9 +17,9 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/cli-core": "^1.1.3", - "@aws-amplify/platform-core": "^1.0.3", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/cli-core": "^1.2.1", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "execa": "^9.5.1", "kleur": "^4.1.5", "yargs": "^17.7.2" diff --git a/packages/integration-tests/CHANGELOG.md b/packages/integration-tests/CHANGELOG.md index e06cf49a9d7..e49fedd0c34 100644 --- a/packages/integration-tests/CHANGELOG.md +++ b/packages/integration-tests/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/integration-tests +## 0.6.1 + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 + ## 0.6.0 ### Minor Changes diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index aa9d14fca14..576363de098 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -1,20 +1,20 @@ { "name": "@aws-amplify/integration-tests", "private": true, - "version": "0.6.0", + "version": "0.6.1", "type": "module", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^1.0.0", - "@aws-amplify/auth-construct": "^1.4.0", - "@aws-amplify/backend": "^1.6.0", - "@aws-amplify/backend-ai": "^1.0.0", + "@aws-amplify/ai-constructs": "^1.1.0", + "@aws-amplify/auth-construct": "^1.5.1", + "@aws-amplify/backend": "^1.9.0", + "@aws-amplify/backend-ai": "^1.1.0", "@aws-amplify/backend-secret": "^1.1.4", - "@aws-amplify/client-config": "^1.5.1", + "@aws-amplify/client-config": "^1.5.3", "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-accessanalyzer": "^3.624.0", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-bedrock-runtime": "^3.622.0", diff --git a/packages/platform-core/CHANGELOG.md b/packages/platform-core/CHANGELOG.md index 44de4598fc3..5523757a5e7 100644 --- a/packages/platform-core/CHANGELOG.md +++ b/packages/platform-core/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/platform-core +## 1.3.0 + +### Minor Changes + +- 65abf6a: Add options to control log settings + +### Patch Changes + +- cfdc854: return amplify user error as it is from `AmplifyError.fromError` +- Updated dependencies [72b2fe0] +- Updated dependencies [f6ba240] + - @aws-amplify/plugin-types@1.6.0 + ## 1.2.2 ### Patch Changes diff --git a/packages/platform-core/package.json b/packages/platform-core/package.json index 131980b90a2..6ea68ea197a 100644 --- a/packages/platform-core/package.json +++ b/packages/platform-core/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/platform-core", - "version": "1.2.2", + "version": "1.3.0", "type": "commonjs", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "@types/uuid": "9.0.7" }, "dependencies": { - "@aws-amplify/plugin-types": "^1.5.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-sts": "^3.624.0", "is-ci": "^3.0.1", "lodash.mergewith": "^4.6.2", diff --git a/packages/plugin-types/CHANGELOG.md b/packages/plugin-types/CHANGELOG.md index 88512aac069..6affed723d7 100644 --- a/packages/plugin-types/CHANGELOG.md +++ b/packages/plugin-types/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/plugin-types +## 1.6.0 + +### Minor Changes + +- f6ba240: Upgrade execa + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 + ## 1.5.0 ### Minor Changes diff --git a/packages/plugin-types/package.json b/packages/plugin-types/package.json index 34ecb44a736..05f2958a8f0 100644 --- a/packages/plugin-types/package.json +++ b/packages/plugin-types/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/plugin-types", - "version": "1.5.0", + "version": "1.6.0", "types": "lib/index.d.ts", "type": "commonjs", "publishConfig": { diff --git a/packages/sandbox/CHANGELOG.md b/packages/sandbox/CHANGELOG.md index 42bdfd2209b..3d225cea5c3 100644 --- a/packages/sandbox/CHANGELOG.md +++ b/packages/sandbox/CHANGELOG.md @@ -1,5 +1,29 @@ # @aws-amplify/sandbox +## 1.2.7 + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- Updated dependencies [1593ce8] +- Updated dependencies [a406263] +- Updated dependencies [37d8564] +- Updated dependencies [cfdc854] +- Updated dependencies [d66ab17] +- Updated dependencies [5a47d21] +- Updated dependencies [72b2fe0] +- Updated dependencies [65abf6a] +- Updated dependencies [0a360fb] +- Updated dependencies [6015595] +- Updated dependencies [daaedb6] +- Updated dependencies [0cf5c26] +- Updated dependencies [f6ba240] + - @aws-amplify/backend-deployer@1.1.11 + - @aws-amplify/platform-core@1.3.0 + - @aws-amplify/client-config@1.5.3 + - @aws-amplify/plugin-types@1.6.0 + - @aws-amplify/cli-core@1.2.1 + ## 1.2.6 ### Patch Changes diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index fbb7da66ab9..6247d7ce2ca 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/sandbox", - "version": "1.2.6", + "version": "1.2.7", "type": "module", "publishConfig": { "access": "public" @@ -19,13 +19,13 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.8", + "@aws-amplify/backend-deployer": "^1.1.11", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/cli-core": "^1.2.0", - "@aws-amplify/client-config": "^1.5.1", + "@aws-amplify/cli-core": "^1.2.1", + "@aws-amplify/client-config": "^1.5.3", "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/platform-core": "^1.2.1", - "@aws-amplify/plugin-types": "^1.5.0", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", diff --git a/packages/schema-generator/CHANGELOG.md b/packages/schema-generator/CHANGELOG.md index 488a2cbb719..a8cefeccb36 100644 --- a/packages/schema-generator/CHANGELOG.md +++ b/packages/schema-generator/CHANGELOG.md @@ -1,5 +1,14 @@ # @aws-amplify/schema-generator +## 1.2.6 + +### Patch Changes + +- 72b2fe0: update aws-cdk lib to ^2.168.0 +- Updated dependencies [cfdc854] +- Updated dependencies [65abf6a] + - @aws-amplify/platform-core@1.3.0 + ## 1.2.5 ### Patch Changes diff --git a/packages/schema-generator/package.json b/packages/schema-generator/package.json index f860dbc5628..7600a7f32ad 100644 --- a/packages/schema-generator/package.json +++ b/packages/schema-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/schema-generator", - "version": "1.2.5", + "version": "1.2.6", "type": "module", "publishConfig": { "access": "public" @@ -19,7 +19,7 @@ }, "dependencies": { "@aws-amplify/graphql-schema-generator": "^0.11.0", - "@aws-amplify/platform-core": "^1.0.5" + "@aws-amplify/platform-core": "^1.3.0" }, "license": "Apache-2.0" } From 1a2521a26f87d46b772f27b9c4c8365b896e563b Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 9 Dec 2024 09:28:10 -0800 Subject: [PATCH 150/199] Disable telemetry in health checks (#2309) * disable telemetry in health checks * handle in tests * handle in tests --- .changeset/loud-dots-bathe.md | 2 ++ .github/workflows/health_checks.yml | 5 +++++ .../usage-data/usage_data_emitter_factory.test.ts | 15 ++++++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 .changeset/loud-dots-bathe.md diff --git a/.changeset/loud-dots-bathe.md b/.changeset/loud-dots-bathe.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/loud-dots-bathe.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/workflows/health_checks.yml b/.github/workflows/health_checks.yml index fef7c581b8d..d00ef3e5144 100644 --- a/.github/workflows/health_checks.yml +++ b/.github/workflows/health_checks.yml @@ -19,6 +19,11 @@ on: - cron: '0 0,6,12,18 * * *' workflow_dispatch: +env: + # Health checks can run on un-released code. Often work in progress. + # Disable data from there. + AMPLIFY_DISABLE_TELEMETRY: true + jobs: install: strategy: diff --git a/packages/platform-core/src/usage-data/usage_data_emitter_factory.test.ts b/packages/platform-core/src/usage-data/usage_data_emitter_factory.test.ts index a447ad3604e..7d7e0aaf84e 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter_factory.test.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter_factory.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { beforeEach, describe, it, mock } from 'node:test'; +import { after, before, beforeEach, describe, it, mock } from 'node:test'; import { UsageDataEmitterFactory } from './usage_data_emitter_factory'; import { DefaultUsageDataEmitter } from './usage_data_emitter'; @@ -21,6 +21,19 @@ void describe('UsageDataEmitterFactory', () => { () => mockedConfigController ); + const originalAmplifyDisableTelemetry = + process.env['AMPLIFY_DISABLE_TELEMETRY']; + + before(() => { + // Unset AMPLIFY_DISABLE_TELEMETRY. We may be setting this variable in GitHub workflows. + delete process.env['AMPLIFY_DISABLE_TELEMETRY']; + }); + + after(() => { + // Restore original value after tests. + process.env['AMPLIFY_DISABLE_TELEMETRY'] = originalAmplifyDisableTelemetry; + }); + beforeEach(() => { configControllerGet.mock.resetCalls(); }); From f409666be836a8ee1bde3bb184491d9b8330a28d Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 9 Dec 2024 10:34:22 -0800 Subject: [PATCH 151/199] Add debug section to contributing guide. (#2310) --- CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d76eea6cdf6..b7cffcc92a7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -146,6 +146,13 @@ At a minimum, each package needs: 6. An `.npmignore` file 7. A `README.md` file that gives a brief description of the intent of the package +## Debugging + +For debugging purposes you can use the following knobs: + +1. Most of `npx ampx` commands take `--debug` parameter. It enables verbose console output. +2. We are using `execa` for spawning child processes. You can set `export NODE_DEBUG=execa` to reveal exact command lines. + ## Licensing See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. From 383884985f0da2e849ca71769ee6ba59041d33aa Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 9 Dec 2024 11:33:01 -0800 Subject: [PATCH 152/199] Add console team as codeowners for packages used by them. (#2311) * Add console team as codeowners for packages used by them. * add comment --- .github/CODEOWNERS | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 066b0d8ef52..d8a32d72030 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,3 +8,15 @@ # GitHub actions/checks approval /.github/ @aws-amplify/amplify-backend-admins + +# Packages used by console team. +/packages/backend-secret @aws-amplify/amplify-backend @aws-amplify/amplify-studio-uibuilder +# API.md change always has related code change. Both teams are notified, but API approval by backend team is mandatory this way. +/packages/backend-secret/API.md @aws-amplify/amplify-backend-api-approvers +/packages/backend-secret/package.json @aws-amplify/amplify-backend-api-approvers @aws-amplify/amplify-studio-uibuilder +/packages/client-config @aws-amplify/amplify-backend @aws-amplify/amplify-studio-uibuilder +/packages/client-config/API.md @aws-amplify/amplify-backend-api-approvers +/packages/client-config/package.json @aws-amplify/amplify-backend-api-approvers @aws-amplify/amplify-studio-uibuilder +/packages/deployed-backend-client @aws-amplify/amplify-backend @aws-amplify/amplify-studio-uibuilder +/packages/deployed-backend-client/API.md @aws-amplify/amplify-backend-api-approvers +/packages/deployed-backend-client/package.json @aws-amplify/amplify-backend-api-approvers @aws-amplify/amplify-studio-uibuilder From a713a027ae84965bbb852c24c900a23a360bffc0 Mon Sep 17 00:00:00 2001 From: Pawan Mude Date: Tue, 10 Dec 2024 22:45:57 +0530 Subject: [PATCH 153/199] Update README.md (#2306) * Update README.md Added Amplify Logo * Updated Update README.md with Amplify Doc link Updated Update README.md with Amplify Doc link Co-authored-by: Kamil Sobol * Update README.md Updated new Logo * Update README.md Co-authored-by: josef --------- Co-authored-by: Kamil Sobol Co-authored-by: josef --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f02f9af1778..782788f8362 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ + +Amplify logo + + # AWS Amplify Gen 2 Backend This next generation of Amplify’s backend building experience lets you author your frontend and backend definition completely with TypeScript, a file convention, and Git branch-based environments. To learn more, visit [AWS Amplify Gen 2](https://docs.amplify.aws). From 3cf073865a15bb0d5306ec51ab3101e7550bb177 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Wed, 11 Dec 2024 08:52:08 -0800 Subject: [PATCH 154/199] do not stream function logs if stack does not exist (#2315) * start streaming function logs if deployment successful * update detection of BackendOutputClientErrors * change to not stream only if stack does not exist * update changeset * remove eslint package from changeset --- .changeset/few-candles-wink.md | 8 ++++ .changeset/great-mugs-warn.md | 5 +++ .../generate/forms/generate_forms_command.ts | 8 ++-- .../src/unified_client_config_generator.ts | 10 ++--- packages/deployed-backend-client/API.md | 1 + .../src/backend_output_client_factory.ts | 24 +++++++++++ packages/eslint-rules/src/index.ts | 5 +++ ...output_client_error_no_instance_of.test.ts | 32 ++++++++++++++ ...kent_output_client_error_no_instance_of.ts | 43 +++++++++++++++++++ .../get_backend_output_with_error_handling.ts | 8 ++-- .../src/lambda_function_log_streamer.test.ts | 23 +++++++++- .../src/lambda_function_log_streamer.ts | 23 ++++++++-- 12 files changed, 173 insertions(+), 17 deletions(-) create mode 100644 .changeset/few-candles-wink.md create mode 100644 .changeset/great-mugs-warn.md create mode 100644 packages/eslint-rules/src/rules/backend_output_client_error_no_instance_of.test.ts create mode 100644 packages/eslint-rules/src/rules/backent_output_client_error_no_instance_of.ts diff --git a/.changeset/few-candles-wink.md b/.changeset/few-candles-wink.md new file mode 100644 index 00000000000..224a172d780 --- /dev/null +++ b/.changeset/few-candles-wink.md @@ -0,0 +1,8 @@ +--- +'@aws-amplify/deployed-backend-client': minor +'@aws-amplify/model-generator': patch +'@aws-amplify/client-config': patch +'@aws-amplify/backend-cli': patch +--- + +update detection of BackendOutputClientErrors diff --git a/.changeset/great-mugs-warn.md b/.changeset/great-mugs-warn.md new file mode 100644 index 00000000000..36189b9cb33 --- /dev/null +++ b/.changeset/great-mugs-warn.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/sandbox': patch +--- + +do not stream function logs if stack does not exist diff --git a/packages/cli/src/commands/generate/forms/generate_forms_command.ts b/packages/cli/src/commands/generate/forms/generate_forms_command.ts index 34667029dc3..b9597e769ac 100644 --- a/packages/cli/src/commands/generate/forms/generate_forms_command.ts +++ b/packages/cli/src/commands/generate/forms/generate_forms_command.ts @@ -83,7 +83,7 @@ export class GenerateFormsCommand output = await backendOutputClient.getOutput(backendIdentifier); } catch (error) { if ( - error instanceof BackendOutputClientError && + BackendOutputClientError.isBackendOutputClientError(error) && error.code === BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS ) { throw new AmplifyUserError( @@ -96,7 +96,7 @@ export class GenerateFormsCommand ); } if ( - error instanceof BackendOutputClientError && + BackendOutputClientError.isBackendOutputClientError(error) && error.code === BackendOutputClientErrorType.NO_STACK_FOUND ) { throw new AmplifyUserError( @@ -110,7 +110,7 @@ export class GenerateFormsCommand ); } if ( - error instanceof BackendOutputClientError && + BackendOutputClientError.isBackendOutputClientError(error) && error.code === BackendOutputClientErrorType.CREDENTIALS_ERROR ) { throw new AmplifyUserError( @@ -125,7 +125,7 @@ export class GenerateFormsCommand ); } if ( - error instanceof BackendOutputClientError && + BackendOutputClientError.isBackendOutputClientError(error) && error.code === BackendOutputClientErrorType.ACCESS_DENIED ) { throw new AmplifyUserError( diff --git a/packages/client-config/src/unified_client_config_generator.ts b/packages/client-config/src/unified_client_config_generator.ts index 284bde00971..fc33c316cca 100644 --- a/packages/client-config/src/unified_client_config_generator.ts +++ b/packages/client-config/src/unified_client_config_generator.ts @@ -40,7 +40,7 @@ export class UnifiedClientConfigGenerator implements ClientConfigGenerator { output = await this.fetchOutput(); } catch (error) { if ( - error instanceof BackendOutputClientError && + BackendOutputClientError.isBackendOutputClientError(error) && error.code === BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS ) { throw new AmplifyUserError( @@ -53,7 +53,7 @@ export class UnifiedClientConfigGenerator implements ClientConfigGenerator { ); } if ( - error instanceof BackendOutputClientError && + BackendOutputClientError.isBackendOutputClientError(error) && error.code === BackendOutputClientErrorType.NO_STACK_FOUND ) { throw new AmplifyUserError( @@ -67,7 +67,7 @@ export class UnifiedClientConfigGenerator implements ClientConfigGenerator { ); } if ( - error instanceof BackendOutputClientError && + BackendOutputClientError.isBackendOutputClientError(error) && error.code === BackendOutputClientErrorType.METADATA_RETRIEVAL_ERROR ) { throw new AmplifyUserError( @@ -81,7 +81,7 @@ export class UnifiedClientConfigGenerator implements ClientConfigGenerator { ); } if ( - error instanceof BackendOutputClientError && + BackendOutputClientError.isBackendOutputClientError(error) && error.code === BackendOutputClientErrorType.CREDENTIALS_ERROR ) { throw new AmplifyUserError( @@ -96,7 +96,7 @@ export class UnifiedClientConfigGenerator implements ClientConfigGenerator { ); } if ( - error instanceof BackendOutputClientError && + BackendOutputClientError.isBackendOutputClientError(error) && error.code === BackendOutputClientErrorType.ACCESS_DENIED ) { throw new AmplifyUserError( diff --git a/packages/deployed-backend-client/API.md b/packages/deployed-backend-client/API.md index c0dfc594729..3c5353103ac 100644 --- a/packages/deployed-backend-client/API.md +++ b/packages/deployed-backend-client/API.md @@ -89,6 +89,7 @@ export class BackendOutputClientError extends Error { constructor(code: BackendOutputClientErrorType, message: string, options?: ErrorOptions); // (undocumented) code: BackendOutputClientErrorType; + static isBackendOutputClientError: (error: unknown) => error is BackendOutputClientError; } // @public (undocumented) diff --git a/packages/deployed-backend-client/src/backend_output_client_factory.ts b/packages/deployed-backend-client/src/backend_output_client_factory.ts index 2dca755d66b..f9e553f369d 100644 --- a/packages/deployed-backend-client/src/backend_output_client_factory.ts +++ b/packages/deployed-backend-client/src/backend_output_client_factory.ts @@ -31,6 +31,30 @@ export class BackendOutputClientError extends Error { super(message, options); this.code = code; } + + /** + * This function is a type predicate for BackendOutputClientError. + * See https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates. + * + * Checks if error is an BackendOutputClientError by inspecting if required properties are set. + * This is recommended instead of instanceof operator. + * The instance of operator does not work as expected if BackendOutputClientError class is loaded + * from multiple sources, for example when package manager decides to not de-duplicate dependencies. + * See https://github.com/nodejs/node/issues/17943. + */ + static isBackendOutputClientError = ( + error: unknown + ): error is BackendOutputClientError => { + return ( + error instanceof Error && + 'code' in error && + typeof error.code === 'string' && + (Object.values(BackendOutputClientErrorType) as unknown[]).includes( + error.code + ) && + typeof error.message === 'string' + ); + }; } /** diff --git a/packages/eslint-rules/src/index.ts b/packages/eslint-rules/src/index.ts index 2daf628e91c..a32e2ca3afa 100644 --- a/packages/eslint-rules/src/index.ts +++ b/packages/eslint-rules/src/index.ts @@ -3,10 +3,13 @@ import { amplifyErrorNameRule } from './rules/amplify_error_name.js'; import { preferAmplifyErrorsRule } from './rules/prefer_amplify_errors.js'; import { noAmplifyErrors } from './rules/no_amplify_errors.js'; import { amplifyErrorNoInstanceOf } from './rules/amplify_error_no_instance_of'; +import { backendOutputClientErrorNoInstanceOf } from './rules/backent_output_client_error_no_instance_of.js'; export const rules: Record = { 'amplify-error-name': amplifyErrorNameRule, 'amplify-error-no-instanceof': amplifyErrorNoInstanceOf, + 'backend-output-client-error-no-instanceof': + backendOutputClientErrorNoInstanceOf, 'no-empty-catch': noEmptyCatchRule, 'prefer-amplify-errors': preferAmplifyErrorsRule, 'no-amplify-errors': noAmplifyErrors, @@ -18,6 +21,8 @@ export const configs = { rules: { 'amplify-backend-rules/amplify-error-name': 'error', 'amplify-backend-rules/amplify-error-no-instanceof': 'error', + 'amplify-backend-rules/backend-output-client-error-no-instanceof': + 'error', 'amplify-backend-rules/no-empty-catch': 'error', 'amplify-backend-rules/prefer-amplify-errors': 'off', 'amplify-backend-rules/no-amplify-errors': 'off', diff --git a/packages/eslint-rules/src/rules/backend_output_client_error_no_instance_of.test.ts b/packages/eslint-rules/src/rules/backend_output_client_error_no_instance_of.test.ts new file mode 100644 index 00000000000..fec54d38412 --- /dev/null +++ b/packages/eslint-rules/src/rules/backend_output_client_error_no_instance_of.test.ts @@ -0,0 +1,32 @@ +import * as nodeTest from 'node:test'; +import { RuleTester } from '@typescript-eslint/rule-tester'; +import { backendOutputClientErrorNoInstanceOf } from './backent_output_client_error_no_instance_of'; + +RuleTester.afterAll = nodeTest.after; +// See https://typescript-eslint.io/packages/rule-tester/#with-specific-frameworks +// Node test runner methods return promises which are not relevant in the context of testing. +// We do ignore them in other places with void keyword. +// eslint-disable-next-line @typescript-eslint/no-misused-promises +RuleTester.it = nodeTest.it; +// eslint-disable-next-line @typescript-eslint/no-misused-promises +RuleTester.describe = nodeTest.describe; + +const ruleTester = new RuleTester(); + +ruleTester.run( + 'backend-output-client-error-no-instanceof', + backendOutputClientErrorNoInstanceOf, + { + valid: ['e instanceof Error'], + invalid: [ + { + code: 'e instanceof BackendOutputClientError', + errors: [ + { + messageId: 'noInstanceOfWithBackendOutputClientError', + }, + ], + }, + ], + } +); diff --git a/packages/eslint-rules/src/rules/backent_output_client_error_no_instance_of.ts b/packages/eslint-rules/src/rules/backent_output_client_error_no_instance_of.ts new file mode 100644 index 00000000000..11e0c6ae4ec --- /dev/null +++ b/packages/eslint-rules/src/rules/backent_output_client_error_no_instance_of.ts @@ -0,0 +1,43 @@ +import { ESLintUtils } from '@typescript-eslint/utils'; + +/** + * This rule flags empty catch blocks. Even if they contain comments. + * + * This rule differs from built in https://github.com/eslint/eslint/blob/main/lib/rules/no-empty.js + * in such a way that it uses typescript-eslint and typescript AST + * which does not include comments as statements in catch clause body block. + */ +export const backendOutputClientErrorNoInstanceOf = + ESLintUtils.RuleCreator.withoutDocs({ + create(context) { + return { + // This naming comes from @typescript-eslint/utils types. + // eslint-disable-next-line @typescript-eslint/naming-convention + BinaryExpression(node) { + if ( + node.operator === 'instanceof' && + node.right.type === 'Identifier' && + node.right.name === 'BackendOutputClientError' + ) { + context.report({ + messageId: 'noInstanceOfWithBackendOutputClientError', + node, + }); + } + }, + }; + }, + meta: { + docs: { + description: + 'Instanceof operator must not be used with BackendOutputClientError.', + }, + messages: { + noInstanceOfWithBackendOutputClientError: + 'Do not use instanceof with BackendOutputClientError. Use BackendOutputClientError.isBackendOutputClientError instead.', + }, + type: 'problem', + schema: [], + }, + defaultOptions: [], + }); diff --git a/packages/model-generator/src/get_backend_output_with_error_handling.ts b/packages/model-generator/src/get_backend_output_with_error_handling.ts index 98fb6ffc703..fc45ed295f9 100644 --- a/packages/model-generator/src/get_backend_output_with_error_handling.ts +++ b/packages/model-generator/src/get_backend_output_with_error_handling.ts @@ -17,7 +17,7 @@ export const getBackendOutputWithErrorHandling = async ( return await backendOutputClient.getOutput(backendIdentifier); } catch (error) { if ( - error instanceof BackendOutputClientError && + BackendOutputClientError.isBackendOutputClientError(error) && error.code === BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS ) { throw new AmplifyUserError( @@ -30,7 +30,7 @@ export const getBackendOutputWithErrorHandling = async ( ); } if ( - error instanceof BackendOutputClientError && + BackendOutputClientError.isBackendOutputClientError(error) && error.code === BackendOutputClientErrorType.NO_STACK_FOUND ) { throw new AmplifyUserError( @@ -44,7 +44,7 @@ export const getBackendOutputWithErrorHandling = async ( ); } if ( - error instanceof BackendOutputClientError && + BackendOutputClientError.isBackendOutputClientError(error) && error.code === BackendOutputClientErrorType.CREDENTIALS_ERROR ) { throw new AmplifyUserError( @@ -58,7 +58,7 @@ export const getBackendOutputWithErrorHandling = async ( ); } if ( - error instanceof BackendOutputClientError && + BackendOutputClientError.isBackendOutputClientError(error) && error.code === BackendOutputClientErrorType.ACCESS_DENIED ) { throw new AmplifyUserError( diff --git a/packages/sandbox/src/lambda_function_log_streamer.test.ts b/packages/sandbox/src/lambda_function_log_streamer.test.ts index 6072e57adc5..dfd98eb1198 100644 --- a/packages/sandbox/src/lambda_function_log_streamer.test.ts +++ b/packages/sandbox/src/lambda_function_log_streamer.test.ts @@ -1,7 +1,11 @@ import { beforeEach, describe, it, mock } from 'node:test'; import { LambdaFunctionLogStreamer } from './lambda_function_log_streamer.js'; import assert from 'node:assert'; -import { BackendOutputClient } from '@aws-amplify/deployed-backend-client'; +import { + BackendOutputClient, + BackendOutputClientError, + BackendOutputClientErrorType, +} from '@aws-amplify/deployed-backend-client'; import { CloudWatchLogsClient } from '@aws-sdk/client-cloudwatch-logs'; import { @@ -147,6 +151,23 @@ void describe('LambdaFunctionLogStreamer', () => { assert.strictEqual(lambdaClientSendMock.mock.callCount(), 0); }); + void it('return early if backend output client throws with stack does not exist', async () => { + backendOutputClientMock.getOutput.mock.mockImplementationOnce(() => { + return Promise.reject( + new BackendOutputClientError( + BackendOutputClientErrorType.NO_STACK_FOUND, + 'Stack with id test-stack does not exist' + ) + ); + }); + await classUnderTest.startStreamingLogs(testSandboxBackendId, { + enabled: true, + }); + + // No lambda calls to retrieve tags + assert.strictEqual(lambdaClientSendMock.mock.callCount(), 0); + }); + void it('calls logs monitor with all the customer defined functions and conversation handlers if no function name filter is provided', async () => { await classUnderTest.startStreamingLogs(testSandboxBackendId, { enabled: true, diff --git a/packages/sandbox/src/lambda_function_log_streamer.ts b/packages/sandbox/src/lambda_function_log_streamer.ts index ee3ac62019d..12858111e77 100644 --- a/packages/sandbox/src/lambda_function_log_streamer.ts +++ b/packages/sandbox/src/lambda_function_log_streamer.ts @@ -1,5 +1,9 @@ import { LogLevel, Printer } from '@aws-amplify/cli-core'; -import { BackendOutputClient } from '@aws-amplify/deployed-backend-client'; +import { + BackendOutputClient, + BackendOutputClientError, + BackendOutputClientErrorType, +} from '@aws-amplify/deployed-backend-client'; import { TagName } from '@aws-amplify/platform-core'; import { BackendIdentifier, BackendOutput } from '@aws-amplify/plugin-types'; @@ -37,8 +41,21 @@ export class LambdaFunctionLogStreamer { return; } - const backendOutput: BackendOutput = - await this.backendOutputClient.getOutput(sandboxBackendId); + let backendOutput: BackendOutput = {}; + try { + backendOutput = await this.backendOutputClient.getOutput( + sandboxBackendId + ); + } catch (error) { + // If stack does not exist, we do not want to go further to start streaming logs + if ( + BackendOutputClientError.isBackendOutputClientError(error) && + error.code === BackendOutputClientErrorType.NO_STACK_FOUND + ) { + this.enabled = false; + return; + } + } const definedFunctionsPayload = backendOutput['AWS::Amplify::Function']?.payload.definedFunctions; From 560878f3af595d4a661f396545fe970fac39504d Mon Sep 17 00:00:00 2001 From: Kethan sai Date: Wed, 11 Dec 2024 12:23:34 -0500 Subject: [PATCH 155/199] updates layer to also use layername:version (#2316) * updates layer to also use layername:version --- .changeset/silent-files-appear.md | 6 ++ packages/backend-function/src/factory.ts | 26 ++++-- .../backend-function/src/layer_parser.test.ts | 81 ++++++++++++++++++- packages/backend-function/src/layer_parser.ts | 55 ++++++++++--- 4 files changed, 147 insertions(+), 21 deletions(-) create mode 100644 .changeset/silent-files-appear.md diff --git a/.changeset/silent-files-appear.md b/.changeset/silent-files-appear.md new file mode 100644 index 00000000000..3cdd1f61306 --- /dev/null +++ b/.changeset/silent-files-appear.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/backend-function': minor +'@aws-amplify/backend': minor +--- + +updates layer to also use layername:version diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 81769e7d6ac..e0c8abb4a49 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -46,8 +46,8 @@ import * as path from 'path'; import { FunctionEnvironmentTranslator } from './function_env_translator.js'; import { FunctionEnvironmentTypeGenerator } from './function_env_type_generator.js'; import { FunctionLayerArnParser } from './layer_parser.js'; -import { convertFunctionSchedulesToRuleSchedules } from './schedule_parser.js'; import { convertLoggingOptionsToCDK } from './logging_options_parser.js'; +import { convertFunctionSchedulesToRuleSchedules } from './schedule_parser.js'; const functionStackType = 'function-Lambda'; @@ -148,6 +148,11 @@ export type FunctionProps = { * layers: { * "@aws-lambda-powertools/logger": "arn:aws:lambda::094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:11" * }, + * or + * @example + * layers: { + * "Sharp": "SharpLayer:1" + * }, * @see [Amplify documentation for Lambda layers](https://docs.amplify.aws/react/build-a-backend/functions/add-lambda-layers) * @see [AWS documentation for Lambda layers](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html) */ @@ -225,8 +230,7 @@ class FunctionFactory implements ConstructFactory { ): HydratedFunctionProps => { const name = this.resolveName(); resourceNameValidator?.validate(name); - const parser = new FunctionLayerArnParser(); - const layers = parser.parseLayers(this.props.layers ?? {}, name); + return { name, entry: this.resolveEntry(), @@ -236,7 +240,7 @@ class FunctionFactory implements ConstructFactory { runtime: this.resolveRuntime(), schedule: this.resolveSchedule(), bundling: this.resolveBundling(), - layers, + layers: this.props.layers ?? {}, resourceGroupName: this.props.resourceGroupName ?? 'function', logging: this.props.logging ?? {}, }; @@ -413,8 +417,18 @@ class FunctionGenerator implements ConstructContainerEntryGenerator { scope, backendSecretResolver, }: GenerateContainerEntryProps) => { - // resolve layers to LayerVersion objects for the NodejsFunction constructor using the scope. - const resolvedLayers = Object.entries(this.props.layers).map(([key, arn]) => + // Move layer resolution here where we have access to scope + const parser = new FunctionLayerArnParser( + Stack.of(scope).region, + Stack.of(scope).account + ); + const resolvedLayerArns = parser.parseLayers( + this.props.layers ?? {}, + this.props.name + ); + + // resolve layers to LayerVersion objects for the NodejsFunction constructor + const resolvedLayers = Object.entries(resolvedLayerArns).map(([key, arn]) => LayerVersion.fromLayerVersionArn( scope, `${this.props.name}-${key}-layer`, diff --git a/packages/backend-function/src/layer_parser.test.ts b/packages/backend-function/src/layer_parser.test.ts index 19c3087364a..7e8ae2653ba 100644 --- a/packages/backend-function/src/layer_parser.test.ts +++ b/packages/backend-function/src/layer_parser.test.ts @@ -10,12 +10,12 @@ import { ResourceNameValidator, } from '@aws-amplify/plugin-types'; import { App, Stack } from 'aws-cdk-lib'; -import { Template } from 'aws-cdk-lib/assertions'; +import { Match, Template } from 'aws-cdk-lib/assertions'; +import fsp from 'fs/promises'; import assert from 'node:assert'; +import path from 'node:path'; import { after, beforeEach, describe, it } from 'node:test'; import { defineFunction } from './factory.js'; -import path from 'node:path'; -import fsp from 'fs/promises'; const createStackAndSetContext = (): Stack => { const app = new App(); @@ -115,7 +115,7 @@ void describe('AmplifyFunctionFactory - Layers', () => { (error: AmplifyUserError) => { assert.strictEqual( error.message, - `Invalid ARN format for layer: ${invalidLayerArn}` + `Invalid format for layer: ${invalidLayerArn}` ); assert.ok(error.resolution); return true; @@ -184,4 +184,77 @@ void describe('AmplifyFunctionFactory - Layers', () => { Layers: [duplicateArn], }); }); + + void it('accepts and converts name:version format to ARN', () => { + const functionFactory = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'lambdaWithNameVersionLayer', + layers: { + myLayer: 'my-layer:1', + }, + }); + const lambda = functionFactory.getInstance(getInstanceProps); + const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'index.handler', + Layers: [ + { + 'Fn::Join': [ + '', + [ + 'arn:aws:lambda:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':layer:my-layer:1', + ], + ], + }, + ], + }); + }); + + void it('throws an error for invalid name:version format', () => { + const invalidFormat = 'my-layer'; // missing version number + const functionFactory = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'lambdaWithInvalidNameVersion', + layers: { + myLayer: invalidFormat, + }, + }); + + assert.throws( + () => functionFactory.getInstance(getInstanceProps), + (error: AmplifyUserError) => { + assert.strictEqual( + error.message, + `Invalid format for layer: ${invalidFormat}` + ); + assert.ok(error.resolution); + return true; + } + ); + }); + + void it('accepts mixed format of ARNs and name:version', () => { + const fullArn = + 'arn:aws:lambda:us-east-1:123456789012:layer:full-arn-layer:1'; + const functionFactory = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'lambdaWithMixedLayerFormats', + layers: { + fullArnLayer: fullArn, + nameVersionLayer: 'name-version-layer:2', + }, + }); + const lambda = functionFactory.getInstance(getInstanceProps); + const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'index.handler', + Layers: Match.arrayWith([fullArn]), + }); + }); }); diff --git a/packages/backend-function/src/layer_parser.ts b/packages/backend-function/src/layer_parser.ts index f90ba68e21b..e5c25361f74 100644 --- a/packages/backend-function/src/layer_parser.ts +++ b/packages/backend-function/src/layer_parser.ts @@ -7,13 +7,26 @@ export class FunctionLayerArnParser { private arnPattern = new RegExp( 'arn:[a-zA-Z0-9-]+:lambda:[a-zA-Z0-9-]+:\\d{12}:layer:[a-zA-Z0-9-_]+:[0-9]+' ); + private nameVersionPattern = new RegExp('^[a-zA-Z0-9-_]+:[0-9]+$'); + + /** + * Creates a new FunctionLayerArnParser + * @param region - AWS region + * @param account - AWS account ID + */ + constructor( + private readonly region: string, + private readonly account: string + ) {} /** * Parse the layers for a function - * @param layers - Layers to be attached to the function + * @param layers - Layers to be attached to the function. Each layer can be specified as either: + * - A full ARN (arn:aws:lambda:::layer::) + * - A name:version format (e.g., "my-layer:1") * @param functionName - Name of the function - * @returns Valid layers for the function - * @throws AmplifyUserError if the layer ARN is invalid + * @returns Valid layers for the function with resolved ARNs + * @throws AmplifyUserError if the layer ARN or name:version format is invalid * @throws AmplifyUserError if the number of layers exceeds the limit */ parseLayers( @@ -23,36 +36,56 @@ export class FunctionLayerArnParser { const validLayers: Record = {}; const uniqueArns = new Set(); - for (const [key, arn] of Object.entries(layers)) { - if (!this.isValidLayerArn(arn)) { - throw new AmplifyUserError('InvalidLayerArnFormatError', { - message: `Invalid ARN format for layer: ${arn}`, - resolution: `Update the layer ARN with the expected format: arn:aws:lambda:::layer:: for function: ${functionName}`, + for (const [key, value] of Object.entries(layers)) { + let arn: string; + + if (this.isValidLayerArn(value)) { + // If it's already a valid ARN, use it as is + arn = value; + } else if (this.isValidNameVersion(value)) { + // If it's in name:version format, construct the ARN using provided region and account + const [name, version] = value.split(':'); + arn = `arn:aws:lambda:${this.region}:${this.account}:layer:${name}:${version}`; + } else { + throw new AmplifyUserError('InvalidLayerFormatError', { + message: `Invalid format for layer: ${value}`, + resolution: `Layer must be either a full ARN (arn:aws:lambda:::layer::) or name:version format for function: ${functionName}`, }); } - // Add to validLayers and uniqueArns only if the ARN hasn't been added already + // Ensure we don't add duplicate ARNs if (!uniqueArns.has(arn)) { uniqueArns.add(arn); validLayers[key] = arn; } } - // Validate the number of unique layers this.validateLayerCount(uniqueArns); - return validLayers; } /** * Validate the ARN format for a Lambda Layer + * @param arn - The ARN string to validate + * @returns boolean indicating if the ARN format is valid */ private isValidLayerArn(arn: string): boolean { return this.arnPattern.test(arn); } + /** + * Validate the name:version format for a Lambda Layer + * @param value - The string to validate in format "name:version" + * @returns boolean indicating if the format is valid + */ + private isValidNameVersion(value: string): boolean { + return this.nameVersionPattern.test(value); + } + /** * Validate the number of layers attached to a function + * @param uniqueArns - Set of unique layer ARNs + * @throws AmplifyUserError if the number of layers exceeds 5 * @see https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html#function-configuration-deployment-and-execution */ private validateLayerCount(uniqueArns: Set): void { From dedcc27fcfa2441e1d33bff04be33c4fa31fca5a Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Thu, 12 Dec 2024 15:00:25 -0800 Subject: [PATCH 156/199] add error mapping for role is invalid or cannot be assumed error (#2325) --- .changeset/brown-mayflies-turn.md | 5 +++++ .../backend-deployer/src/cdk_error_mapper.test.ts | 7 +++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 11 +++++++++++ 3 files changed, 23 insertions(+) create mode 100644 .changeset/brown-mayflies-turn.md diff --git a/.changeset/brown-mayflies-turn.md b/.changeset/brown-mayflies-turn.md new file mode 100644 index 00000000000..7cc4e3fc980 --- /dev/null +++ b/.changeset/brown-mayflies-turn.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +add error mapping for role is invalid or cannot be assumed error' diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 8860260173e..58d2e0d6beb 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -659,6 +659,13 @@ npm error enoent`, errorName: 'LambdaEmptyZipFault', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: `Error: some-stack failed: ValidationError: Role role-arn is invalid or cannot be assumed`, + expectedTopLevelErrorMessage: + 'Role role-arn is invalid or cannot be assumed', + errorName: 'InvalidOrCannotAssumeRoleError', + expectedDownstreamErrorMessage: undefined, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 4418fc3275b..3bcb69c8053 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -164,6 +164,16 @@ export class CdkErrorMapper { errorName: 'CDKNotFoundError', classification: 'ERROR', }, + { + errorRegex: + /ValidationError: Role (?.*) is invalid or cannot be assumed/, + humanReadableErrorMessage: + 'Role {roleArn} is invalid or cannot be assumed', + resolutionMessage: + 'Ensure the role exists and AWS credentials have an IAM policy that grants sts:AssumeRole for the role', + errorName: 'InvalidOrCannotAssumeRoleError', + classification: 'ERROR', + }, { errorRegex: new RegExp( `(SyntaxError|ReferenceError|TypeError)( \\[[A-Z_]+])?:((?:.|${this.multiLineEolRegex})*?at .*)` @@ -523,6 +533,7 @@ export type CDKDeploymentError = | 'ExpiredTokenError' | 'FileConventionError' | 'ModuleNotFoundError' + | 'InvalidOrCannotAssumeRoleError' | 'InvalidPackageJsonError' | 'SecretNotSetError' | 'SyntaxError' From 95942c5d2f2fa3dd6830a4a47145dc6734037366 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:42:52 +0100 Subject: [PATCH 157/199] fix: expand wrapping of credentials related errors (#2328) --- .changeset/clever-crabs-sip.md | 6 + package-lock.json | 172 +++++++++--------- .../src/cdk_error_mapper.test.ts | 7 +- .../backend-deployer/src/cdk_error_mapper.ts | 2 +- .../src/errors/amplify_error.test.ts | 20 ++ .../platform-core/src/errors/amplify_error.ts | 11 +- 6 files changed, 126 insertions(+), 92 deletions(-) create mode 100644 .changeset/clever-crabs-sip.md diff --git a/.changeset/clever-crabs-sip.md b/.changeset/clever-crabs-sip.md new file mode 100644 index 00000000000..6ed49869540 --- /dev/null +++ b/.changeset/clever-crabs-sip.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/backend-deployer': patch +'@aws-amplify/platform-core': patch +--- + +expand wrapping of credentials related errors diff --git a/package-lock.json b/package-lock.json index 53ab71e34ed..c3fe5324670 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31607,18 +31607,18 @@ }, "packages/ai-constructs": { "name": "@aws-amplify/ai-constructs", - "version": "1.0.0", + "version": "1.1.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-bedrock-runtime": "^3.622.0", "@smithy/types": "^3.3.0", "json-schema-to-ts": "^3.1.1" }, "devDependencies": { - "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/backend-output-storage": "^1.1.4", "typescript": "^5.0.0" }, "peerDependencies": { @@ -31632,12 +31632,12 @@ }, "packages/auth-construct": { "name": "@aws-amplify/auth-construct", - "version": "1.5.0", + "version": "1.5.1", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/backend-output-storage": "^1.1.4", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/util-arn-parser": "^3.568.0" }, "peerDependencies": { @@ -31647,20 +31647,20 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.8.0", + "version": "1.9.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-auth": "^1.4.1", - "@aws-amplify/backend-data": "^1.2.1", - "@aws-amplify/backend-function": "^1.8.0", + "@aws-amplify/backend-auth": "^1.4.2", + "@aws-amplify/backend-data": "^1.2.2", + "@aws-amplify/backend-function": "^1.9.0", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/backend-secret": "^1.1.4", - "@aws-amplify/backend-storage": "^1.2.3", - "@aws-amplify/client-config": "^1.5.2", + "@aws-amplify/backend-storage": "^1.2.4", + "@aws-amplify/client-config": "^1.5.3", "@aws-amplify/data-schema": "^1.13.4", - "@aws-amplify/platform-core": "^1.2.1", - "@aws-amplify/plugin-types": "^1.5.0", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-amplify": "^3.624.0", "lodash.snakecase": "^4.1.1" }, @@ -31676,15 +31676,15 @@ }, "packages/backend-ai": { "name": "@aws-amplify/backend-ai", - "version": "1.0.1", + "version": "1.1.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^1.0.0", + "@aws-amplify/ai-constructs": "^1.1.0", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/data-schema-types": "^1.2.0", - "@aws-amplify/platform-core": "^1.2.1", - "@aws-amplify/plugin-types": "^1.5.0" + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", @@ -31693,17 +31693,17 @@ }, "packages/backend-auth": { "name": "@aws-amplify/backend-auth", - "version": "1.4.1", + "version": "1.4.2", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/auth-construct": "^1.5.0", + "@aws-amplify/auth-construct": "^1.5.1", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.5.0" + "@aws-amplify/backend-output-storage": "^1.1.4", + "@aws-amplify/plugin-types": "^1.6.0" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.2.1", + "@aws-amplify/backend-platform-test-stubs": "^0.3.7", + "@aws-amplify/platform-core": "^1.3.0", "@aws-sdk/client-cognito-identity": "^3.624.0", "@aws-sdk/client-cognito-identity-provider": "^3.624.0", "@types/aws-lambda": "^8.10.119", @@ -31716,20 +31716,20 @@ }, "packages/backend-data": { "name": "@aws-amplify/backend-data", - "version": "1.2.1", + "version": "1.2.2", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/data-construct": "^1.10.1", "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/graphql-generator": "^0.5.1", - "@aws-amplify/plugin-types": "^1.4.0" + "@aws-amplify/plugin-types": "^1.6.0" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.6", + "@aws-amplify/backend-platform-test-stubs": "^0.3.7", "@aws-amplify/data-schema": "^1.13.4", - "@aws-amplify/platform-core": "^1.2.1" + "@aws-amplify/platform-core": "^1.3.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", @@ -31738,11 +31738,11 @@ }, "packages/backend-deployer": { "name": "@aws-amplify/backend-deployer", - "version": "1.1.10", + "version": "1.1.11", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.2.2", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "execa": "^9.5.1", "strip-ansi": "^6.0.1", "tsx": "^4.6.1" @@ -31872,18 +31872,18 @@ }, "packages/backend-function": { "name": "@aws-amplify/backend-function", - "version": "1.8.0", + "version": "1.9.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.5.0", + "@aws-amplify/backend-output-storage": "^1.1.4", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-s3": "^3.624.0", "execa": "^9.5.1" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.2.1", + "@aws-amplify/backend-platform-test-stubs": "^0.3.7", + "@aws-amplify/platform-core": "^1.3.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", @@ -32039,12 +32039,12 @@ }, "packages/backend-output-storage": { "name": "@aws-amplify/backend-output-storage", - "version": "1.1.3", + "version": "1.1.4", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.3.1" + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0" @@ -32052,10 +32052,10 @@ }, "packages/backend-platform-test-stubs": { "name": "@aws-amplify/backend-platform-test-stubs", - "version": "0.3.6", + "version": "0.3.7", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.6.0", "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } @@ -32075,16 +32075,16 @@ }, "packages/backend-storage": { "name": "@aws-amplify/backend-storage", - "version": "1.2.3", + "version": "1.2.4", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.1", - "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.5.0" + "@aws-amplify/backend-output-storage": "^1.1.4", + "@aws-amplify/plugin-types": "^1.6.0" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.2.1" + "@aws-amplify/backend-platform-test-stubs": "^0.3.7", + "@aws-amplify/platform-core": "^1.3.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", @@ -32093,21 +32093,21 @@ }, "packages/cli": { "name": "@aws-amplify/backend-cli", - "version": "1.4.2", + "version": "1.4.3", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.9", + "@aws-amplify/backend-deployer": "^1.1.11", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/cli-core": "^1.2.0", - "@aws-amplify/client-config": "^1.5.1", + "@aws-amplify/cli-core": "^1.2.1", + "@aws-amplify/client-config": "^1.5.3", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/form-generator": "^1.0.3", "@aws-amplify/model-generator": "^1.0.9", - "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.4.0", - "@aws-amplify/sandbox": "^1.2.5", - "@aws-amplify/schema-generator": "^1.2.5", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/sandbox": "^1.2.7", + "@aws-amplify/schema-generator": "^1.2.6", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", @@ -32140,10 +32140,10 @@ }, "packages/cli-core": { "name": "@aws-amplify/cli-core", - "version": "1.2.0", + "version": "1.2.1", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.0.5", + "@aws-amplify/platform-core": "^1.3.0", "@inquirer/prompts": "^3.0.0", "execa": "^9.5.1", "kleur": "^4.1.5" @@ -32483,14 +32483,14 @@ }, "packages/client-config": { "name": "@aws-amplify/client-config", - "version": "1.5.2", + "version": "1.5.3", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/model-generator": "^1.0.7", - "@aws-amplify/platform-core": "^1.0.7", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "zod": "^3.22.2" }, "devDependencies": { @@ -32505,12 +32505,12 @@ } }, "packages/create-amplify": { - "version": "1.0.6", + "version": "1.0.7", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/cli-core": "^1.1.3", - "@aws-amplify/platform-core": "^1.0.3", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/cli-core": "^1.2.1", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "execa": "^9.5.1", "kleur": "^4.1.5", "yargs": "^17.7.2" @@ -32786,20 +32786,20 @@ }, "packages/integration-tests": { "name": "@aws-amplify/integration-tests", - "version": "0.6.0", + "version": "0.6.1", "license": "Apache-2.0", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^1.0.0", - "@aws-amplify/auth-construct": "^1.4.0", - "@aws-amplify/backend": "^1.6.0", - "@aws-amplify/backend-ai": "^1.0.0", + "@aws-amplify/ai-constructs": "^1.1.0", + "@aws-amplify/auth-construct": "^1.5.1", + "@aws-amplify/backend": "^1.9.0", + "@aws-amplify/backend-ai": "^1.1.0", "@aws-amplify/backend-secret": "^1.1.4", - "@aws-amplify/client-config": "^1.5.1", + "@aws-amplify/client-config": "^1.5.3", "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-accessanalyzer": "^3.624.0", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-bedrock-runtime": "^3.622.0", @@ -32995,10 +32995,10 @@ }, "packages/platform-core": { "name": "@aws-amplify/platform-core", - "version": "1.2.2", + "version": "1.3.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.5.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-sts": "^3.624.0", "is-ci": "^3.0.1", "lodash.mergewith": "^4.6.2", @@ -33031,7 +33031,7 @@ }, "packages/plugin-types": { "name": "@aws-amplify/plugin-types", - "version": "1.5.0", + "version": "1.6.0", "license": "Apache-2.0", "peerDependencies": { "@aws-sdk/types": "^3.609.0", @@ -33041,16 +33041,16 @@ }, "packages/sandbox": { "name": "@aws-amplify/sandbox", - "version": "1.2.6", + "version": "1.2.7", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.8", + "@aws-amplify/backend-deployer": "^1.1.11", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/cli-core": "^1.2.0", - "@aws-amplify/client-config": "^1.5.1", + "@aws-amplify/cli-core": "^1.2.1", + "@aws-amplify/client-config": "^1.5.3", "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/platform-core": "^1.2.1", - "@aws-amplify/plugin-types": "^1.5.0", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", @@ -33072,11 +33072,11 @@ }, "packages/schema-generator": { "name": "@aws-amplify/schema-generator", - "version": "1.2.5", + "version": "1.2.6", "license": "Apache-2.0", "dependencies": { "@aws-amplify/graphql-schema-generator": "^0.11.0", - "@aws-amplify/platform-core": "^1.0.5" + "@aws-amplify/platform-core": "^1.3.0" } } } diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 58d2e0d6beb..9266b7d999c 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -25,13 +25,12 @@ const testErrorMappings = [ 'ExpiredToken: The security token included in the request is expired', }, { - errorMessage: - 'Error: The security token included in the request is expired', + errorMessage: 'The security token included in the request is expired', expectedTopLevelErrorMessage: 'The security token included in the request is invalid.', errorName: 'ExpiredTokenError', expectedDownstreamErrorMessage: - 'Error: The security token included in the request is expired', + 'The security token included in the request is expired', }, { errorMessage: @@ -40,7 +39,7 @@ const testErrorMappings = [ 'The security token included in the request is invalid.', errorName: 'ExpiredTokenError', expectedDownstreamErrorMessage: - 'InvalidClientTokenId: The security token included in the request is invalid', + 'The security token included in the request is invalid', }, { errorMessage: 'Access Denied', diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 3bcb69c8053..def97f292de 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -99,7 +99,7 @@ export class CdkErrorMapper { }> => [ { errorRegex: - /ExpiredToken: .*|(Error|InvalidClientTokenId): The security token included in the request is (expired|invalid)/, + /ExpiredToken: .*|The security token included in the request is (expired|invalid)/, humanReadableErrorMessage: 'The security token included in the request is invalid.', resolutionMessage: diff --git a/packages/platform-core/src/errors/amplify_error.test.ts b/packages/platform-core/src/errors/amplify_error.test.ts index c4f0c974f46..8ecc5d838d7 100644 --- a/packages/platform-core/src/errors/amplify_error.test.ts +++ b/packages/platform-core/src/errors/amplify_error.test.ts @@ -189,6 +189,26 @@ void describe('AmplifyError.fromError', async () => { `Failed the test for error ${error.message}` ); }); + void it('wraps credentials related errors in AmplifyUserError', () => { + const error = new Error( + 'The security token included in the request is expired' + ); + [ + 'ExpiredToken', + 'ExpiredTokenException', + 'CredentialsProviderError', + 'InvalidClientTokenId', + 'CredentialsError', + ].forEach((name) => { + error.name = name; + const actual = AmplifyError.fromError(error); + assert.ok( + AmplifyError.isAmplifyError(actual) && + actual.name === 'CredentialsError', + `Failed the test while wrapping error ${name}` + ); + }); + }); void it('wraps InsufficientDiskSpaceError in AmplifyUserError', () => { const insufficientDiskSpaceErrors = [ new Error( diff --git a/packages/platform-core/src/errors/amplify_error.ts b/packages/platform-core/src/errors/amplify_error.ts index 314444252e8..a304dee6f6e 100644 --- a/packages/platform-core/src/errors/amplify_error.ts +++ b/packages/platform-core/src/errors/amplify_error.ts @@ -197,7 +197,16 @@ export abstract class AmplifyError extends Error { } const isCredentialsError = (err?: Error): boolean => { - return !!err && err?.name === 'CredentialsProviderError'; + return ( + !!err && + [ + 'ExpiredToken', + 'ExpiredTokenException', + 'CredentialsProviderError', + 'InvalidClientTokenId', + 'CredentialsError', + ].includes(err.name) + ); }; // These validation messages are taken from https://github.com/yargs/yargs/blob/0c95f9c79e1810cf9c8964fbf7d139009412f7e7/lib/validation.ts From f679cf6704f5d8d7b23d778341ad40014cfd570e Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 13 Dec 2024 13:55:08 -0800 Subject: [PATCH 158/199] expand handling of getaddrinfo ENOTFOUND errors (#2330) --- .changeset/hip-buses-clap.md | 5 +++++ packages/platform-core/src/errors/amplify_error.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/hip-buses-clap.md diff --git a/.changeset/hip-buses-clap.md b/.changeset/hip-buses-clap.md new file mode 100644 index 00000000000..24ef844e85a --- /dev/null +++ b/.changeset/hip-buses-clap.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/platform-core': patch +--- + +expand handling of getaddrinfo ENOTFOUND errors diff --git a/packages/platform-core/src/errors/amplify_error.ts b/packages/platform-core/src/errors/amplify_error.ts index a304dee6f6e..76821f8c63f 100644 --- a/packages/platform-core/src/errors/amplify_error.ts +++ b/packages/platform-core/src/errors/amplify_error.ts @@ -229,7 +229,7 @@ const isYargsValidationError = (err?: Error): boolean => { }; const isENotFoundError = (err?: Error): boolean => { - return !!err && err.message.startsWith('getaddrinfo ENOTFOUND'); + return !!err && err.message.includes('getaddrinfo ENOTFOUND'); }; const isSyntaxError = (err?: Error): boolean => { From 478ea85d3aeac741c060172f054b0b4419d4c3b7 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 13 Dec 2024 14:52:11 -0800 Subject: [PATCH 159/199] add retries to generated env file cleanup (#2331) --- .changeset/gentle-seahorses-sleep.md | 2 ++ packages/backend-function/src/factory.test.ts | 1 + packages/backend-function/src/function_env_translator.test.ts | 1 + .../backend-function/src/function_env_type_generator.test.ts | 1 + packages/backend-function/src/layer_parser.test.ts | 1 + .../src/test-in-memory/data_storage_auth_with_triggers.test.ts | 1 + 6 files changed, 7 insertions(+) create mode 100644 .changeset/gentle-seahorses-sleep.md diff --git a/.changeset/gentle-seahorses-sleep.md b/.changeset/gentle-seahorses-sleep.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/gentle-seahorses-sleep.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index df619e0012a..abf16133542 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -60,6 +60,7 @@ void describe('AmplifyFunctionFactory', () => { await fsp.rm(path.join(process.cwd(), '.amplify'), { recursive: true, force: true, + maxRetries: 3, }); }); diff --git a/packages/backend-function/src/function_env_translator.test.ts b/packages/backend-function/src/function_env_translator.test.ts index b2f6f96fa53..2bccfb39097 100644 --- a/packages/backend-function/src/function_env_translator.test.ts +++ b/packages/backend-function/src/function_env_translator.test.ts @@ -62,6 +62,7 @@ void describe('FunctionEnvironmentTranslator', () => { await fsp.rm(path.join(process.cwd(), '.amplify'), { recursive: true, force: true, + maxRetries: 3, }); }); diff --git a/packages/backend-function/src/function_env_type_generator.test.ts b/packages/backend-function/src/function_env_type_generator.test.ts index 0865afa8183..9916ef2ee08 100644 --- a/packages/backend-function/src/function_env_type_generator.test.ts +++ b/packages/backend-function/src/function_env_type_generator.test.ts @@ -12,6 +12,7 @@ void describe('FunctionEnvironmentTypeGenerator', () => { await fsp.rm(path.join(process.cwd(), '.amplify'), { recursive: true, force: true, + maxRetries: 3, }); }); diff --git a/packages/backend-function/src/layer_parser.test.ts b/packages/backend-function/src/layer_parser.test.ts index 7e8ae2653ba..d76e6a9f417 100644 --- a/packages/backend-function/src/layer_parser.test.ts +++ b/packages/backend-function/src/layer_parser.test.ts @@ -56,6 +56,7 @@ void describe('AmplifyFunctionFactory - Layers', () => { await fsp.rm(path.join(process.cwd(), '.amplify'), { recursive: true, force: true, + maxRetries: 3, }); }); diff --git a/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts b/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts index 59a1d17525b..688261096f3 100644 --- a/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts +++ b/packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts @@ -65,5 +65,6 @@ void it('data storage auth with triggers', async () => { await fsp.rm(path.join(process.cwd(), '.amplify'), { recursive: true, force: true, + maxRetries: 3, }); }); From d32e4cd2ae6021df90bcbaa9adfbac066afd4019 Mon Sep 17 00:00:00 2001 From: MURAKAMI Masahiko Date: Sun, 15 Dec 2024 03:39:27 +0900 Subject: [PATCH 160/199] Add ephemeralStorageSizeMB option to defineFunction (#2283) * Add ephemeralStorageSize option to defineFunction * fixup * fix: remove unit from default value * re-generate api reports * rename ephemeralStorageSize to ephemeralStorageSizeMB * add `mebibytes` to eslint dictionary * fix: throw AmplifyUserError instead of Error --- .changeset/nasty-tables-heal.md | 5 ++ .eslint_dictionary.json | 1 + packages/backend-function/API.md | 1 + packages/backend-function/src/factory.test.ts | 67 +++++++++++++++++++ packages/backend-function/src/factory.ts | 33 ++++++++- 5 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 .changeset/nasty-tables-heal.md diff --git a/.changeset/nasty-tables-heal.md b/.changeset/nasty-tables-heal.md new file mode 100644 index 00000000000..777a807a343 --- /dev/null +++ b/.changeset/nasty-tables-heal.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-function': minor +--- + +Add ephemeralStorageSizeMB option to defineFunction diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index 71e74fbd53b..708bec2391d 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -100,6 +100,7 @@ "lstat", "macos", "matchers", + "mebibytes", "mfas", "minify", "mkdtemp", diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index fed504b411d..4c44cd115dd 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -93,6 +93,7 @@ export type FunctionProps = { entry?: string; timeoutSeconds?: number; memoryMB?: number; + ephemeralStorageSizeMB?: number; environment?: Record; runtime?: NodeVersion; schedule?: FunctionSchedule | FunctionSchedule[]; diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index abf16133542..6bb63f14d2a 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -683,4 +683,71 @@ void describe('AmplifyFunctionFactory', () => { 'function-Lambda' ); }); + + void describe('ephemeralStorageSizeMB property', () => { + void it('sets valid ephemeralStorageSize', () => { + const lambda = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + ephemeralStorageSizeMB: 1024, + }).getInstance(getInstanceProps); + const template = Template.fromStack(lambda.stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + EphemeralStorage: { Size: 1024 }, + }); + }); + + void it('sets default ephemeralStorageSizeMB', () => { + const lambda = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + }).getInstance(getInstanceProps); + const template = Template.fromStack(lambda.stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + EphemeralStorage: { Size: 512 }, + }); + }); + + void it('throws on ephemeralStorageSizeMB below 512 MB', () => { + assert.throws( + () => + defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + ephemeralStorageSizeMB: 511, + }).getInstance(getInstanceProps), + new AmplifyUserError('InvalidEphemeralStorageSizeMBError', { + message: `Invalid function ephemeralStorageSizeMB of 511`, + resolution: `ephemeralStorageSizeMB must be a whole number between 512 and 10240 inclusive`, + }) + ); + }); + + void it('throws on ephemeralStorageSizeMB above 10240 MB', () => { + assert.throws( + () => + defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + ephemeralStorageSizeMB: 10241, + }).getInstance(getInstanceProps), + new AmplifyUserError('InvalidEphemeralStorageSizeMBError', { + message: `Invalid function ephemeralStorageSizeMB of 10241`, + resolution: `ephemeralStorageSizeMB must be a whole number between 512 and 10240 inclusive`, + }) + ); + }); + + void it('throws on fractional ephemeralStorageSizeMB', () => { + assert.throws( + () => + defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + ephemeralStorageSizeMB: 512.5, + }).getInstance(getInstanceProps), + new AmplifyUserError('InvalidEphemeralStorageSizeMBError', { + message: `Invalid function ephemeralStorageSizeMB of 512.5`, + resolution: `ephemeralStorageSizeMB must be a whole number between 512 and 10240 inclusive`, + }) + ); + }); + }); }); diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index e0c8abb4a49..7f2f04b0815 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -26,7 +26,7 @@ import { SsmEnvironmentEntry, StackProvider, } from '@aws-amplify/plugin-types'; -import { Duration, Stack, Tags } from 'aws-cdk-lib'; +import { Duration, Size, Stack, Tags } from 'aws-cdk-lib'; import { Rule } from 'aws-cdk-lib/aws-events'; import * as targets from 'aws-cdk-lib/aws-events-targets'; import { Policy } from 'aws-cdk-lib/aws-iam'; @@ -115,6 +115,13 @@ export type FunctionProps = { */ memoryMB?: number; + /** + * The size of the function's /tmp directory in MB. + * Must be a whole number. + * @default 512 + */ + ephemeralStorageSizeMB?: number; + /** * Environment variables that will be available during function execution */ @@ -236,6 +243,7 @@ class FunctionFactory implements ConstructFactory { entry: this.resolveEntry(), timeoutSeconds: this.resolveTimeout(), memoryMB: this.resolveMemory(), + ephemeralStorageSizeMB: this.resolveEphemeralStorageSize(), environment: this.resolveEnvironment(), runtime: this.resolveRuntime(), schedule: this.resolveSchedule(), @@ -324,6 +332,28 @@ class FunctionFactory implements ConstructFactory { return this.props.memoryMB; }; + private resolveEphemeralStorageSize = () => { + const ephemeralStorageSizeMin = 512; + const ephemeralStorageSizeMax = 10240; + const ephemeralStorageSizeDefault = 512; + if (this.props.ephemeralStorageSizeMB === undefined) { + return ephemeralStorageSizeDefault; + } + if ( + !isWholeNumberBetweenInclusive( + this.props.ephemeralStorageSizeMB, + ephemeralStorageSizeMin, + ephemeralStorageSizeMax + ) + ) { + throw new AmplifyUserError('InvalidEphemeralStorageSizeMBError', { + message: `Invalid function ephemeralStorageSizeMB of ${this.props.ephemeralStorageSizeMB}`, + resolution: `ephemeralStorageSizeMB must be a whole number between ${ephemeralStorageSizeMin} and ${ephemeralStorageSizeMax} inclusive`, + }); + } + return this.props.ephemeralStorageSizeMB; + }; + private resolveEnvironment = () => { if (this.props.environment === undefined) { return {}; @@ -509,6 +539,7 @@ class AmplifyFunction entry: props.entry, timeout: Duration.seconds(props.timeoutSeconds), memorySize: props.memoryMB, + ephemeralStorageSize: Size.mebibytes(props.ephemeralStorageSizeMB), runtime: nodeVersionMap[props.runtime], layers: props.resolvedLayers, bundling: { From d47855964185905ba7f71bc7c495eac645fe5555 Mon Sep 17 00:00:00 2001 From: MURAKAMI Masahiko Date: Tue, 17 Dec 2024 00:18:58 +0900 Subject: [PATCH 161/199] test: fix a incorrect the usage of the assert function (#2109) improve to clear require cache. --- .changeset/afraid-swans-rescue.md | 2 ++ .../src/usage-data/get_usage_data_url.test.ts | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 .changeset/afraid-swans-rescue.md diff --git a/.changeset/afraid-swans-rescue.md b/.changeset/afraid-swans-rescue.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/afraid-swans-rescue.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/platform-core/src/usage-data/get_usage_data_url.test.ts b/packages/platform-core/src/usage-data/get_usage_data_url.test.ts index 9c55985dcc2..e7c44a13b77 100644 --- a/packages/platform-core/src/usage-data/get_usage_data_url.test.ts +++ b/packages/platform-core/src/usage-data/get_usage_data_url.test.ts @@ -1,18 +1,26 @@ import { afterEach, describe, test } from 'node:test'; import assert from 'node:assert'; -import { getUrl } from './get_usage_data_url'; +import url from 'node:url'; void describe('getUrl', () => { afterEach(() => { delete process.env.AMPLIFY_BACKEND_USAGE_TRACKING_ENDPOINT; + delete require.cache[require.resolve('./get_usage_data_url')]; }); void test('that prod URL is returned when the env for beta URL is not set', () => { - assert(getUrl(), 'https://api.cli.amplify.aws/v1.0/metrics'); + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { getUrl } = require('./get_usage_data_url'); + assert.equal( + url.format(getUrl()), + 'https://api.cli.amplify.aws/v1.0/metrics' + ); }); void test('that BETA URL is returned when the env for beta URL is set', () => { process.env.AMPLIFY_BACKEND_USAGE_TRACKING_ENDPOINT = 'https://aws.amazon.com/amplify/'; - assert(getUrl(), 'https://aws.amazon.com/amplify/'); + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { getUrl } = require('./get_usage_data_url'); + assert.equal(url.format(getUrl()), 'https://aws.amazon.com/amplify/'); }); }); From 9fee057f09574faa590a18c97dbb8bb3d6b26089 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 16 Dec 2024 09:30:26 -0800 Subject: [PATCH 162/199] delete domain (#2336) --- scripts/cleanup_e2e_resources.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scripts/cleanup_e2e_resources.ts b/scripts/cleanup_e2e_resources.ts index 97463ce7440..8a2771bc164 100644 --- a/scripts/cleanup_e2e_resources.ts +++ b/scripts/cleanup_e2e_resources.ts @@ -27,6 +27,8 @@ import { import { CognitoIdentityProviderClient, DeleteUserPoolCommand, + DeleteUserPoolDomainCommand, + DescribeUserPoolCommand, ListUserPoolsCommand, ListUserPoolsCommandOutput, UserPoolDescriptionType, @@ -296,6 +298,19 @@ const staleUserPools = await listStaleCognitoUserPools(); for (const staleUserPool of staleUserPools) { if (staleUserPool.Name) { try { + const describeUserPoolResponse = await cognitoClient.send( + new DescribeUserPoolCommand({ + UserPoolId: staleUserPool.Id, + }) + ); + if (describeUserPoolResponse.UserPool?.Domain) { + await cognitoClient.send( + new DeleteUserPoolDomainCommand({ + UserPoolId: describeUserPoolResponse.UserPool.Id, + Domain: describeUserPoolResponse.UserPool?.Domain, + }) + ); + } await cognitoClient.send( new DeleteUserPoolCommand({ UserPoolId: staleUserPool.Id, From 1400e6f65cf8e156b8aaeddc4cb37311a37a3fc3 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Mon, 16 Dec 2024 15:56:30 -0800 Subject: [PATCH 163/199] Test latest cdk deeply. (#2333) * parameter * try this * try this * try this * try this * try this * try this * boop * does this work ? * try this * Comment. * try this * try this * this * Revert "this" This reverts commit 94939555559e9d7edc303fca53687b4d52a5d323. * this * this * this * more * try this * try this * try this * try this * try this * try this * try this * try this * try this * try this * try this * try this * try this * try this * try this * try this * comment * make node version required * fix that * fix that * try this * try this * try this * fix that * remove from canaries * that * that * that * try this --- .changeset/good-jokes-eat.md | 2 + .github/actions/build_with_cache/action.yml | 23 +- .github/actions/install_with_cache/action.yml | 28 +- .../actions/restore_build_cache/action.yml | 23 +- .../actions/restore_install_cache/action.yml | 20 +- .../actions/run_with_e2e_account/action.yml | 10 +- .../actions/setup_baseline_version/action.yml | 6 + .github/actions/setup_node/action.yml | 12 +- .github/workflows/canary_checks.yml | 7 +- .github/workflows/deprecate_release.yml | 10 + .github/workflows/e2e_resource_cleanup.yml | 5 + .github/workflows/health_checks.yml | 288 ++++++++++++++++-- .github/workflows/restore_release.yml | 10 + .github/workflows/snapshot_release.yml | 20 ++ .github/workflows/validate_cdk_release.yml | 30 ++ package-lock.json | 1 + packages/integration-tests/package.json | 1 + .../predicated_action_queue_builder.ts | 2 + scripts/generate_sparse_test_matrix.ts | 20 +- 19 files changed, 482 insertions(+), 36 deletions(-) create mode 100644 .changeset/good-jokes-eat.md create mode 100644 .github/workflows/validate_cdk_release.yml diff --git a/.changeset/good-jokes-eat.md b/.changeset/good-jokes-eat.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/good-jokes-eat.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/actions/build_with_cache/action.yml b/.github/actions/build_with_cache/action.yml index cbe1e5a5da8..6e93905d1e1 100644 --- a/.github/actions/build_with_cache/action.yml +++ b/.github/actions/build_with_cache/action.yml @@ -2,17 +2,36 @@ name: build_with_cache description: builds the source code if cache miss and caches the result inputs: node-version: - default: 18 + required: true + cdk-version: + required: true runs: using: composite steps: + # Validate that non-blank inputs are provided. + # This is to ensure that inputs are plumbed and not defaulted accidentally in action call chains. + # The 'required' input property does not assert this if value is provided at runtime. + - name: Validate input + shell: bash + run: | + if [ -z "${{ inputs.cdk-version }}" ]; then + echo "CDK version must be provided" + exit 1; + fi + if [ -z "${{ inputs.node-version }}" ]; then + echo "Node version must be provided" + exit 1; + fi - uses: ./.github/actions/install_with_cache + with: + node-version: ${{ inputs.node-version }} + cdk-version: ${{ inputs.cdk-version }} # cache build output based on commit sha - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # version 4.0.2 id: build-cache with: path: '**/lib' - key: ${{ github.sha }}-node${{ inputs.node-version }} + key: ${{ github.sha }}-node${{ inputs.node-version }}-cdk${{ inputs.cdk-version }} enableCrossOsArchive: true # only build if cache miss - if: steps.build-cache.outputs.cache-hit != 'true' diff --git a/.github/actions/install_with_cache/action.yml b/.github/actions/install_with_cache/action.yml index e8eedf39c1a..cf2549fbb06 100644 --- a/.github/actions/install_with_cache/action.yml +++ b/.github/actions/install_with_cache/action.yml @@ -2,10 +2,26 @@ name: install_with_cache description: installs node_modules if cache miss and stores in the cache inputs: node-version: - default: 18 + required: true + cdk-version: + required: true runs: using: composite steps: + # Validate that non-blank inputs are provided. + # This is to ensure that inputs are plumbed and not defaulted accidentally in action call chains. + # The 'required' input property does not assert this if value is provided at runtime. + - name: Validate input + shell: bash + run: | + if [ -z "${{ inputs.cdk-version }}" ]; then + echo "CDK version must be provided" + exit 1; + fi + if [ -z "${{ inputs.node-version }}" ]; then + echo "Node version must be provided" + exit 1; + fi # cache node_modules based on package-lock.json hash - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # version 4.0.2 id: npm-cache @@ -13,8 +29,14 @@ runs: path: | node_modules packages/**/node_modules - key: ${{ runner.os }}-${{ hashFiles('package-lock.json') }}-node${{ inputs.node-version }} + key: ${{ runner.os }}-${{ hashFiles('package-lock.json') }}-node${{ inputs.node-version }}-cdk${{ inputs.cdk-version }} # only install if cache miss - if: steps.npm-cache.outputs.cache-hit != 'true' shell: bash - run: npm ci + run: | + npm ci + if [[ ${{ inputs.cdk-version }} != 'FROM_PACKAGE_LOCK' ]]; then + echo "Installing CDK version ${{ inputs.cdk-version }}" + npm install --no-save aws-cdk@${{ inputs.cdk-version }} aws-cdk-lib@${{ inputs.cdk-version }} + npx cdk --version + fi diff --git a/.github/actions/restore_build_cache/action.yml b/.github/actions/restore_build_cache/action.yml index 83adef08346..27785b4789e 100644 --- a/.github/actions/restore_build_cache/action.yml +++ b/.github/actions/restore_build_cache/action.yml @@ -3,16 +3,35 @@ description: composes restoring node_modules and restoring build artifacts inputs: node-version: description: node version used to configure environment with - default: 18 + required: true + cdk-version: + required: true runs: using: composite steps: + # Validate that non-blank inputs are provided. + # This is to ensure that inputs are plumbed and not defaulted accidentally in action call chains. + # The 'required' input property does not assert this if value is provided at runtime. + - name: Validate input + shell: bash + run: | + if [ -z "${{ inputs.cdk-version }}" ]; then + echo "CDK version must be provided" + exit 1; + fi + if [ -z "${{ inputs.node-version }}" ]; then + echo "Node version must be provided" + exit 1; + fi - uses: ./.github/actions/restore_install_cache + with: + node-version: ${{ inputs.node-version }} + cdk-version: ${{ inputs.cdk-version }} # restore build output from cache - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # version 4.0.2 id: build-cache with: path: '**/lib' - key: ${{ github.sha }}-node${{ inputs.node-version }} + key: ${{ github.sha }}-node${{ inputs.node-version }}-cdk${{ inputs.cdk-version }} fail-on-cache-miss: true enableCrossOsArchive: true diff --git a/.github/actions/restore_install_cache/action.yml b/.github/actions/restore_install_cache/action.yml index a6141cf65c1..e070d63c808 100644 --- a/.github/actions/restore_install_cache/action.yml +++ b/.github/actions/restore_install_cache/action.yml @@ -3,10 +3,26 @@ description: restores node_modules from the cache and fails if no cache entry fo inputs: node-version: description: node version used to configure environment with - default: 18 + required: true + cdk-version: + required: true runs: using: composite steps: + # Validate that non-blank inputs are provided. + # This is to ensure that inputs are plumbed and not defaulted accidentally in action call chains. + # The 'required' input property does not assert this if value is provided at runtime. + - name: Validate input + shell: bash + run: | + if [ -z "${{ inputs.cdk-version }}" ]; then + echo "CDK version must be provided" + exit 1; + fi + if [ -z "${{ inputs.node-version }}" ]; then + echo "Node version must be provided" + exit 1; + fi # restore node_modules from cache - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # version 4.0.2 id: npm-cache @@ -14,5 +30,5 @@ runs: path: | node_modules packages/**/node_modules - key: ${{ runner.os }}-${{ hashFiles('package-lock.json') }}-node${{ inputs.node-version }} + key: ${{ runner.os }}-${{ hashFiles('package-lock.json') }}-node${{ inputs.node-version }}-cdk${{ inputs.cdk-version }} fail-on-cache-miss: true diff --git a/.github/actions/run_with_e2e_account/action.yml b/.github/actions/run_with_e2e_account/action.yml index 5af2303bdc1..0a170e433ef 100644 --- a/.github/actions/run_with_e2e_account/action.yml +++ b/.github/actions/run_with_e2e_account/action.yml @@ -9,7 +9,7 @@ inputs: required: false node_version: description: node version used to configure environment with - required: false + required: true e2e_test_accounts: description: Serialized JSON array of strings with account numbers required: true @@ -22,6 +22,8 @@ inputs: fresh_build: description: Whether should build from scratch default: false + cdk-version: + required: true runs: using: composite steps: @@ -32,9 +34,15 @@ runs: - name: Restore Build Cache if: inputs.fresh_build != 'true' uses: ./.github/actions/restore_build_cache + with: + cdk-version: ${{ inputs.cdk-version }} + node-version: ${{ inputs.node_version }} - name: Build With Cache if: inputs.fresh_build == 'true' uses: ./.github/actions/build_with_cache + with: + cdk-version: ${{ inputs.cdk-version }} + node-version: ${{ inputs.node_version }} - name: Link CLI if: inputs.link_cli == 'true' shell: bash diff --git a/.github/actions/setup_baseline_version/action.yml b/.github/actions/setup_baseline_version/action.yml index 1594ab6dde6..7091cbd5190 100644 --- a/.github/actions/setup_baseline_version/action.yml +++ b/.github/actions/setup_baseline_version/action.yml @@ -1,5 +1,9 @@ name: setup_baseline_version description: Set up a baseline or "previous" version of the library for testing. Mostly useful for backwards compatibility +inputs: + node_version: + description: node version used to configure environment with + required: true outputs: baseline_dir: description: 'Path where baseline project directory is setup' @@ -35,6 +39,8 @@ runs: with: ref: ${{ steps.get_baseline_commit_sha.outputs.baseline_commit_sha }} - uses: ./.github/actions/setup_node + with: + node-version: ${{ inputs.node_version }} - name: Install and build baseline version shell: bash run: | diff --git a/.github/actions/setup_node/action.yml b/.github/actions/setup_node/action.yml index 8f407ee4370..d8a794f9b4c 100644 --- a/.github/actions/setup_node/action.yml +++ b/.github/actions/setup_node/action.yml @@ -4,10 +4,20 @@ name: setup_node inputs: node-version: description: node version used to configure environment with - default: 18 + required: true runs: using: composite steps: + # Validate that non-blank inputs are provided. + # This is to ensure that inputs are plumbed and not defaulted accidentally in action call chains. + # The 'required' input property does not assert this if value is provided at runtime. + - name: Validate input + shell: bash + run: | + if [ -z "${{ inputs.node-version }}" ]; then + echo "Node version must be provided" + exit 1; + fi - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # version 4.0.2 with: node-version: ${{ inputs.node-version }} diff --git a/.github/workflows/canary_checks.yml b/.github/workflows/canary_checks.yml index e534fe29c1f..af08fd3592b 100644 --- a/.github/workflows/canary_checks.yml +++ b/.github/workflows/canary_checks.yml @@ -11,6 +11,8 @@ jobs: steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - name: Install and build without lock file shell: bash run: | @@ -46,7 +48,10 @@ jobs: uses: ./.github/actions/run_with_e2e_account with: e2e_test_accounts: ${{ vars.E2E_TEST_ACCOUNTS }} - node_version: ${{ matrix.node-version }} + node_version: 18 + # Use version from package lock. Tests projects are created outside of repository root + # and are using latest CDK version. + cdk-version: FROM_PACKAGE_LOCK aws_region: ${{ matrix.region }} fresh_build: true shell: bash diff --git a/.github/workflows/deprecate_release.yml b/.github/workflows/deprecate_release.yml index 83bc4e69c73..f618695cad9 100644 --- a/.github/workflows/deprecate_release.yml +++ b/.github/workflows/deprecate_release.yml @@ -28,7 +28,12 @@ jobs: steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/install_with_cache + with: + node-version: 18 + cdk-version: FROM_PACKAGE_LOCK deprecate_release: needs: - install @@ -47,6 +52,11 @@ jobs: # fetch full history so that we can properly lookup past releases fetch-depth: 0 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_install_cache + with: + node-version: 18 + cdk-version: FROM_PACKAGE_LOCK - name: Deprecate release versions run: npx tsx scripts/deprecate_release.ts diff --git a/.github/workflows/e2e_resource_cleanup.yml b/.github/workflows/e2e_resource_cleanup.yml index ff3e7554b7e..406ac3981e7 100644 --- a/.github/workflows/e2e_resource_cleanup.yml +++ b/.github/workflows/e2e_resource_cleanup.yml @@ -24,7 +24,12 @@ jobs: steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/install_with_cache + with: + node-version: 18 + cdk-version: FROM_PACKAGE_LOCK - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # version 4.0.2 with: diff --git a/.github/workflows/health_checks.yml b/.github/workflows/health_checks.yml index d00ef3e5144..22bc67ab861 100644 --- a/.github/workflows/health_checks.yml +++ b/.github/workflows/health_checks.yml @@ -11,13 +11,74 @@ on: - hotfix - feature/** schedule: - # Every day at At minute 0 past hour 0, 6, 12, and 18 UTC. + # Every day at minute 0 past hour 0, 6, 12, and 18 UTC. # This is to make sure that there is at least one workflow run every 24 hours # taking into account that # 1) scheduled runs may not fire at exact prescribed time; # 2) transient failures may happen and auto recover; - cron: '0 0,6,12,18 * * *' workflow_dispatch: + inputs: + desired-cdk-version: + description: 'AWS CDK version (exact or tag). Defaults to package-locked version.' + required: false + type: string + include-package-manager-e2e-tests: + description: 'Include package manager e2e tests?' + required: false + type: boolean + default: true + include-create-amplify-e2e-tests: + description: 'Include create-amplify e2e tests?' + required: false + type: boolean + default: true + include-macos: + description: 'Include MacOS?' + required: false + type: boolean + default: true + include-windows: + description: 'Include Windows?' + required: false + type: boolean + default: true + node: + description: 'Node versions list (as JSON array).' + required: false + type: string + default: '["18", "20"]' + workflow_call: + inputs: + desired-cdk-version: + description: 'AWS CDK version (exact or tag). Defaults to package-locked version.' + required: false + type: string + include-package-manager-e2e-tests: + description: 'Include package manager e2e tests?' + required: false + type: boolean + default: true + include-create-amplify-e2e-tests: + description: 'Include create-amplify e2e tests?' + required: false + type: boolean + default: true + include-macos: + description: 'Include MacOS?' + required: false + type: boolean + default: true + include-windows: + description: 'Include Windows?' + required: false + type: boolean + default: true + node: + description: 'Node versions list (as JSON array).' + required: false + type: string + default: '["18", "20"]' env: # Health checks can run on un-released code. Often work in progress. @@ -25,6 +86,49 @@ env: AMPLIFY_DISABLE_TELEMETRY: true jobs: + # This workflow may be called by variety of events. + # This steps resolves and applies appropriate defaults depending on the trigger. + resolve_inputs: + runs-on: ubuntu-latest + outputs: + cdk_version: ${{ steps.resolve_inputs.outputs.cdk_version }} + os: ${{ steps.resolve_inputs.outputs.os }} + os_for_e2e: ${{ steps.resolve_inputs.outputs.os_for_e2e }} + node: ${{ steps.resolve_inputs.outputs.node }} + steps: + - name: Resolve Inputs + id: resolve_inputs + # This is intentionally in pure bash to make this job independent of repo checkout and fast. + run: | + if [ -z "${{ inputs.desired-cdk-version }}" ]; then + echo "cdk_version=FROM_PACKAGE_LOCK" >> "$GITHUB_OUTPUT" + else + echo "cdk_version=$(npm view aws-cdk@${{ inputs.desired-cdk-version }} version)" >> "$GITHUB_OUTPUT" + fi + # Build JSON array in readable way in bash... + os='["ubuntu-latest"' + os_for_e2e='["ubuntu-latest"' + if [ "${{ inputs.include-macos }}" != "false" ]; then + os+=', "macos-14"' + os_for_e2e+=', "macos-14-xlarge"' + fi + if [ "${{ inputs.include-windows }}" != "false" ]; then + os+=', "windows-latest"' + os_for_e2e+=', "windows-latest"' + fi + os+=']' + os_for_e2e+=']' + echo "os=$os" >> "$GITHUB_OUTPUT" + echo "os_for_e2e=$os_for_e2e" >> "$GITHUB_OUTPUT" + if [ -z "${{ inputs.node }}" ]; then + echo 'node=["18", "20"]' >> "$GITHUB_OUTPUT" + else + echo 'node=${{ inputs.node }}' >> "$GITHUB_OUTPUT" + fi + - run: echo cdk_version set to ${{ steps.resolve_inputs.outputs.cdk_version }} + - run: echo os set to ${{ steps.resolve_inputs.outputs.os }} + - run: echo os_for_e2e set to ${{ steps.resolve_inputs.outputs.os_for_e2e }} + - run: echo node set to ${{ steps.resolve_inputs.outputs.node }} install: strategy: matrix: @@ -32,9 +136,15 @@ jobs: # Larger workers use different drive (C: instead of D:) to check out project and NPM installation # creates file system links that include drive letter. # Changing between standard and custom workers requires full install cache invalidation - os: [ubuntu-latest, macos-14, windows-latest] - node: [18, 20] + os: ${{ fromJSON(needs.resolve_inputs.outputs.os) }} + node: ${{ fromJSON(needs.resolve_inputs.outputs.node) }} + # Always include Node 18 and Ubuntu. Non-testing jobs depend on it. + include: + - os: ubuntu-latest + node: 18 runs-on: ${{ matrix.os }} + needs: + - resolve_inputs timeout-minutes: 10 steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 @@ -44,13 +154,18 @@ jobs: - uses: ./.github/actions/install_with_cache with: node-version: ${{ matrix.node }} + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} build: strategy: matrix: - node: [18, 20] + node: ${{ fromJSON(needs.resolve_inputs.outputs.node) }} + # Always include Node 18. Non-testing jobs depend on it. + include: + - node: 18 runs-on: ubuntu-latest needs: - install + - resolve_inputs steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node @@ -59,13 +174,15 @@ jobs: - uses: ./.github/actions/build_with_cache with: node-version: ${{ matrix.node }} + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} test_with_coverage: needs: - build + - resolve_inputs strategy: matrix: - os: [ubuntu-latest, macos-14, windows-latest] - node: [18, 20] + os: ${{ fromJSON(needs.resolve_inputs.outputs.os) }} + node: ${{ fromJSON(needs.resolve_inputs.outputs.node) }} runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 @@ -75,27 +192,40 @@ jobs: - uses: ./.github/actions/restore_build_cache with: node-version: ${{ matrix.node }} + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - run: npm run set-script-shell - run: npm run test:coverage:threshold test_scripts: needs: - build + - resolve_inputs runs-on: ubuntu-latest steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_build_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - run: | npm run set-script-shell npm run test:scripts test_with_baseline_dependencies: needs: - install + - resolve_inputs runs-on: ubuntu-latest steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_install_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - name: Pin some dependencies to nearest patch and rebuild run: | npx tsx scripts/set_baseline_dependency_versions.ts @@ -111,13 +241,19 @@ jobs: if: github.event_name == 'pull_request' needs: - build + - resolve_inputs runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Checkout pull request ref uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_build_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - name: Publish packages locally timeout-minutes: 2 run: | @@ -136,6 +272,7 @@ jobs: do_include_e2e: needs: - install + - resolve_inputs runs-on: ubuntu-latest permissions: # This is required so that the step can read the labels on the pull request @@ -145,14 +282,39 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} outputs: run_e2e: ${{ steps.check.outputs.run_e2e }} + include_package_manager_e2e: ${{ steps.check_package_manager.outputs.include_package_manager_e2e }} + include_create_amplify_e2e: ${{ steps.check_create_amplify.outputs.include_create_amplify_e2e }} steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_install_cache - - name: Check if E2E tests should run + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} + - name: Check if any E2E tests should run id: check run: echo "run_e2e=$(npx tsx scripts/do_include_e2e.ts)" >> "$GITHUB_OUTPUT" - run: echo run_e2e set to ${{ steps.check.outputs.run_e2e }} + - name: Check if Package Manager E2E tests should be included + id: check_package_manager + run: | + if [ -z "${{ inputs.include-package-manager-e2e-tests }}" ]; then + echo "include_package_manager_e2e=true" >> "$GITHUB_OUTPUT" + else + echo "include_package_manager_e2e=${{ inputs.include-package-manager-e2e-tests }}" >> "$GITHUB_OUTPUT" + fi + - run: echo include_package_manager_e2e set to ${{ steps.check_package_manager.outputs.include_package_manager_e2e }} + - name: Check if Create Amplify E2E tests should be included + id: check_create_amplify + run: | + if [ -z "${{ inputs.include-create-amplify-e2e-tests }}" ]; then + echo "include_create_amplify_e2e=true" >> "$GITHUB_OUTPUT" + else + echo "include_create_amplify_e2e=${{ inputs.include-create-amplify-e2e-tests }}" >> "$GITHUB_OUTPUT" + fi + - run: echo include_create_amplify_e2e set to ${{ steps.check_create_amplify.outputs.include_create_amplify_e2e }} e2e_iam_access_drift: if: needs.do_include_e2e.outputs.run_e2e == 'true' runs-on: ubuntu-latest @@ -160,6 +322,7 @@ jobs: needs: - do_include_e2e - build + - resolve_inputs permissions: # these permissions are required for the configure-aws-credentials action to get a JWT from GitHub id-token: write @@ -172,13 +335,16 @@ jobs: - name: Setup baseline version uses: ./.github/actions/setup_baseline_version id: setup_baseline_version + with: + node_version: 18 - name: Checkout current version uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - name: Run e2e iam access drift test uses: ./.github/actions/run_with_e2e_account with: e2e_test_accounts: ${{ vars.E2E_TEST_ACCOUNTS }} - node_version: ${{ matrix.node-version }} + node_version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} run: npm run test:dir packages/integration-tests/lib/test-e2e/iam_access_drift.test.js env: BASELINE_DIR: ${{ steps.setup_baseline_version.outputs.baseline_dir }} @@ -189,6 +355,7 @@ jobs: needs: - do_include_e2e - build + - resolve_inputs permissions: # these permissions are required for the configure-aws-credentials action to get a JWT from GitHub id-token: write @@ -201,13 +368,16 @@ jobs: - name: Setup baseline version uses: ./.github/actions/setup_baseline_version id: setup_baseline_version + with: + node_version: 18 - name: Checkout current version uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - name: Run e2e amplify outputs backwards compatibility test uses: ./.github/actions/run_with_e2e_account with: e2e_test_accounts: ${{ vars.E2E_TEST_ACCOUNTS }} - node_version: ${{ matrix.node-version }} + node_version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} run: npm run test:dir packages/integration-tests/lib/test-e2e/amplify_outputs_backwards_compatibility.test.js env: BASELINE_DIR: ${{ steps.setup_baseline_version.outputs.baseline_dir }} @@ -220,12 +390,16 @@ jobs: needs: - do_include_e2e - build + - resolve_inputs steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/restore_build_cache - - run: echo "$(npx tsx scripts/generate_sparse_test_matrix.ts 'packages/integration-tests/lib/test-e2e/deployment/*.deployment.test.js')" + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} + - run: echo "$(npx tsx scripts/generate_sparse_test_matrix.ts 'packages/integration-tests/lib/test-e2e/deployment/*.deployment.test.js' '${{ needs.resolve_inputs.outputs.node }}' '${{ needs.resolve_inputs.outputs.os_for_e2e }}')" - id: generateMatrix - run: echo "matrix=$(npx tsx scripts/generate_sparse_test_matrix.ts 'packages/integration-tests/lib/test-e2e/deployment/*.deployment.test.js')" >> "$GITHUB_OUTPUT" + run: echo "matrix=$(npx tsx scripts/generate_sparse_test_matrix.ts 'packages/integration-tests/lib/test-e2e/deployment/*.deployment.test.js' '${{ needs.resolve_inputs.outputs.node }}' '${{ needs.resolve_inputs.outputs.os_for_e2e }}')" >> "$GITHUB_OUTPUT" e2e_deployment: if: needs.do_include_e2e.outputs.run_e2e == 'true' strategy: @@ -239,6 +413,7 @@ jobs: - do_include_e2e - build - e2e_generate_deployment_tests_matrix + - resolve_inputs permissions: # these permissions are required for the configure-aws-credentials action to get a JWT from GitHub id-token: write @@ -250,6 +425,7 @@ jobs: with: e2e_test_accounts: ${{ vars.E2E_TEST_ACCOUNTS }} node_version: ${{ matrix.node-version }} + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} link_cli: true run: | npm run test:dir ${{ matrix.testPaths }} @@ -262,12 +438,16 @@ jobs: needs: - do_include_e2e - build + - resolve_inputs steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/restore_build_cache - - run: echo "$(npx tsx scripts/generate_sparse_test_matrix.ts 'packages/integration-tests/lib/test-e2e/sandbox/*.sandbox.test.js')" + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} + - run: echo "$(npx tsx scripts/generate_sparse_test_matrix.ts 'packages/integration-tests/lib/test-e2e/sandbox/*.sandbox.test.js' '${{ needs.resolve_inputs.outputs.node }}' '${{ needs.resolve_inputs.outputs.os_for_e2e }}')" - id: generateMatrix - run: echo "matrix=$(npx tsx scripts/generate_sparse_test_matrix.ts 'packages/integration-tests/lib/test-e2e/sandbox/*.sandbox.test.js')" >> "$GITHUB_OUTPUT" + run: echo "matrix=$(npx tsx scripts/generate_sparse_test_matrix.ts 'packages/integration-tests/lib/test-e2e/sandbox/*.sandbox.test.js' '${{ needs.resolve_inputs.outputs.node }}' '${{ needs.resolve_inputs.outputs.os_for_e2e }}')" >> "$GITHUB_OUTPUT" e2e_sandbox: if: needs.do_include_e2e.outputs.run_e2e == 'true' strategy: @@ -281,6 +461,7 @@ jobs: - do_include_e2e - build - e2e_generate_sandbox_tests_matrix + - resolve_inputs permissions: # these permissions are required for the configure-aws-credentials action to get a JWT from GitHub id-token: write @@ -292,6 +473,7 @@ jobs: with: e2e_test_accounts: ${{ vars.E2E_TEST_ACCOUNTS }} node_version: ${{ matrix.node-version }} + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} link_cli: true run: npm run test:dir ${{ matrix.testPaths }} e2e_backend_output: @@ -301,6 +483,7 @@ jobs: needs: - do_include_e2e - build + - resolve_inputs permissions: # these permissions are required for the configure-aws-credentials action to get a JWT from GitHub id-token: write @@ -311,17 +494,18 @@ jobs: uses: ./.github/actions/run_with_e2e_account with: e2e_test_accounts: ${{ vars.E2E_TEST_ACCOUNTS }} - node_version: ${{ matrix.node-version }} + node_version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} link_cli: true run: npm run test:dir packages/integration-tests/lib/test-e2e/backend_output.test.js e2e_create_amplify: - if: needs.do_include_e2e.outputs.run_e2e == 'true' + if: needs.do_include_e2e.outputs.run_e2e == 'true' && needs.do_include_e2e.outputs.include_create_amplify_e2e == 'true' strategy: # will finish running other test matrices even if one fails fail-fast: false matrix: - os: [ubuntu-latest, macos-14, windows-latest] - node-version: [18, 20] + os: ${{ fromJSON(needs.resolve_inputs.outputs.os) }} + node-version: ${{ fromJSON(needs.resolve_inputs.outputs.node) }} # skip multiple node version test on other os exclude: - os: macos-14 @@ -333,22 +517,26 @@ jobs: needs: - do_include_e2e - build + - resolve_inputs steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node with: node-version: ${{ matrix.node-version }} - uses: ./.github/actions/restore_build_cache + with: + node-version: ${{ matrix.node-version }} + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - run: cd packages/cli && npm link - name: Run e2e create-amplify tests run: npm run test:dir packages/integration-tests/lib/test-e2e/create_amplify.test.js e2e_package_manager: - if: needs.do_include_e2e.outputs.run_e2e == 'true' + if: needs.do_include_e2e.outputs.run_e2e == 'true' && needs.do_include_e2e.outputs.include_package_manager_e2e == 'true' strategy: # will finish running other test matrices even if one fails fail-fast: false matrix: - os: [ubuntu-latest, macos-14, windows-latest] + os: ${{ fromJSON(needs.resolve_inputs.outputs.os) }} pkg-manager: [npm, yarn-classic, yarn-modern, pnpm] node-version: ['20'] env: @@ -358,6 +546,7 @@ jobs: needs: - build - do_include_e2e + - resolve_inputs permissions: # these permissions are required for the configure-aws-credentials action to get a JWT from GitHub id-token: write @@ -370,6 +559,7 @@ jobs: with: e2e_test_accounts: ${{ vars.E2E_TEST_ACCOUNTS }} node_version: ${{ matrix.node-version }} + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} shell: bash run: | PACKAGE_MANAGER=${{matrix.pkg-manager}} npm run test:dir packages/integration-tests/src/package_manager_sanity_checks.test.ts @@ -377,46 +567,76 @@ jobs: runs-on: ubuntu-latest needs: - build + - resolve_inputs steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_build_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - run: npm run lint check_dependencies: runs-on: ubuntu-latest needs: - install + - resolve_inputs steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_install_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - run: npm run check:dependencies check_tsconfig_refs: runs-on: ubuntu-latest needs: - install + - resolve_inputs steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_install_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - run: npm run check:tsconfig-refs check_api_extract: runs-on: ubuntu-latest needs: - build + - resolve_inputs steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_build_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - run: npm run check:api docs_build_and_publish: runs-on: ubuntu-latest needs: - build + - resolve_inputs steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_build_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - run: npm run docs - if: ${{ github.event_name == 'push' && github.ref_name == 'main' }} uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # version 4.0.0 @@ -429,10 +649,16 @@ jobs: runs-on: ubuntu-latest needs: - install + - resolve_inputs steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_install_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - run: git fetch origin - run: npm run diff:check "$BASE_SHA" env: @@ -442,13 +668,19 @@ jobs: runs-on: ubuntu-latest needs: - install + - resolve_inputs steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 with: # fetch full history so that changeset can properly compute divergence point fetch-depth: 0 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_install_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - name: Validate that PR has changeset run: npx changeset status --since origin/"$BASE_REF" env: @@ -468,10 +700,16 @@ jobs: timeout-minutes: 10 needs: - install + - resolve_inputs steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_install_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - run: npx changeset version - run: npm run check:package-versions @@ -479,11 +717,17 @@ jobs: if: ${{ github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'hotfix') }} needs: - install + - resolve_inputs runs-on: ubuntu-latest steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_install_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - id: is_version_packages_commit run: echo "is_version_packages_commit=$(npx tsx scripts/is_version_packages_commit.ts)" >> "$GITHUB_OUTPUT" - name: Create or update Version Packages PR @@ -506,11 +750,17 @@ jobs: - e2e_deployment - e2e_sandbox - e2e_create_amplify + - resolve_inputs runs-on: ubuntu-latest steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_build_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - id: is_version_packages_commit run: echo "is_version_packages_commit=$(npx tsx scripts/is_version_packages_commit.ts)" >> "$GITHUB_OUTPUT" - name: Publish packages diff --git a/.github/workflows/restore_release.yml b/.github/workflows/restore_release.yml index 403c9d84a61..219b33692df 100644 --- a/.github/workflows/restore_release.yml +++ b/.github/workflows/restore_release.yml @@ -24,7 +24,12 @@ jobs: steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/install_with_cache + with: + node-version: 18 + cdk-version: FROM_PACKAGE_LOCK restore_release: needs: - install @@ -42,6 +47,11 @@ jobs: # fetch full history so that we can properly lookup past releases fetch-depth: 0 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_install_cache + with: + node-version: 18 + cdk-version: FROM_PACKAGE_LOCK - name: Restore release versions run: npx tsx scripts/restore_release.ts diff --git a/.github/workflows/snapshot_release.yml b/.github/workflows/snapshot_release.yml index 048c06de062..4f5b5a22e8a 100644 --- a/.github/workflows/snapshot_release.yml +++ b/.github/workflows/snapshot_release.yml @@ -9,7 +9,12 @@ jobs: steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/install_with_cache + with: + node-version: 18 + cdk-version: FROM_PACKAGE_LOCK build: runs-on: ubuntu-latest needs: @@ -17,7 +22,12 @@ jobs: steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/build_with_cache + with: + node-version: 18 + cdk-version: FROM_PACKAGE_LOCK test: needs: - build @@ -25,7 +35,12 @@ jobs: steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_build_cache + with: + node-version: 18 + cdk-version: FROM_PACKAGE_LOCK - run: npm run set-script-shell - run: npm run test publish_snapshot: @@ -35,7 +50,12 @@ jobs: steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 - uses: ./.github/actions/setup_node + with: + node-version: 18 - uses: ./.github/actions/restore_build_cache + with: + node-version: 18 + cdk-version: FROM_PACKAGE_LOCK - name: Authenticate run: | echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc diff --git a/.github/workflows/validate_cdk_release.yml b/.github/workflows/validate_cdk_release.yml new file mode 100644 index 00000000000..d54707ef0b2 --- /dev/null +++ b/.github/workflows/validate_cdk_release.yml @@ -0,0 +1,30 @@ +name: validate_cdk_release + +on: + schedule: + # Every day at 16:00 UTC (8:00 PST) and 18:00 UTC (10:00 PST) + # So that it produces at least 2 data points daily. + - cron: '0 16,18 * * *' + workflow_dispatch: + inputs: + desired-cdk-version: + description: 'AWS CDK version (exact or tag). Defaults to latest version.' + required: false + type: string + +jobs: + health_checks_with_cdk_version: + uses: ./.github/workflows/health_checks.yml + secrets: inherit + with: + # This runs all deployment and sandbox tests. + desired-cdk-version: ${{ inputs.desired-cdk-version || 'latest' }} + # Exclude additional runtimes. + # They don't bring much value, but may bring instability and extra latency. + include-macos: false + include-windows: false + node: '["18"]' + # Exclude package manager and create-amplify tests. + # They don't bring functional coverage for CDK usage patterns. + include-package-manager-e2e-tests: false + include-create-amplify-e2e-tests: false diff --git a/package-lock.json b/package-lock.json index c3fe5324670..21a75c93778 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32828,6 +32828,7 @@ "node-fetch": "^3.3.2", "semver": "^7.6.3", "ssh2": "^1.15.0", + "strip-ansi": "^6.0.1", "uuid": "^9.0.1" } }, diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 576363de098..2a60ce15446 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -43,6 +43,7 @@ "node-fetch": "^3.3.2", "semver": "^7.6.3", "ssh2": "^1.15.0", + "strip-ansi": "^6.0.1", "uuid": "^9.0.1" }, "license": "Apache-2.0" diff --git a/packages/integration-tests/src/process-controller/predicated_action_queue_builder.ts b/packages/integration-tests/src/process-controller/predicated_action_queue_builder.ts index 6b0e3572ab5..06429df8290 100644 --- a/packages/integration-tests/src/process-controller/predicated_action_queue_builder.ts +++ b/packages/integration-tests/src/process-controller/predicated_action_queue_builder.ts @@ -5,6 +5,7 @@ import { } from './predicated_action.js'; import os from 'os'; import fs from 'fs/promises'; +import stripANSI from 'strip-ansi'; import { killExecaProcess } from './execa_process_killer.js'; import { ExecaMethod } from 'execa'; @@ -92,6 +93,7 @@ export class PredicatedActionBuilder { action: (strWithDeploymentTime: string) => { // the time can be in fractional or whole seconds. 24.3, 24, 24.22 etc. const regex = /^✨ {2}Total time: (\d*\.*\d*)s.*$/; + strWithDeploymentTime = stripANSI(strWithDeploymentTime); const deploymentTime = strWithDeploymentTime.match(regex); if ( deploymentTime && diff --git a/scripts/generate_sparse_test_matrix.ts b/scripts/generate_sparse_test_matrix.ts index c5a76eb9654..0f2c9ffb541 100644 --- a/scripts/generate_sparse_test_matrix.ts +++ b/scripts/generate_sparse_test_matrix.ts @@ -4,14 +4,16 @@ import { SparseTestMatrixGenerator } from './components/sparse_test_matrix_gener // Every test must run on each type of OS and each version of node. // However, we don't have to run every combination. -if (process.argv.length < 3) { +if (process.argv.length < 5) { console.log( - "Usage: npx tsx scripts/generate_sparse_test_matrix.ts '' " + "Usage: npx tsx scripts/generate_sparse_test_matrix.ts '' '' '' " ); } const testGlobPattern = process.argv[2]; -const maxTestsPerJob = process.argv[3] ? parseInt(process.argv[3]) : 2; +const nodeVersions = JSON.parse(process.argv[3]) as Array; +let os = JSON.parse(process.argv[4]) as Array; +const maxTestsPerJob = process.argv[5] ? parseInt(process.argv[5]) : 2; if (!Number.isInteger(maxTestsPerJob)) { throw new Error( @@ -19,12 +21,20 @@ if (!Number.isInteger(maxTestsPerJob)) { ); } +os = os.map((entry) => { + if (entry === 'macos-14') { + // replace with large. + return 'macos-14-xlarge'; + } + return entry; +}); + const matrix = await new SparseTestMatrixGenerator({ testGlobPattern, maxTestsPerJob, dimensions: { - 'node-version': ['18', '20'], - os: ['ubuntu-latest', 'macos-14-xlarge', 'windows-latest'], + 'node-version': nodeVersions, + os, }, }).generate(); From f193105ec0e8e8f65cb2e3b4c85a182e6993471f Mon Sep 17 00:00:00 2001 From: "Aaron S." <94858815+stocaaro@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:35:52 -0600 Subject: [PATCH 164/199] fix: Lambda client env var name issue (#2324) --------- Co-authored-by: Kamil Sobol --- .changeset/warm-sloths-tickle.md | 7 + package-lock.json | 4 +- packages/backend-data/src/factory.ts | 33 ++- packages/backend-function/API.md | 8 +- .../get_amplify_clients_configuration.test.ts | 245 +++++++++++------- .../get_amplify_clients_configuration.ts | 97 +++++-- packages/backend/package.json | 3 +- ...coped_ssm_environment_entries_generator.ts | 10 +- .../engine/naming_convention_conversions.ts | 8 - .../amplify/data/resource.ts | 1 + packages/platform-core/API.md | 5 + packages/platform-core/package.json | 1 + packages/platform-core/src/index.ts | 1 + .../naming_convention_conversions.test.ts | 7 +- .../src/naming_convention_conversions.ts | 16 ++ 15 files changed, 296 insertions(+), 150 deletions(-) create mode 100644 .changeset/warm-sloths-tickle.md delete mode 100644 packages/backend/src/engine/naming_convention_conversions.ts rename packages/{backend/src/engine => platform-core/src}/naming_convention_conversions.test.ts (80%) create mode 100644 packages/platform-core/src/naming_convention_conversions.ts diff --git a/.changeset/warm-sloths-tickle.md b/.changeset/warm-sloths-tickle.md new file mode 100644 index 00000000000..d8806943394 --- /dev/null +++ b/.changeset/warm-sloths-tickle.md @@ -0,0 +1,7 @@ +--- +'@aws-amplify/backend-function': minor +'@aws-amplify/backend-data': patch +'@aws-amplify/platform-core': minor +--- + +Update getAmplifyDataClientConfig to work with named data backend diff --git a/package-lock.json b/package-lock.json index 21a75c93778..0b0f20b83f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31661,8 +31661,7 @@ "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/platform-core": "^1.3.0", "@aws-amplify/plugin-types": "^1.6.0", - "@aws-sdk/client-amplify": "^3.624.0", - "lodash.snakecase": "^4.1.1" + "@aws-sdk/client-amplify": "^3.624.0" }, "devDependencies": { "@types/aws-lambda": "^8.10.119", @@ -33003,6 +33002,7 @@ "@aws-sdk/client-sts": "^3.624.0", "is-ci": "^3.0.1", "lodash.mergewith": "^4.6.2", + "lodash.snakecase": "^4.1.1", "semver": "^7.6.3", "uuid": "^9.0.1", "zod": "^3.22.2" diff --git a/packages/backend-data/src/factory.ts b/packages/backend-data/src/factory.ts index 98d48b0667e..af2cfd97891 100644 --- a/packages/backend-data/src/factory.ts +++ b/packages/backend-data/src/factory.ts @@ -52,6 +52,7 @@ import { Bucket } from 'aws-cdk-lib/aws-s3'; import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment'; const modelIntrospectionSchemaKey = 'modelIntrospectionSchema.json'; +const defaultName = 'amplifyData'; /** * Singleton factory for AmplifyGraphqlApi constructs that can be used in Amplify project files. @@ -127,7 +128,7 @@ class DataGenerator implements ConstructContainerEntryGenerator { private readonly getInstanceProps: ConstructFactoryGetInstanceProps, private readonly outputStorageStrategy: BackendOutputStorageStrategy ) { - this.name = props.name ?? 'amplifyData'; + this.name = props.name ?? defaultName; } generateContainerEntry = ({ @@ -307,14 +308,32 @@ class DataGenerator implements ConstructContainerEntryGenerator { convertJsResolverDefinition(scope, amplifyApi, schemasJsFunctions); + const namePrefix = this.name === defaultName ? '' : defaultName; + + const ssmEnvironmentScopeContext = { + [`${namePrefix}${this.name}_GRAPHQL_ENDPOINT`]: + amplifyApi.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl, + [`${namePrefix}${this.name}_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME`]: + modelIntrospectionSchemaBucket.bucketName, + [`${namePrefix}${this.name}_MODEL_INTROSPECTION_SCHEMA_KEY`]: + modelIntrospectionSchemaKey, + ['AMPLIFY_DATA_DEFAULT_NAME']: `${namePrefix}${this.name}`, + }; + + const backwardsCompatibleScopeContext = + `${this.name}_GRAPHQL_ENDPOINT` !== + `${namePrefix}${this.name}_GRAPHQL_ENDPOINT` + ? { + // @deprecated + [`${this.name}_GRAPHQL_ENDPOINT`]: + amplifyApi.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl, + } + : {}; + const ssmEnvironmentEntries = ssmEnvironmentEntriesGenerator.generateSsmEnvironmentEntries({ - [`${this.name}_GRAPHQL_ENDPOINT`]: - amplifyApi.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl, - [`${this.name}_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME`]: - modelIntrospectionSchemaBucket.bucketName, - [`${this.name}_MODEL_INTROSPECTION_SCHEMA_KEY`]: - modelIntrospectionSchemaKey, + ...ssmEnvironmentScopeContext, + ...backwardsCompatibleScopeContext, }); const policyGenerator = new AppSyncPolicyGenerator( diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index 4c44cd115dd..d5453c7eb39 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -45,14 +45,12 @@ type DataClientConfig = { // @public (undocumented) type DataClientEnv = { - AMPLIFY_DATA_GRAPHQL_ENDPOINT: string; - AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME: string; - AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY: string; AWS_ACCESS_KEY_ID: string; AWS_SECRET_ACCESS_KEY: string; AWS_SESSION_TOKEN: string; AWS_REGION: string; -}; + AMPLIFY_DATA_DEFAULT_NAME: string; +} & Record; // @public (undocumented) type DataClientError = { @@ -111,7 +109,7 @@ const getAmplifyDataClientConfig: (env: T, s3Client?: S3Client) => Promise [allow.resource(fcn)])` on the data schema.'; + invalidType: 'Some of the AWS environment variables needed to configure Amplify are missing.'; }; // @public (undocumented) diff --git a/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts b/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts index e2e5d45407f..1287b11922d 100644 --- a/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts +++ b/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts @@ -4,7 +4,7 @@ import { NoSuchKey, S3, S3ServiceException } from '@aws-sdk/client-s3'; import { getAmplifyDataClientConfig } from './get_amplify_clients_configuration.js'; -const validEnv = { +const validDefaultEnv = { AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME: 'TEST_VALUE for AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME', AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY: @@ -14,6 +14,21 @@ const validEnv = { AWS_SESSION_TOKEN: 'TEST_VALUE for AWS_SESSION_TOKEN', AWS_REGION: 'TEST_VALUE for AWS_REGION', AMPLIFY_DATA_GRAPHQL_ENDPOINT: 'TEST_VALUE for AMPLIFY_DATA_GRAPHQL_ENDPOINT', + AMPLIFY_DATA_DEFAULT_NAME: 'AmplifyData', +}; + +const validNamedEnv = { + AMPLIFY_DATA_TEST_NAME_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME: + 'TEST_VALUE for AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME', + AMPLIFY_DATA_TEST_NAME_MODEL_INTROSPECTION_SCHEMA_KEY: + 'TEST_VALUE for AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY', + AWS_ACCESS_KEY_ID: 'TEST_VALUE for AWS_ACCESS_KEY_ID', + AWS_SECRET_ACCESS_KEY: 'TEST_VALUE for AWS_SECRET_ACCESS_KEY', + AWS_SESSION_TOKEN: 'TEST_VALUE for AWS_SESSION_TOKEN', + AWS_REGION: 'TEST_VALUE for AWS_REGION', + AMPLIFY_DATA_TEST_NAME_GRAPHQL_ENDPOINT: + 'TEST_VALUE for AMPLIFY_DATA_GRAPHQL_ENDPOINT', + AMPLIFY_DATA_DEFAULT_NAME: 'AmplifyDataTestName', }; let mockS3Client: S3; @@ -23,110 +38,146 @@ void describe('getAmplifyDataClientConfig', () => { mockS3Client = new S3(); }); - Object.keys(validEnv).forEach((envFieldToExclude) => { - void it(`returns empty config objects when ${envFieldToExclude} is not included`, async () => { - const env = { ...validEnv } as Record; - delete env[envFieldToExclude]; - assert.deepEqual(await getAmplifyDataClientConfig(env), { - resourceConfig: {}, - libraryOptions: {}, + [ + { + name: 'no set name', + dataBackendName: 'AMPLIFY_DATA', + validEnv: validDefaultEnv, + }, + { + name: 'an explicit name', + dataBackendName: 'AMPLIFY_DATA_TEST_NAME', + validEnv: validNamedEnv, + }, + ].forEach(({ name, dataBackendName, validEnv }) => { + void describe(`env variable with ${name} for the data backend`, () => { + Object.keys(validEnv) + .filter((k) => k !== 'AMPLIFY_DATA_DEFAULT_NAME') + .forEach((envFieldToExclude) => { + if (envFieldToExclude.includes(dataBackendName)) { + void it(`throws error when ${envFieldToExclude} is not included`, async () => { + const env = { ...validEnv } as Record; + delete env[envFieldToExclude]; + await assert.rejects( + async () => await getAmplifyDataClientConfig(env), + /The data environment variables are malformed/ + ); + }); + + void it(`throws error when ${envFieldToExclude} is not a string`, async () => { + const env = { ...validEnv } as Record; + env[envFieldToExclude] = 123; + await assert.rejects( + async () => await getAmplifyDataClientConfig(env), + /The data environment variables are malformed/ + ); + }); + } else { + void it(`returns empty config objects when ${envFieldToExclude} is not included`, async () => { + const env = { ...validEnv } as Record; + delete env[envFieldToExclude]; + assert.deepEqual(await getAmplifyDataClientConfig(env), { + resourceConfig: {}, + libraryOptions: {}, + }); + }); + + void it(`returns empty config objects when ${envFieldToExclude} is not a string`, async () => { + const env = { ...validEnv } as Record; + env[envFieldToExclude] = 123; + assert.deepEqual(await getAmplifyDataClientConfig(env), { + resourceConfig: {}, + libraryOptions: {}, + }); + }); + } + }); + + void it('raises a custom error message when the model introspection schema is missing from the s3 bucket', async () => { + const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { + throw new NoSuchKey({ message: 'TEST_ERROR', $metadata: {} }); + }); + mock.method(mockS3Client, 'send', s3ClientSendMock); + + await assert.rejects( + async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), + new Error( + 'Error retrieving the schema from S3. Please confirm that your project has a `defineData` included in the `defineBackend` definition.' + ) + ); }); - }); - void it(`returns empty config objects when ${envFieldToExclude} is not a string`, async () => { - const env = { ...validEnv } as Record; - env[envFieldToExclude] = 123; - assert.deepEqual(await getAmplifyDataClientConfig(env), { - resourceConfig: {}, - libraryOptions: {}, + void it('raises a custom error message when there is a S3ServiceException error retrieving the model introspection schema from the s3 bucket', async () => { + const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { + throw new S3ServiceException({ + name: 'TEST_ERROR', + message: 'TEST_MESSAGE', + $fault: 'server', + $metadata: {}, + }); + }); + mock.method(mockS3Client, 'send', s3ClientSendMock); + + await assert.rejects( + async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), + new Error( + 'Error retrieving the schema from S3. You may need to grant this function authorization on the schema. TEST_ERROR: TEST_MESSAGE.' + ) + ); }); - }); - }); - void it('raises a custom error message when the model introspection schema is missing from the s3 bucket', async () => { - const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { - throw new NoSuchKey({ message: 'TEST_ERROR', $metadata: {} }); - }); - mock.method(mockS3Client, 'send', s3ClientSendMock); - - await assert.rejects( - async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), - new Error( - 'Error retrieving the schema from S3. Please confirm that your project has a `defineData` included in the `defineBackend` definition.' - ) - ); - }); + void it('re-raises a non-S3 error received when retrieving the model introspection schema from the s3 bucket', async () => { + const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { + throw new Error('Test Error'); + }); + mock.method(mockS3Client, 'send', s3ClientSendMock); - void it('raises a custom error message when there is a S3ServiceException error retrieving the model introspection schema from the s3 bucket', async () => { - const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { - throw new S3ServiceException({ - name: 'TEST_ERROR', - message: 'TEST_MESSAGE', - $fault: 'server', - $metadata: {}, + await assert.rejects( + async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), + new Error('Test Error') + ); }); - }); - mock.method(mockS3Client, 'send', s3ClientSendMock); - - await assert.rejects( - async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), - new Error( - 'Error retrieving the schema from S3. You may need to grant this function authorization on the schema. TEST_ERROR: TEST_MESSAGE.' - ) - ); - }); - void it('re-raises a non-S3 error received when retrieving the model introspection schema from the s3 bucket', async () => { - const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { - throw new Error('Test Error'); - }); - mock.method(mockS3Client, 'send', s3ClientSendMock); - - await assert.rejects( - async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), - new Error('Test Error') - ); - }); - - void it('returns the expected libraryOptions and resourceConfig values in the happy case', async () => { - const s3ClientSendMock = mock.method(mockS3Client, 'send', () => { - return Promise.resolve({ - Body: { - transformToString: () => JSON.stringify({ testSchema: 'TESTING' }), - }, + void it('returns the expected libraryOptions and resourceConfig values in the happy case', async () => { + const s3ClientSendMock = mock.method(mockS3Client, 'send', () => { + return Promise.resolve({ + Body: { + transformToString: () => + JSON.stringify({ testSchema: 'TESTING' }), + }, + }); + }); + mock.method(mockS3Client, 'send', s3ClientSendMock); + + const { resourceConfig, libraryOptions } = + await getAmplifyDataClientConfig(validEnv, mockS3Client); + + assert.deepEqual( + await libraryOptions.Auth.credentialsProvider.getCredentialsAndIdentityId?.(), + { + credentials: { + accessKeyId: 'TEST_VALUE for AWS_ACCESS_KEY_ID', + secretAccessKey: 'TEST_VALUE for AWS_SECRET_ACCESS_KEY', + sessionToken: 'TEST_VALUE for AWS_SESSION_TOKEN', + }, + } + ); + assert.deepEqual( + await libraryOptions.Auth.credentialsProvider.clearCredentialsAndIdentityId?.(), + undefined + ); + + assert.deepEqual(resourceConfig, { + API: { + GraphQL: { + endpoint: 'TEST_VALUE for AMPLIFY_DATA_GRAPHQL_ENDPOINT', + region: 'TEST_VALUE for AWS_REGION', + defaultAuthMode: 'iam', + modelIntrospection: { testSchema: 'TESTING' }, + }, + }, + }); }); }); - mock.method(mockS3Client, 'send', s3ClientSendMock); - - const { resourceConfig, libraryOptions } = await getAmplifyDataClientConfig( - validEnv, - mockS3Client - ); - - assert.deepEqual( - await libraryOptions.Auth.credentialsProvider.getCredentialsAndIdentityId?.(), - { - credentials: { - accessKeyId: 'TEST_VALUE for AWS_ACCESS_KEY_ID', - secretAccessKey: 'TEST_VALUE for AWS_SECRET_ACCESS_KEY', - sessionToken: 'TEST_VALUE for AWS_SESSION_TOKEN', - }, - } - ); - assert.deepEqual( - await libraryOptions.Auth.credentialsProvider.clearCredentialsAndIdentityId?.(), - undefined - ); - - assert.deepEqual(resourceConfig, { - API: { - GraphQL: { - endpoint: 'TEST_VALUE for AMPLIFY_DATA_GRAPHQL_ENDPOINT', - region: 'TEST_VALUE for AWS_REGION', - defaultAuthMode: 'iam', - modelIntrospection: { testSchema: 'TESTING' }, - }, - }, - }); }); }); diff --git a/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts index 2f377fe3492..8c179fdf57c 100644 --- a/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts +++ b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts @@ -1,3 +1,4 @@ +import { NamingConverter } from '@aws-amplify/platform-core'; import { GetObjectCommand, NoSuchKey, @@ -5,37 +6,40 @@ import { S3ServiceException, } from '@aws-sdk/client-s3'; +const dataKeyNameContent = '_MODEL_INTROSPECTION_SCHEMA_KEY'; +const dataBucketNameContent = '_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME'; +const dataEndpointNameContent = '_GRAPHQL_ENDPOINT'; + export type DataClientEnv = { /* eslint-disable @typescript-eslint/naming-convention */ - AMPLIFY_DATA_GRAPHQL_ENDPOINT: string; - AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME: string; - AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY: string; AWS_ACCESS_KEY_ID: string; AWS_SECRET_ACCESS_KEY: string; AWS_SESSION_TOKEN: string; AWS_REGION: string; + AMPLIFY_DATA_DEFAULT_NAME: string; /* eslint-enable @typescript-eslint/naming-convention */ +} & Record; + +type DataEnvExtension = { + dataBucket: string; + dataKey: string; + dataEndpoint: string; }; -const isDataClientEnv = (env: unknown): env is DataClientEnv => { +type ExtendedAmplifyClientEnv = DataClientEnv & DataEnvExtension; + +const isAmplifyClientEnv = (env: object): env is DataClientEnv => { return ( - env !== null && - typeof env === 'object' && - 'AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME' in env && - 'AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY' in env && 'AWS_ACCESS_KEY_ID' in env && - 'AWS_SECRET_ACCESS_KEY' in env && - 'AWS_SESSION_TOKEN' in env && - 'AWS_REGION' in env && - 'AMPLIFY_DATA_GRAPHQL_ENDPOINT' in env && - typeof env.AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME === - 'string' && - typeof env.AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY === 'string' && typeof env.AWS_ACCESS_KEY_ID === 'string' && + 'AWS_SECRET_ACCESS_KEY' in env && typeof env.AWS_SECRET_ACCESS_KEY === 'string' && + 'AWS_SESSION_TOKEN' in env && typeof env.AWS_SESSION_TOKEN === 'string' && + 'AWS_REGION' in env && typeof env.AWS_REGION === 'string' && - typeof env.AMPLIFY_DATA_GRAPHQL_ENDPOINT === 'string' + 'AMPLIFY_DATA_DEFAULT_NAME' in env && + typeof env.AMPLIFY_DATA_DEFAULT_NAME === 'string' ); }; @@ -56,16 +60,15 @@ export type ResourceConfig = { /* eslint-enable @typescript-eslint/naming-convention */ const getResourceConfig = ( - env: DataClientEnv, + env: ExtendedAmplifyClientEnv, modelIntrospectionSchema: object ): ResourceConfig => { return { API: { GraphQL: { - endpoint: env.AMPLIFY_DATA_GRAPHQL_ENDPOINT, + endpoint: env.dataEndpoint, region: env.AWS_REGION, defaultAuthMode: 'iam' as const, - modelIntrospection: modelIntrospectionSchema, }, }, @@ -108,7 +111,7 @@ const getLibraryOptions = (env: DataClientEnv): LibraryOptions => { }; export type InvalidConfig = unknown & { - invalidType: 'This function needs to be granted `authorization((allow) => [allow.resource(fcn)])` on the data schema.'; + invalidType: 'Some of the AWS environment variables needed to configure Amplify are missing.'; }; export type DataClientError = { @@ -125,6 +128,40 @@ export type DataClientReturn = T extends DataClientEnv ? DataClientConfig : DataClientError; +const extendEnv = ( + env: DataClientEnv & Record, + dataName: string +): ExtendedAmplifyClientEnv => { + const bucketName = `${dataName}${dataBucketNameContent}`; + const keyName = `${dataName}${dataKeyNameContent}`; + const endpointName = `${dataName}${dataEndpointNameContent}`; + if ( + !( + bucketName in env && + keyName in env && + endpointName in env && + typeof env[bucketName] === 'string' && + typeof env[keyName] === 'string' && + typeof env[endpointName] === 'string' + ) + ) { + throw new Error( + `The data environment variables are malformed. env=${JSON.stringify(env)}` + ); + } + + const dataBucket = env[bucketName] as string; + const dataKey = env[keyName] as string; + const dataEndpoint = env[endpointName] as string; + + return { + ...env, + dataBucket, + dataKey, + dataEndpoint, + }; +}; + /** * Generate the `resourceConfig` and `libraryOptions` need to configure * Amplify for the data client in a lambda. @@ -141,17 +178,26 @@ export const getAmplifyDataClientConfig = async ( if (!s3Client) { s3Client = new S3Client(); } + if (env === null || typeof env !== 'object') { + throw new Error(`Invalid environment variables: ${JSON.stringify(env)}`); + } - if (!isDataClientEnv(env)) { + if (!isAmplifyClientEnv(env)) { return { resourceConfig: {}, libraryOptions: {} } as DataClientReturn; } + + const dataName = new NamingConverter().toScreamingSnakeCase( + env.AMPLIFY_DATA_DEFAULT_NAME + ); + const extendedEnv = extendEnv(env, dataName); + let modelIntrospectionSchema: object; try { const response = await s3Client.send( new GetObjectCommand({ - Bucket: env.AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME, - Key: env.AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY, + Bucket: extendedEnv.dataBucket, + Key: extendedEnv.dataKey, }) ); const modelIntrospectionSchemaJson = @@ -173,7 +219,10 @@ export const getAmplifyDataClientConfig = async ( const libraryOptions = getLibraryOptions(env); - const resourceConfig = getResourceConfig(env, modelIntrospectionSchema); + const resourceConfig = getResourceConfig( + extendedEnv, + modelIntrospectionSchema + ); return { resourceConfig, libraryOptions } as DataClientReturn; }; diff --git a/packages/backend/package.json b/packages/backend/package.json index 7ef401f5aa2..6ff0d2c3840 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -41,8 +41,7 @@ "@aws-amplify/client-config": "^1.5.3", "@aws-amplify/platform-core": "^1.3.0", "@aws-amplify/plugin-types": "^1.6.0", - "@aws-sdk/client-amplify": "^3.624.0", - "lodash.snakecase": "^4.1.1" + "@aws-sdk/client-amplify": "^3.624.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", diff --git a/packages/backend/src/engine/backend_id_scoped_ssm_environment_entries_generator.ts b/packages/backend/src/engine/backend_id_scoped_ssm_environment_entries_generator.ts index 067ccf32bac..c104f96b028 100644 --- a/packages/backend/src/engine/backend_id_scoped_ssm_environment_entries_generator.ts +++ b/packages/backend/src/engine/backend_id_scoped_ssm_environment_entries_generator.ts @@ -1,11 +1,13 @@ -import { ParameterPathConversions } from '@aws-amplify/platform-core'; +import { + NamingConverter, + ParameterPathConversions, +} from '@aws-amplify/platform-core'; import { BackendIdentifier, SsmEnvironmentEntriesGenerator, } from '@aws-amplify/plugin-types'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { Construct } from 'constructs'; -import { toScreamingSnakeCase } from './naming_convention_conversions.js'; /** * Generates SsmEnvironmentEntry[] with SSM parameters that are scoped to a specific backend identifier @@ -50,7 +52,9 @@ export class BackendIdScopedSsmEnvironmentEntriesGenerator */ generateSsmEnvironmentEntries = (scopeContext: Record) => Object.entries(scopeContext).map(([contextKey, contextValue]) => { - const sanitizedContextKey = toScreamingSnakeCase(contextKey); + const sanitizedContextKey = new NamingConverter().toScreamingSnakeCase( + contextKey + ); const parameterPath = ParameterPathConversions.toResourceReferenceFullPath( this.backendId, diff --git a/packages/backend/src/engine/naming_convention_conversions.ts b/packages/backend/src/engine/naming_convention_conversions.ts deleted file mode 100644 index 272601da6f6..00000000000 --- a/packages/backend/src/engine/naming_convention_conversions.ts +++ /dev/null @@ -1,8 +0,0 @@ -import snakeCase from 'lodash.snakecase'; - -/** - * Converts input string to SCREAMING_SNAKE_CASE - */ -export const toScreamingSnakeCase = (input: string): string => { - return snakeCase(input).toUpperCase(); -}; diff --git a/packages/integration-tests/src/test-projects/data_access_from_function/amplify/data/resource.ts b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/data/resource.ts index 4c3442bdc72..2bb4831790b 100644 --- a/packages/integration-tests/src/test-projects/data_access_from_function/amplify/data/resource.ts +++ b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/data/resource.ts @@ -31,6 +31,7 @@ const schema = a export type Schema = ClientSchema; export const data = defineData({ + name: 'DATATEST', schema, authorizationModes: { defaultAuthorizationMode: 'apiKey', diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index f40d1c53648..50d40bce8e1 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -145,6 +145,11 @@ class LogRetentionConverter { toCDKRetentionDays: (retention: LogRetention | undefined) => RetentionDays | undefined; } +// @public +export class NamingConverter { + toScreamingSnakeCase(input: string): string; +} + // @public export class ObjectAccumulator { constructor(accumulator: DeepPartialAmplifyGeneratedConfigs, versionKey?: string); diff --git a/packages/platform-core/package.json b/packages/platform-core/package.json index 6ea68ea197a..bce16548a83 100644 --- a/packages/platform-core/package.json +++ b/packages/platform-core/package.json @@ -34,6 +34,7 @@ "is-ci": "^3.0.1", "lodash.mergewith": "^4.6.2", "semver": "^7.6.3", + "lodash.snakecase": "^4.1.1", "uuid": "^9.0.1", "zod": "^3.22.2" }, diff --git a/packages/platform-core/src/index.ts b/packages/platform-core/src/index.ts index 7e05a510ea4..97987eade81 100644 --- a/packages/platform-core/src/index.ts +++ b/packages/platform-core/src/index.ts @@ -11,3 +11,4 @@ export { CDKContextKey } from './cdk_context_key.js'; export * from './parameter_path_conversions.js'; export * from './object_accumulator.js'; export { TagName } from './tag_name.js'; +export * from './naming_convention_conversions.js'; diff --git a/packages/backend/src/engine/naming_convention_conversions.test.ts b/packages/platform-core/src/naming_convention_conversions.test.ts similarity index 80% rename from packages/backend/src/engine/naming_convention_conversions.test.ts rename to packages/platform-core/src/naming_convention_conversions.test.ts index d6e9e2d4964..f2092bc4f3c 100644 --- a/packages/backend/src/engine/naming_convention_conversions.test.ts +++ b/packages/platform-core/src/naming_convention_conversions.test.ts @@ -1,5 +1,5 @@ import { describe, it } from 'node:test'; -import { toScreamingSnakeCase } from './naming_convention_conversions.js'; +import { NamingConverter } from './naming_convention_conversions.js'; import assert from 'node:assert'; void describe('screaming snake conversions', () => { @@ -16,7 +16,10 @@ void describe('screaming snake conversions', () => { ]; testCases.forEach((testCase) => { void it(`should successfully convert ${testCase.input} to ${testCase.expected}`, () => { - assert.equal(toScreamingSnakeCase(testCase.input), testCase.expected); + assert.equal( + new NamingConverter().toScreamingSnakeCase(testCase.input), + testCase.expected + ); }); }); }); diff --git a/packages/platform-core/src/naming_convention_conversions.ts b/packages/platform-core/src/naming_convention_conversions.ts new file mode 100644 index 00000000000..7829db94962 --- /dev/null +++ b/packages/platform-core/src/naming_convention_conversions.ts @@ -0,0 +1,16 @@ +import snakeCase from 'lodash.snakecase'; + +/** + * Naming Converter + * @example + * new NamingConverter().toScreamingSnakeCase('myInputString') + */ +export class NamingConverter { + /** + * Converts input string to SCREAMING_SNAKE_CASE + * @param input Input string to convert + */ + public toScreamingSnakeCase(input: string): string { + return snakeCase(input).toUpperCase(); + } +} From 1eced2caa64fb1f1e65d1d5db42c28e3a059c34f Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:21:54 +0100 Subject: [PATCH 165/199] chore: add error mappings and resolutions for circular dependency failures (#2342) --- .changeset/early-dodos-pull.md | 5 ++++ .../src/cdk_error_mapper.test.ts | 14 ++++++++++ .../backend-deployer/src/cdk_error_mapper.ts | 28 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 .changeset/early-dodos-pull.md diff --git a/.changeset/early-dodos-pull.md b/.changeset/early-dodos-pull.md new file mode 100644 index 00000000000..d9b13d1e3e1 --- /dev/null +++ b/.changeset/early-dodos-pull.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +add mapping for circular dependency failures with resolution diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 9266b7d999c..3b5611bf157 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -665,6 +665,20 @@ npm error enoent`, errorName: 'InvalidOrCannotAssumeRoleError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: `some-stack failed: ValidationError: Circular dependency between resources: [storage1, data1, function1] `, + expectedTopLevelErrorMessage: + 'The CloudFormation deployment failed due to circular dependency found between nested stacks [storage1, data1, function1]', + errorName: 'CloudformationStackCircularDependencyError', + expectedDownstreamErrorMessage: undefined, + }, + { + errorMessage: `The stack named named-stack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Circular dependency between resources: [resource1, resource2] `, + expectedTopLevelErrorMessage: + 'The CloudFormation deployment failed due to circular dependency found between resources [resource1, resource2] in a single stack', + errorName: 'CloudformationResourceCircularDependencyError', + expectedDownstreamErrorMessage: undefined, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index def97f292de..bf1694e4ec9 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -486,6 +486,32 @@ export class CdkErrorMapper { errorName: 'BackendSynthError', classification: 'ERROR', }, + { + // This error pattern is observed when circular dependency is between stacks but not resources in a stack + errorRegex: + /ValidationError: Circular dependency between resources: \[(?.*)\]/, + humanReadableErrorMessage: + 'The CloudFormation deployment failed due to circular dependency found between nested stacks [{resources}]', + resolutionMessage: `If you are using functions then you can assign them to existing nested stacks that are dependent on functions or functions depend on them, for example: +1. If your function is defined as auth triggers, you should assign this function to auth stack. +2. If your function is used as data resolver or calls data API, you should assign this function to data stack. +To assign a function to a different stack, use the property 'resourceGroupName' in the defineFunction call and choose auth, data or any custom stack. + +If your circular dependency issue is not resolved with this workaround, please create an issue here https://github.com/aws-amplify/amplify-backend/issues/new/choose +`, + errorName: 'CloudformationStackCircularDependencyError', + classification: 'ERROR', + }, + { + // This error pattern is observed when circular dependency is between resources in a single stack, i.e. ValidationError is absent from the error message + errorRegex: + /(?.*)\]/, + humanReadableErrorMessage: + 'The CloudFormation deployment failed due to circular dependency found between resources [{resources}] in a single stack', + resolutionMessage: `If you are creating custom stacks or adding new CDK resources to amplify stacks, ensure that there are no cyclic dependencies. For more details see: https://aws.amazon.com/blogs/infrastructure-and-automation/handling-circular-dependency-errors-in-aws-cloudformation/`, + errorName: 'CloudformationResourceCircularDependencyError', + classification: 'ERROR', + }, { errorRegex: /(?amplify-[a-z0-9-]+)(.*) failed: ValidationError: Stack:(.*) is in (?.*) state and can not be updated/, @@ -523,6 +549,8 @@ export type CDKDeploymentError = | 'CDKResolveAWSAccountError' | 'CDKVersionMismatchError' | 'CFNUpdateNotSupportedError' + | 'CloudformationResourceCircularDependencyError' + | 'CloudformationStackCircularDependencyError' | 'CloudFormationDeletionError' | 'CloudFormationDeploymentError' | 'CommonNPMError' From f1dbf71f2b72a67d417557148addec95967c0ed8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:19:26 +0000 Subject: [PATCH 166/199] Version Packages (#2317) Co-authored-by: github-actions[bot] --- .changeset/afraid-swans-rescue.md | 2 -- .changeset/brown-mayflies-turn.md | 5 ----- .changeset/clever-crabs-sip.md | 6 ------ .changeset/early-dodos-pull.md | 5 ----- .changeset/few-candles-wink.md | 8 -------- .changeset/gentle-seahorses-sleep.md | 2 -- .changeset/good-jokes-eat.md | 2 -- .changeset/great-mugs-warn.md | 5 ----- .changeset/hip-buses-clap.md | 5 ----- .changeset/loud-dots-bathe.md | 2 -- .changeset/nasty-tables-heal.md | 5 ----- .changeset/silent-files-appear.md | 6 ------ .changeset/warm-sloths-tickle.md | 7 ------- packages/backend-data/CHANGELOG.md | 6 ++++++ packages/backend-data/package.json | 4 ++-- packages/backend-deployer/CHANGELOG.md | 12 ++++++++++++ packages/backend-deployer/package.json | 4 ++-- packages/backend-function/CHANGELOG.md | 8 ++++++++ packages/backend-function/package.json | 4 ++-- packages/backend/CHANGELOG.md | 19 +++++++++++++++++++ packages/backend/package.json | 10 +++++----- packages/cli/CHANGELOG.md | 19 +++++++++++++++++++ packages/cli/package.json | 14 +++++++------- packages/client-config/CHANGELOG.md | 13 +++++++++++++ packages/client-config/package.json | 8 ++++---- packages/deployed-backend-client/CHANGELOG.md | 13 +++++++++++++ packages/deployed-backend-client/package.json | 4 ++-- packages/model-generator/CHANGELOG.md | 12 ++++++++++++ packages/model-generator/package.json | 6 +++--- packages/platform-core/CHANGELOG.md | 11 +++++++++++ packages/platform-core/package.json | 2 +- packages/sandbox/CHANGELOG.md | 16 ++++++++++++++++ packages/sandbox/package.json | 10 +++++----- 33 files changed, 162 insertions(+), 93 deletions(-) delete mode 100644 .changeset/afraid-swans-rescue.md delete mode 100644 .changeset/brown-mayflies-turn.md delete mode 100644 .changeset/clever-crabs-sip.md delete mode 100644 .changeset/early-dodos-pull.md delete mode 100644 .changeset/few-candles-wink.md delete mode 100644 .changeset/gentle-seahorses-sleep.md delete mode 100644 .changeset/good-jokes-eat.md delete mode 100644 .changeset/great-mugs-warn.md delete mode 100644 .changeset/hip-buses-clap.md delete mode 100644 .changeset/loud-dots-bathe.md delete mode 100644 .changeset/nasty-tables-heal.md delete mode 100644 .changeset/silent-files-appear.md delete mode 100644 .changeset/warm-sloths-tickle.md diff --git a/.changeset/afraid-swans-rescue.md b/.changeset/afraid-swans-rescue.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/afraid-swans-rescue.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/brown-mayflies-turn.md b/.changeset/brown-mayflies-turn.md deleted file mode 100644 index 7cc4e3fc980..00000000000 --- a/.changeset/brown-mayflies-turn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -add error mapping for role is invalid or cannot be assumed error' diff --git a/.changeset/clever-crabs-sip.md b/.changeset/clever-crabs-sip.md deleted file mode 100644 index 6ed49869540..00000000000 --- a/.changeset/clever-crabs-sip.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch -'@aws-amplify/platform-core': patch ---- - -expand wrapping of credentials related errors diff --git a/.changeset/early-dodos-pull.md b/.changeset/early-dodos-pull.md deleted file mode 100644 index d9b13d1e3e1..00000000000 --- a/.changeset/early-dodos-pull.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -add mapping for circular dependency failures with resolution diff --git a/.changeset/few-candles-wink.md b/.changeset/few-candles-wink.md deleted file mode 100644 index 224a172d780..00000000000 --- a/.changeset/few-candles-wink.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@aws-amplify/deployed-backend-client': minor -'@aws-amplify/model-generator': patch -'@aws-amplify/client-config': patch -'@aws-amplify/backend-cli': patch ---- - -update detection of BackendOutputClientErrors diff --git a/.changeset/gentle-seahorses-sleep.md b/.changeset/gentle-seahorses-sleep.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/gentle-seahorses-sleep.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/good-jokes-eat.md b/.changeset/good-jokes-eat.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/good-jokes-eat.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/great-mugs-warn.md b/.changeset/great-mugs-warn.md deleted file mode 100644 index 36189b9cb33..00000000000 --- a/.changeset/great-mugs-warn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/sandbox': patch ---- - -do not stream function logs if stack does not exist diff --git a/.changeset/hip-buses-clap.md b/.changeset/hip-buses-clap.md deleted file mode 100644 index 24ef844e85a..00000000000 --- a/.changeset/hip-buses-clap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/platform-core': patch ---- - -expand handling of getaddrinfo ENOTFOUND errors diff --git a/.changeset/loud-dots-bathe.md b/.changeset/loud-dots-bathe.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/loud-dots-bathe.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/nasty-tables-heal.md b/.changeset/nasty-tables-heal.md deleted file mode 100644 index 777a807a343..00000000000 --- a/.changeset/nasty-tables-heal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-function': minor ---- - -Add ephemeralStorageSizeMB option to defineFunction diff --git a/.changeset/silent-files-appear.md b/.changeset/silent-files-appear.md deleted file mode 100644 index 3cdd1f61306..00000000000 --- a/.changeset/silent-files-appear.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/backend-function': minor -'@aws-amplify/backend': minor ---- - -updates layer to also use layername:version diff --git a/.changeset/warm-sloths-tickle.md b/.changeset/warm-sloths-tickle.md deleted file mode 100644 index d8806943394..00000000000 --- a/.changeset/warm-sloths-tickle.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@aws-amplify/backend-function': minor -'@aws-amplify/backend-data': patch -'@aws-amplify/platform-core': minor ---- - -Update getAmplifyDataClientConfig to work with named data backend diff --git a/packages/backend-data/CHANGELOG.md b/packages/backend-data/CHANGELOG.md index cec24087071..51252b80104 100644 --- a/packages/backend-data/CHANGELOG.md +++ b/packages/backend-data/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/backend-data +## 1.2.3 + +### Patch Changes + +- f193105: Update getAmplifyDataClientConfig to work with named data backend + ## 1.2.2 ### Patch Changes diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index 1fd1c2d5877..98612cfa782 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-data", - "version": "1.2.2", + "version": "1.2.3", "type": "module", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "devDependencies": { "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/backend-platform-test-stubs": "^0.3.7", - "@aws-amplify/platform-core": "^1.3.0" + "@aws-amplify/platform-core": "^1.4.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", diff --git a/packages/backend-deployer/CHANGELOG.md b/packages/backend-deployer/CHANGELOG.md index 69edc3b2a80..75ad01f825d 100644 --- a/packages/backend-deployer/CHANGELOG.md +++ b/packages/backend-deployer/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/backend-deployer +## 1.1.12 + +### Patch Changes + +- dedcc27: add error mapping for role is invalid or cannot be assumed error' +- 95942c5: expand wrapping of credentials related errors +- 1eced2c: add mapping for circular dependency failures with resolution +- Updated dependencies [95942c5] +- Updated dependencies [f679cf6] +- Updated dependencies [f193105] + - @aws-amplify/platform-core@1.4.0 + ## 1.1.11 ### Patch Changes diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index 57b868e301c..93082d2ecc6 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-deployer", - "version": "1.1.11", + "version": "1.1.12", "type": "module", "publishConfig": { "access": "public" @@ -19,7 +19,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.6.0", "execa": "^9.5.1", "tsx": "^4.6.1", diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index 2d9ba784917..31c9a145496 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-function +## 1.10.0 + +### Minor Changes + +- d32e4cd: Add ephemeralStorageSizeMB option to defineFunction +- 560878f: updates layer to also use layername:version +- f193105: Update getAmplifyDataClientConfig to work with named data backend + ## 1.9.0 ### Minor Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index 4275be7f47a..6cb3d38b934 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.9.0", + "version": "1.10.0", "type": "module", "publishConfig": { "access": "public" @@ -32,7 +32,7 @@ }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.7", - "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/platform-core": "^1.4.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 5a57e825ed8..e11c77e6b59 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,24 @@ # @aws-amplify/backend +## 1.10.0 + +### Minor Changes + +- 560878f: updates layer to also use layername:version + +### Patch Changes + +- Updated dependencies [95942c5] +- Updated dependencies [3cf0738] +- Updated dependencies [f679cf6] +- Updated dependencies [d32e4cd] +- Updated dependencies [560878f] +- Updated dependencies [f193105] + - @aws-amplify/platform-core@1.4.0 + - @aws-amplify/client-config@1.5.4 + - @aws-amplify/backend-function@1.10.0 + - @aws-amplify/backend-data@1.2.3 + ## 1.9.0 ### Minor Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 6ff0d2c3840..94784fd2d85 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.9.0", + "version": "1.10.0", "type": "module", "publishConfig": { "access": "public" @@ -32,14 +32,14 @@ "dependencies": { "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/backend-auth": "^1.4.2", - "@aws-amplify/backend-function": "^1.9.0", - "@aws-amplify/backend-data": "^1.2.2", + "@aws-amplify/backend-function": "^1.10.0", + "@aws-amplify/backend-data": "^1.2.3", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.4", - "@aws-amplify/client-config": "^1.5.3", - "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/client-config": "^1.5.4", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-amplify": "^3.624.0" }, diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 54fa3beac60..74f5a07e09a 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,24 @@ # @aws-amplify/backend-cli +## 1.4.4 + +### Patch Changes + +- 3cf0738: update detection of BackendOutputClientErrors +- Updated dependencies [dedcc27] +- Updated dependencies [95942c5] +- Updated dependencies [1eced2c] +- Updated dependencies [3cf0738] +- Updated dependencies [3cf0738] +- Updated dependencies [f679cf6] +- Updated dependencies [f193105] + - @aws-amplify/backend-deployer@1.1.12 + - @aws-amplify/platform-core@1.4.0 + - @aws-amplify/deployed-backend-client@1.5.0 + - @aws-amplify/model-generator@1.0.10 + - @aws-amplify/client-config@1.5.4 + - @aws-amplify/sandbox@1.2.8 + ## 1.4.3 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 1a0c6aa8b5b..e385909af54 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-cli", - "version": "1.4.3", + "version": "1.4.4", "description": "Command line interface for various Amplify tools", "bin": { "ampx": "lib/ampx.js", @@ -31,17 +31,17 @@ }, "homepage": "https://github.com/aws-amplify/amplify-backend#readme", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.11", + "@aws-amplify/backend-deployer": "^1.1.12", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.1", - "@aws-amplify/client-config": "^1.5.3", - "@aws-amplify/deployed-backend-client": "^1.4.1", + "@aws-amplify/client-config": "^1.5.4", + "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/form-generator": "^1.0.3", - "@aws-amplify/model-generator": "^1.0.9", - "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/model-generator": "^1.0.10", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.6.0", - "@aws-amplify/sandbox": "^1.2.7", + "@aws-amplify/sandbox": "^1.2.8", "@aws-amplify/schema-generator": "^1.2.6", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", diff --git a/packages/client-config/CHANGELOG.md b/packages/client-config/CHANGELOG.md index e04bbebf631..e15faba6692 100644 --- a/packages/client-config/CHANGELOG.md +++ b/packages/client-config/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/client-config +## 1.5.4 + +### Patch Changes + +- 3cf0738: update detection of BackendOutputClientErrors +- Updated dependencies [95942c5] +- Updated dependencies [3cf0738] +- Updated dependencies [f679cf6] +- Updated dependencies [f193105] + - @aws-amplify/platform-core@1.4.0 + - @aws-amplify/deployed-backend-client@1.5.0 + - @aws-amplify/model-generator@1.0.10 + ## 1.5.3 ### Patch Changes diff --git a/packages/client-config/package.json b/packages/client-config/package.json index 4decaaf10ac..3672b0590c2 100644 --- a/packages/client-config/package.json +++ b/packages/client-config/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/client-config", - "version": "1.5.3", + "version": "1.5.4", "type": "module", "publishConfig": { "access": "public" @@ -25,9 +25,9 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/model-generator": "^1.0.7", - "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/deployed-backend-client": "^1.5.0", + "@aws-amplify/model-generator": "^1.0.10", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.6.0", "zod": "^3.22.2" }, diff --git a/packages/deployed-backend-client/CHANGELOG.md b/packages/deployed-backend-client/CHANGELOG.md index 49a70a81904..86d4dfd0444 100644 --- a/packages/deployed-backend-client/CHANGELOG.md +++ b/packages/deployed-backend-client/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/deployed-backend-client +## 1.5.0 + +### Minor Changes + +- 3cf0738: update detection of BackendOutputClientErrors + +### Patch Changes + +- Updated dependencies [95942c5] +- Updated dependencies [f679cf6] +- Updated dependencies [f193105] + - @aws-amplify/platform-core@1.4.0 + ## 1.4.2 ### Patch Changes diff --git a/packages/deployed-backend-client/package.json b/packages/deployed-backend-client/package.json index e1df5238d24..440388678ca 100644 --- a/packages/deployed-backend-client/package.json +++ b/packages/deployed-backend-client/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/deployed-backend-client", - "version": "1.4.2", + "version": "1.5.0", "type": "module", "publishConfig": { "access": "public" @@ -20,7 +20,7 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/platform-core": "^1.0.5", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.2.2", "zod": "^3.22.2" }, diff --git a/packages/model-generator/CHANGELOG.md b/packages/model-generator/CHANGELOG.md index 01d352bb5f4..7563e878910 100644 --- a/packages/model-generator/CHANGELOG.md +++ b/packages/model-generator/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/model-generator +## 1.0.10 + +### Patch Changes + +- 3cf0738: update detection of BackendOutputClientErrors +- Updated dependencies [95942c5] +- Updated dependencies [3cf0738] +- Updated dependencies [f679cf6] +- Updated dependencies [f193105] + - @aws-amplify/platform-core@1.4.0 + - @aws-amplify/deployed-backend-client@1.5.0 + ## 1.0.9 ### Patch Changes diff --git a/packages/model-generator/package.json b/packages/model-generator/package.json index e8633f937ab..21647b8726b 100644 --- a/packages/model-generator/package.json +++ b/packages/model-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/model-generator", - "version": "1.0.9", + "version": "1.0.10", "type": "module", "publishConfig": { "access": "public" @@ -20,10 +20,10 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", - "@aws-amplify/deployed-backend-client": "^1.4.1", + "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/graphql-generator": "^0.5.1", "@aws-amplify/graphql-types-generator": "^3.6.0", - "@aws-amplify/platform-core": "^1.0.5", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.4.0", "@aws-sdk/client-appsync": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", diff --git a/packages/platform-core/CHANGELOG.md b/packages/platform-core/CHANGELOG.md index 5523757a5e7..96ffdd282b5 100644 --- a/packages/platform-core/CHANGELOG.md +++ b/packages/platform-core/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/platform-core +## 1.4.0 + +### Minor Changes + +- f193105: Update getAmplifyDataClientConfig to work with named data backend + +### Patch Changes + +- 95942c5: expand wrapping of credentials related errors +- f679cf6: expand handling of getaddrinfo ENOTFOUND errors + ## 1.3.0 ### Minor Changes diff --git a/packages/platform-core/package.json b/packages/platform-core/package.json index bce16548a83..a637c342733 100644 --- a/packages/platform-core/package.json +++ b/packages/platform-core/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/platform-core", - "version": "1.3.0", + "version": "1.4.0", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/sandbox/CHANGELOG.md b/packages/sandbox/CHANGELOG.md index 3d225cea5c3..a6d59617bb0 100644 --- a/packages/sandbox/CHANGELOG.md +++ b/packages/sandbox/CHANGELOG.md @@ -1,5 +1,21 @@ # @aws-amplify/sandbox +## 1.2.8 + +### Patch Changes + +- 3cf0738: do not stream function logs if stack does not exist +- Updated dependencies [dedcc27] +- Updated dependencies [95942c5] +- Updated dependencies [1eced2c] +- Updated dependencies [3cf0738] +- Updated dependencies [f679cf6] +- Updated dependencies [f193105] + - @aws-amplify/backend-deployer@1.1.12 + - @aws-amplify/platform-core@1.4.0 + - @aws-amplify/deployed-backend-client@1.5.0 + - @aws-amplify/client-config@1.5.4 + ## 1.2.7 ### Patch Changes diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index 6247d7ce2ca..4f81dda9ab7 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/sandbox", - "version": "1.2.7", + "version": "1.2.8", "type": "module", "publishConfig": { "access": "public" @@ -19,12 +19,12 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.11", + "@aws-amplify/backend-deployer": "^1.1.12", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.1", - "@aws-amplify/client-config": "^1.5.3", - "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/client-config": "^1.5.4", + "@aws-amplify/deployed-backend-client": "^1.5.0", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", From 07fe7d44ab1eb9ead5ca58edde80eff4824b04a7 Mon Sep 17 00:00:00 2001 From: Dane Pilcher Date: Tue, 17 Dec 2024 11:47:07 -0700 Subject: [PATCH 167/199] fix: allow apiKeyAuthorizationMode to be undefined if defaultAuthorizationMode is apiKey (#2329) --- .changeset/itchy-flowers-reply.md | 6 ++++++ .../src/convert_authorization_modes.test.ts | 21 +++++++++++++++++++ .../src/convert_authorization_modes.ts | 11 ++++++---- 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 .changeset/itchy-flowers-reply.md diff --git a/.changeset/itchy-flowers-reply.md b/.changeset/itchy-flowers-reply.md new file mode 100644 index 00000000000..1717d3691c8 --- /dev/null +++ b/.changeset/itchy-flowers-reply.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/backend-data': patch +'@aws-amplify/backend': patch +--- + +Allow apiKeyAuthorizationMode to be undefined if defaultAuthorizationMode is apiKey diff --git a/packages/backend-data/src/convert_authorization_modes.test.ts b/packages/backend-data/src/convert_authorization_modes.test.ts index f728c7bd72d..1e361795743 100644 --- a/packages/backend-data/src/convert_authorization_modes.test.ts +++ b/packages/backend-data/src/convert_authorization_modes.test.ts @@ -87,6 +87,7 @@ void describe('convertAuthorizationModesToCDK', () => { defaultAuthorizationMode: 'API_KEY', apiKeyConfig: { expires: Duration.days(7), + description: undefined, }, iamConfig: { enableIamAuthorizationMode: true, @@ -103,6 +104,26 @@ void describe('convertAuthorizationModesToCDK', () => { ); }); + void it('defaults api key expiry if default auth mode is api key and apiKeyConfig is undefined', () => { + const expectedOutput: CDKAuthorizationModes = { + defaultAuthorizationMode: 'API_KEY', + apiKeyConfig: { + expires: Duration.days(7), + description: undefined, + }, + iamConfig: { + enableIamAuthorizationMode: true, + }, + }; + + assert.deepStrictEqual( + convertAuthorizationModesToCDK(getInstancePropsStub, undefined, { + defaultAuthorizationMode: 'apiKey', + }), + expectedOutput + ); + }); + void it('defaults to user pool auth if a user pool is present in provided auth resources', () => { const expectedOutput: CDKAuthorizationModes = { defaultAuthorizationMode: 'AMAZON_COGNITO_USER_POOLS', diff --git a/packages/backend-data/src/convert_authorization_modes.ts b/packages/backend-data/src/convert_authorization_modes.ts index 02df9d4a044..fd03862cc54 100644 --- a/packages/backend-data/src/convert_authorization_modes.ts +++ b/packages/backend-data/src/convert_authorization_modes.ts @@ -60,7 +60,7 @@ export const buildConstructFactoryProvidedAuthConfig = ( const convertApiKeyAuthConfigToCDK = ({ description, expiresInDays = DEFAULT_API_KEY_EXPIRATION_DAYS, -}: ApiKeyAuthorizationModeProps): CDKApiKeyAuthorizationConfig => ({ +}: ApiKeyAuthorizationModeProps = {}): CDKApiKeyAuthorizationConfig => ({ description, expires: Duration.days(expiresInDays), }); @@ -207,9 +207,12 @@ export const convertAuthorizationModesToCDK = ( const cdkAuthorizationMode = convertAuthorizationModeToCDK( defaultAuthorizationMode ); - const apiKeyConfig = authModes?.apiKeyAuthorizationMode - ? convertApiKeyAuthConfigToCDK(authModes.apiKeyAuthorizationMode) - : computeApiKeyAuthFromResource(authResources, authModes); + const apiKeyConfig = + authModes?.apiKeyAuthorizationMode || + // If default auth mode is apiKey, don't require apiKeyAuthorizationMode to be defined + defaultAuthorizationMode === 'apiKey' + ? convertApiKeyAuthConfigToCDK(authModes?.apiKeyAuthorizationMode) + : computeApiKeyAuthFromResource(authResources, authModes); const userPoolConfig = computeUserPoolAuthFromResource(authResources); const identityPoolConfig = computeIdentityPoolAuthFromResource(authResources); const lambdaConfig = authModes?.lambdaAuthorizationMode From cf342cc2eaf90ab4fb96ed63683d39230baaeb8d Mon Sep 17 00:00:00 2001 From: awsluja <110861985+awsluja@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:11:28 -0800 Subject: [PATCH 168/199] chore: ref auth e2e test updates (#2346) * fix: add required pb to ref auth e2e test * chore: add changeset --- .changeset/plenty-mugs-learn.md | 2 + package-lock.json | 66 +++++++++---------- .../auth_resource_creator.ts | 9 ++- .../reference_auth_project.ts | 9 ++- 4 files changed, 49 insertions(+), 37 deletions(-) create mode 100644 .changeset/plenty-mugs-learn.md diff --git a/.changeset/plenty-mugs-learn.md b/.changeset/plenty-mugs-learn.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/plenty-mugs-learn.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/package-lock.json b/package-lock.json index 0b0f20b83f9..6d53bff8ec5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31647,19 +31647,19 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.9.0", + "version": "1.10.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-auth": "^1.4.2", - "@aws-amplify/backend-data": "^1.2.2", - "@aws-amplify/backend-function": "^1.9.0", + "@aws-amplify/backend-data": "^1.2.3", + "@aws-amplify/backend-function": "^1.10.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.4", - "@aws-amplify/client-config": "^1.5.3", + "@aws-amplify/client-config": "^1.5.4", "@aws-amplify/data-schema": "^1.13.4", - "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-amplify": "^3.624.0" }, @@ -31715,7 +31715,7 @@ }, "packages/backend-data": { "name": "@aws-amplify/backend-data", - "version": "1.2.2", + "version": "1.2.3", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", @@ -31728,7 +31728,7 @@ "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.7", "@aws-amplify/data-schema": "^1.13.4", - "@aws-amplify/platform-core": "^1.3.0" + "@aws-amplify/platform-core": "^1.4.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", @@ -31737,10 +31737,10 @@ }, "packages/backend-deployer": { "name": "@aws-amplify/backend-deployer", - "version": "1.1.11", + "version": "1.1.12", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.6.0", "execa": "^9.5.1", "strip-ansi": "^6.0.1", @@ -31871,7 +31871,7 @@ }, "packages/backend-function": { "name": "@aws-amplify/backend-function", - "version": "1.9.0", + "version": "1.10.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", @@ -31882,7 +31882,7 @@ }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.7", - "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/platform-core": "^1.4.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", @@ -32092,20 +32092,20 @@ }, "packages/cli": { "name": "@aws-amplify/backend-cli", - "version": "1.4.3", + "version": "1.4.4", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.11", + "@aws-amplify/backend-deployer": "^1.1.12", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.1", - "@aws-amplify/client-config": "^1.5.3", - "@aws-amplify/deployed-backend-client": "^1.4.1", + "@aws-amplify/client-config": "^1.5.4", + "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/form-generator": "^1.0.3", - "@aws-amplify/model-generator": "^1.0.9", - "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/model-generator": "^1.0.10", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.6.0", - "@aws-amplify/sandbox": "^1.2.7", + "@aws-amplify/sandbox": "^1.2.8", "@aws-amplify/schema-generator": "^1.2.6", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", @@ -32482,13 +32482,13 @@ }, "packages/client-config": { "name": "@aws-amplify/client-config", - "version": "1.5.3", + "version": "1.5.4", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/model-generator": "^1.0.7", - "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/deployed-backend-client": "^1.5.0", + "@aws-amplify/model-generator": "^1.0.10", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.6.0", "zod": "^3.22.2" }, @@ -32737,11 +32737,11 @@ }, "packages/deployed-backend-client": { "name": "@aws-amplify/deployed-backend-client", - "version": "1.4.2", + "version": "1.5.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/platform-core": "^1.0.5", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.2.2", "zod": "^3.22.2" }, @@ -32973,14 +32973,14 @@ }, "packages/model-generator": { "name": "@aws-amplify/model-generator", - "version": "1.0.9", + "version": "1.0.10", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", - "@aws-amplify/deployed-backend-client": "^1.4.1", + "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/graphql-generator": "^0.5.1", "@aws-amplify/graphql-types-generator": "^3.6.0", - "@aws-amplify/platform-core": "^1.0.5", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.4.0", "@aws-sdk/client-appsync": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", @@ -32995,7 +32995,7 @@ }, "packages/platform-core": { "name": "@aws-amplify/platform-core", - "version": "1.3.0", + "version": "1.4.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/plugin-types": "^1.6.0", @@ -33042,15 +33042,15 @@ }, "packages/sandbox": { "name": "@aws-amplify/sandbox", - "version": "1.2.7", + "version": "1.2.8", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.11", + "@aws-amplify/backend-deployer": "^1.1.12", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.1", - "@aws-amplify/client-config": "^1.5.3", - "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/client-config": "^1.5.4", + "@aws-amplify/deployed-backend-client": "^1.5.0", + "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", diff --git a/packages/integration-tests/src/resource-creation/auth_resource_creator.ts b/packages/integration-tests/src/resource-creation/auth_resource_creator.ts index da405b6bbe2..33f0b23b50b 100644 --- a/packages/integration-tests/src/resource-creation/auth_resource_creator.ts +++ b/packages/integration-tests/src/resource-creation/auth_resource_creator.ts @@ -277,7 +277,8 @@ export class AuthResourceCreator { setupUserPoolGroup = async ( groupName: string, userPoolId: string, - identityPoolId: string + identityPoolId: string, + permissionBoundaryArn: string ) => { const groupRole = await this.createRoleBase({ RoleName: 'ref-auth-group-role', @@ -285,6 +286,7 @@ export class AuthResourceCreator { identityPoolId, 'authenticated' ), + PermissionsBoundary: permissionBoundaryArn, }); const group = await this.createUserPoolGroupBase({ GroupName: groupName, @@ -304,7 +306,8 @@ export class AuthResourceCreator { setupIdentityPoolRoles = async ( userPoolId: string, userPoolClientId: string, - identityPoolId: string + identityPoolId: string, + permissionBoundaryArn: string ) => { const authRole = await this.createRoleBase({ RoleName: `ref-auth-role`, @@ -312,6 +315,7 @@ export class AuthResourceCreator { identityPoolId, 'authenticated' ), + PermissionsBoundary: permissionBoundaryArn, }); const unauthRole = await this.createRoleBase({ RoleName: `ref-unauth-role`, @@ -319,6 +323,7 @@ export class AuthResourceCreator { identityPoolId, 'unauthenticated' ), + PermissionsBoundary: permissionBoundaryArn, }); const region = await this.cognitoIdentityClient.config.region(); await this.cognitoIdentityClient.send( diff --git a/packages/integration-tests/src/test-project-setup/reference_auth_project.ts b/packages/integration-tests/src/test-project-setup/reference_auth_project.ts index 66c893c9c16..8932cc83b26 100644 --- a/packages/integration-tests/src/test-project-setup/reference_auth_project.ts +++ b/packages/integration-tests/src/test-project-setup/reference_auth_project.ts @@ -183,6 +183,9 @@ class ReferenceAuthTestProject extends TestProjectBase { DeletionProtection: 'INACTIVE', }); + const accountId = userPool.Arn!.split(':')[4]; // arn:aws:cognito-idp:::userpool/ + const permissionBoundaryArn = `arn:aws:iam::${accountId}:policy/CreateRolePermissionBoundaryPolicy`; + const domain = await this.authResourceCreator.createUserPoolDomainBase({ UserPoolId: userPool.Id, Domain: `ref-auth`, @@ -307,13 +310,15 @@ class ReferenceAuthTestProject extends TestProjectBase { const roles = await this.authResourceCreator.setupIdentityPoolRoles( userPool.Id!, userPoolClient.ClientId!, - identityPool.IdentityPoolId + identityPool.IdentityPoolId, + permissionBoundaryArn ); const adminGroup = await this.authResourceCreator.setupUserPoolGroup( 'ADMINS', userPool.Id!, - identityPool.IdentityPoolId + identityPool.IdentityPoolId, + permissionBoundaryArn ); return { userPool, From 0ef53e2ac94d2fe68e18bfed4a1f9e8e3ca237e3 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Wed, 18 Dec 2024 11:59:27 -0800 Subject: [PATCH 169/199] add validation for sandbox set command secret-name (#2352) --- .changeset/old-hornets-type.md | 5 ++++ .../sandbox_secret_set_command.test.ts | 23 +++++++++++++++++- .../sandbox_secret_set_command.ts | 24 +++++++++++++++---- 3 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 .changeset/old-hornets-type.md diff --git a/.changeset/old-hornets-type.md b/.changeset/old-hornets-type.md new file mode 100644 index 00000000000..b0403c5576c --- /dev/null +++ b/.changeset/old-hornets-type.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-cli': patch +--- + +add validation for sandbox set command secret-name diff --git a/packages/cli/src/commands/sandbox/sandbox-secret/sandbox_secret_set_command.test.ts b/packages/cli/src/commands/sandbox/sandbox-secret/sandbox_secret_set_command.test.ts index cabae98bf0c..73e1969623b 100644 --- a/packages/cli/src/commands/sandbox/sandbox-secret/sandbox_secret_set_command.test.ts +++ b/packages/cli/src/commands/sandbox/sandbox-secret/sandbox_secret_set_command.test.ts @@ -1,7 +1,10 @@ import { beforeEach, describe, it, mock } from 'node:test'; import { AmplifyPrompter, printer } from '@aws-amplify/cli-core'; import yargs, { CommandModule } from 'yargs'; -import { TestCommandRunner } from '../../../test-utils/command_runner.js'; +import { + TestCommandError, + TestCommandRunner, +} from '../../../test-utils/command_runner.js'; import assert from 'node:assert'; import { SandboxBackendIdResolver } from '../sandbox_id_resolver.js'; import { @@ -11,6 +14,7 @@ import { import { SandboxSecretSetCommand } from './sandbox_secret_set_command.js'; import { ReadStream } from 'node:tty'; import { PassThrough } from 'node:stream'; +import { AmplifyError } from '@aws-amplify/platform-core'; const testSecretName = 'testSecretName'; const testSecretValue = 'testSecretValue'; @@ -145,6 +149,23 @@ void describe('sandbox secret set command', () => { ]); }); + void it('throws AmplifyUserError if invalid secret name is provided', async () => { + const invalidSecretName = 'invalid@'; + await assert.rejects( + () => commandRunner.runCommand(`set ${invalidSecretName}`), + (err: TestCommandError) => { + assert.ok(AmplifyError.isAmplifyError(err.error)); + assert.strictEqual( + err.error.message, + 'Invalid secret name provided: invalid@' + ); + assert.strictEqual(err.error.name, 'InvalidCommandInputError'); + return true; + } + ); + assert.equal(secretSetMock.mock.callCount(), 0); + }); + void it('show --help', async () => { const output = await commandRunner.runCommand('set --help'); assert.match(output, /Set a sandbox secret/); diff --git a/packages/cli/src/commands/sandbox/sandbox-secret/sandbox_secret_set_command.ts b/packages/cli/src/commands/sandbox/sandbox-secret/sandbox_secret_set_command.ts index 5d14a06ffe1..5b4c63abf59 100644 --- a/packages/cli/src/commands/sandbox/sandbox-secret/sandbox_secret_set_command.ts +++ b/packages/cli/src/commands/sandbox/sandbox-secret/sandbox_secret_set_command.ts @@ -6,6 +6,7 @@ import { ArgumentsKebabCase } from '../../../kebab_case.js'; import { SandboxCommandGlobalOptions } from '../option_types.js'; import { once } from 'events'; import { ReadStream } from 'node:tty'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; /** * Command to set sandbox secret. @@ -57,11 +58,24 @@ export class SandboxSecretSetCommand * @inheritDoc */ builder = (yargs: Argv): Argv => { - return yargs.positional('secret-name', { - describe: 'Name of the secret to set', - type: 'string', - demandOption: true, - }); + return yargs + .positional('secret-name', { + describe: 'Name of the secret to set', + type: 'string', + demandOption: true, + }) + .check(async (argv) => { + if (argv['secret-name']) { + const secretNameRegex = /^[a-zA-Z0-9_.-]+$/; + if (!argv['secret-name'].match(secretNameRegex)) { + throw new AmplifyUserError('InvalidCommandInputError', { + message: `Invalid secret name provided: ${argv['secret-name']}`, + resolution: 'Use a secret name that matches [a-zA-Z0-9_.-]+', + }); + } + } + return true; + }); }; /** From 107600b7f19edcad44977bd29274353084e92b8b Mon Sep 17 00:00:00 2001 From: Leena <81589006+ShadowCat567@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:28:19 -0500 Subject: [PATCH 170/199] Add additional info to `The specified bucket does not exist` (#2338) * adding S3 client error handling * more error handling for S3 bucket * specified errorName * revert error catching changes * attempting to catch error in new place * adjust return * change form generator error message to amplifyError * updated model generator error message * fix unintended change * added changeset * remove form generator handling, not necessary * tsconfig fix * removed all changes to form generator * no longer user error * adjust error message * changed to AmplifyFault * updates based on feedback * adjusted error message * another update to error message --------- Co-authored-by: Vieltojarvi --- .changeset/swift-mirrors-enjoy.md | 5 +++ .../src/s3_string_object_fetcher.ts | 34 ++++++++++++++----- 2 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 .changeset/swift-mirrors-enjoy.md diff --git a/.changeset/swift-mirrors-enjoy.md b/.changeset/swift-mirrors-enjoy.md new file mode 100644 index 00000000000..ae118767c3e --- /dev/null +++ b/.changeset/swift-mirrors-enjoy.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/model-generator': patch +--- + +Updated error handling with S3 Client diff --git a/packages/model-generator/src/s3_string_object_fetcher.ts b/packages/model-generator/src/s3_string_object_fetcher.ts index 3ae37595923..d3483be70eb 100644 --- a/packages/model-generator/src/s3_string_object_fetcher.ts +++ b/packages/model-generator/src/s3_string_object_fetcher.ts @@ -1,4 +1,5 @@ -import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3'; +import { AmplifyFault } from '@aws-amplify/platform-core'; +import { GetObjectCommand, NoSuchBucket, S3Client } from '@aws-sdk/client-s3'; /** * Handles fetching an object from an s3 bucket and parsing the object contents to a string @@ -14,15 +15,30 @@ export class S3StringObjectFetcher { */ fetch = async (uri: string) => { const { bucket, key } = this.parseS3Uri(uri); - const getSchemaCommandResult = await this.s3Client.send( - new GetObjectCommand({ Bucket: bucket, Key: key }) - ); - const schema = await getSchemaCommandResult.Body?.transformToString(); - if (!schema) { - // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors - throw new Error('Error on parsing output schema'); + try { + const getSchemaCommandResult = await this.s3Client.send( + new GetObjectCommand({ Bucket: bucket, Key: key }) + ); + const schema = await getSchemaCommandResult.Body?.transformToString(); + if (!schema) { + // eslint-disable-next-line amplify-backend-rules/prefer-amplify-errors + throw new Error('Error on parsing output schema'); + } + return schema; + } catch (caught) { + if (caught instanceof NoSuchBucket) { + throw new AmplifyFault( + 'NoSuchBucketFault', + { + message: `${bucket} does not exist. \n + Try redeploying your changes again, if the error persists, create a bug report here: https://github.com/aws-amplify/amplify-backend/issues/new/choose`, + }, + caught + ); + } else { + throw caught; + } } - return schema; }; private parseS3Uri = (uri: string): { bucket: string; key: string } => { From fbf209ebaca8777732e2dfe9a97f872b9ed8b6b4 Mon Sep 17 00:00:00 2001 From: Dane Pilcher Date: Thu, 19 Dec 2024 02:34:50 -0700 Subject: [PATCH 171/199] feat: add api id and amplify environment name to stash (#2273) --- .changeset/strong-toes-sniff.md | 6 ++ .eslint_dictionary.json | 1 + .../src/assets/js_resolver_handler.ts | 4 +- .../src/convert_js_resolvers.test.ts | 88 ++++++++++++++++++- .../backend-data/src/convert_js_resolvers.ts | 24 +++-- 5 files changed, 111 insertions(+), 12 deletions(-) create mode 100644 .changeset/strong-toes-sniff.md diff --git a/.changeset/strong-toes-sniff.md b/.changeset/strong-toes-sniff.md new file mode 100644 index 00000000000..f20c42c831f --- /dev/null +++ b/.changeset/strong-toes-sniff.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/backend-data': minor +'@aws-amplify/backend': minor +--- + +Add GraphQL API ID and Amplify environment name to custom JS resolver stash diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index 708bec2391d..86df2644354 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -78,6 +78,7 @@ "hotswappable", "hotswapped", "hotswapping", + "href", "iamv2", "identitypool", "idps", diff --git a/packages/backend-data/src/assets/js_resolver_handler.ts b/packages/backend-data/src/assets/js_resolver_handler.ts index 282dfb725d6..81b26c4e1b4 100644 --- a/packages/backend-data/src/assets/js_resolver_handler.ts +++ b/packages/backend-data/src/assets/js_resolver_handler.ts @@ -1,7 +1,9 @@ /** * Pipeline resolver request handler */ -export const request = () => { +export const request = (ctx: Record>) => { + ctx.stash.awsAppsyncApiId = '${amplifyApiId}'; + ctx.stash.amplifyApiEnvironmentName = '${amplifyApiEnvironmentName}'; return {}; }; /** diff --git a/packages/backend-data/src/convert_js_resolvers.test.ts b/packages/backend-data/src/convert_js_resolvers.test.ts index 51a646de6ff..19508de86ad 100644 --- a/packages/backend-data/src/convert_js_resolvers.test.ts +++ b/packages/backend-data/src/convert_js_resolvers.test.ts @@ -1,4 +1,4 @@ -import { Template } from 'aws-cdk-lib/assertions'; +import { Match, Template } from 'aws-cdk-lib/assertions'; import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; import { App, Duration, Stack } from 'aws-cdk-lib'; @@ -6,10 +6,15 @@ import { AmplifyData, AmplifyDataDefinition, } from '@aws-amplify/data-construct'; -import { resolve } from 'path'; -import { fileURLToPath } from 'url'; -import { convertJsResolverDefinition } from './convert_js_resolvers.js'; +import { join, resolve } from 'path'; +import { tmpdir } from 'os'; +import { fileURLToPath, pathToFileURL } from 'url'; +import { + convertJsResolverDefinition, + defaultJsResolverCode, +} from './convert_js_resolvers.js'; import { a } from '@aws-amplify/data-schema'; +import { writeFileSync } from 'node:fs'; // stub schema for the AmplifyApi construct // not relevant to this test suite @@ -28,6 +33,33 @@ const createStackAndSetContext = (): Stack => { return stack; }; +void describe('defaultJsResolverCode', () => { + void it('returns the default JS resolver code with api id and env name in valid JS', async () => { + const code = defaultJsResolverCode('testApiId', 'testEnvName'); + assert(code.includes("ctx.stash.awsAppsyncApiId = 'testApiId';")); + assert( + code.includes("ctx.stash.amplifyApiEnvironmentName = 'testEnvName';") + ); + + const tempDir = tmpdir(); + const filename = join(tempDir, 'js_resolver_handler.js'); + writeFileSync(filename, code); + + // windows requires dynamic imports to use file urls + const fileUrl = pathToFileURL(filename).href; + const resolver = await import(fileUrl); + const context = { stash: {}, prev: { result: 'result' } }; + assert.deepEqual(resolver.request(context), {}); + + // assert api id and env name are added to the context stash + assert.deepEqual(context.stash, { + awsAppsyncApiId: 'testApiId', + amplifyApiEnvironmentName: 'testEnvName', + }); + assert.equal(resolver.response(context), 'result'); + }); +}); + void describe('convertJsResolverDefinition', () => { let stack: Stack; let amplifyApi: AmplifyData; @@ -158,4 +190,52 @@ void describe('convertJsResolverDefinition', () => { template.resourceCountIs('AWS::AppSync::Resolver', 1); }); + + void it('adds api id and environment name to stash', () => { + const absolutePath = resolve( + fileURLToPath(import.meta.url), + '../../lib/assets', + 'js_resolver_handler.js' + ); + + const schema = a.schema({ + customQuery: a + .query() + .authorization((allow) => allow.publicApiKey()) + .returns(a.string()) + .handler( + a.handler.custom({ + entry: absolutePath, + }) + ), + }); + const { jsFunctions } = schema.transform(); + convertJsResolverDefinition(stack, amplifyApi, jsFunctions); + + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::AppSync::Resolver', { + Runtime: { + Name: 'APPSYNC_JS', + RuntimeVersion: '1.0.0', + }, + Kind: 'PIPELINE', + TypeName: 'Query', + FieldName: 'customQuery', + Code: { + 'Fn::Join': [ + '', + [ + "/**\n * Pipeline resolver request handler\n */\nexport const request = (ctx) => {\n ctx.stash.awsAppsyncApiId = '", + { + 'Fn::GetAtt': [ + Match.stringLikeRegexp('amplifyDataGraphQLAPI.*'), + 'ApiId', + ], + }, + "';\n ctx.stash.amplifyApiEnvironmentName = 'NONE';\n return {};\n};\n/**\n * Pipeline resolver response handler\n */\nexport const response = (ctx) => {\n return ctx.prev.result;\n};\n", + ], + ], + }, + }); + }); }); diff --git a/packages/backend-data/src/convert_js_resolvers.ts b/packages/backend-data/src/convert_js_resolvers.ts index e4d23eec33e..5117a1135e6 100644 --- a/packages/backend-data/src/convert_js_resolvers.ts +++ b/packages/backend-data/src/convert_js_resolvers.ts @@ -4,6 +4,7 @@ import { CfnFunctionConfiguration, CfnResolver } from 'aws-cdk-lib/aws-appsync'; import { JsResolver } from '@aws-amplify/data-schema-types'; import { resolve } from 'path'; import { fileURLToPath } from 'node:url'; +import { readFileSync } from 'fs'; import { Asset } from 'aws-cdk-lib/aws-s3-assets'; import { resolveEntryPath } from './resolve_entry_path.js'; @@ -18,17 +19,25 @@ const JS_PIPELINE_RESOLVER_HANDLER = './assets/js_resolver_handler.js'; * It's required for defining a pipeline resolver. The only purpose it serves is returning the output of the last function in the pipeline back to the client. * * Customer-provided handlers are added as a Functions list in `pipelineConfig.functions` + * + * Add Amplify API ID and environment name to the context stash for use in the customer-provided handlers. */ -const defaultJsResolverAsset = (scope: Construct): Asset => { +export const defaultJsResolverCode = ( + amplifyApiId: string, + amplifyApiEnvironmentName: string +): string => { const resolvedTemplatePath = resolve( fileURLToPath(import.meta.url), '../../lib', JS_PIPELINE_RESOLVER_HANDLER ); - return new Asset(scope, 'default_js_resolver_handler_asset', { - path: resolveEntryPath(resolvedTemplatePath), - }); + return readFileSync(resolvedTemplatePath, 'utf-8') + .replace(new RegExp(/\$\{amplifyApiId\}/, 'g'), amplifyApiId) + .replace( + new RegExp(/\$\{amplifyApiEnvironmentName\}/, 'g'), + amplifyApiEnvironmentName + ); }; /** @@ -44,8 +53,6 @@ export const convertJsResolverDefinition = ( return; } - const jsResolverTemplateAsset = defaultJsResolverAsset(scope); - for (const resolver of jsResolvers) { const functions: string[] = resolver.handlers.map((handler, idx) => { const fnName = `Fn_${resolver.typeName}_${resolver.fieldName}_${idx + 1}`; @@ -71,12 +78,15 @@ export const convertJsResolverDefinition = ( const resolverName = `Resolver_${resolver.typeName}_${resolver.fieldName}`; + const amplifyApiEnvironmentName = + scope.node.tryGetContext('amplifyEnvironmentName') ?? 'NONE'; new CfnResolver(scope, resolverName, { apiId: amplifyApi.apiId, fieldName: resolver.fieldName, typeName: resolver.typeName, kind: APPSYNC_PIPELINE_RESOLVER, - codeS3Location: jsResolverTemplateAsset.s3ObjectUrl, + // Uses synth-time inline code to avoid circular dependency when adding the API ID as an environment variable. + code: defaultJsResolverCode(amplifyApi.apiId, amplifyApiEnvironmentName), runtime: { name: APPSYNC_JS_RUNTIME_NAME, runtimeVersion: APPSYNC_JS_RUNTIME_VERSION, From a66f5f274e0977e5c52933e4930e532106d374fc Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 19 Dec 2024 12:21:58 -0800 Subject: [PATCH 172/199] Expose timeout property. (#2358) --- .changeset/few-oranges-tell.md | 6 ++ packages/ai-constructs/API.md | 1 + .../conversation_handler_construct.test.ts | 62 +++++++++++++++++++ .../conversation_handler_construct.ts | 31 +++++++++- packages/backend-ai/API.md | 1 + .../src/conversation/factory.test.ts | 50 +++++++++++++++ .../backend-ai/src/conversation/factory.ts | 46 +++++++++++--- 7 files changed, 189 insertions(+), 8 deletions(-) create mode 100644 .changeset/few-oranges-tell.md diff --git a/.changeset/few-oranges-tell.md b/.changeset/few-oranges-tell.md new file mode 100644 index 00000000000..d0ed9aebf0a --- /dev/null +++ b/.changeset/few-oranges-tell.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/ai-constructs': minor +'@aws-amplify/backend-ai': minor +--- + +Expose timeout property diff --git a/packages/ai-constructs/API.md b/packages/ai-constructs/API.md index 0f1550885f2..85b12404d02 100644 --- a/packages/ai-constructs/API.md +++ b/packages/ai-constructs/API.md @@ -57,6 +57,7 @@ type ConversationHandlerFunctionProps = { region?: string; }>; memoryMB?: number; + timeoutSeconds?: number; logging?: { level?: ApplicationLogLevel; retention?: RetentionDays; diff --git a/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts b/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts index 284024a89e4..fefa71aa53b 100644 --- a/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts +++ b/packages/ai-constructs/src/conversation/conversation_handler_construct.test.ts @@ -287,6 +287,68 @@ void describe('Conversation Handler Function construct', () => { }); }); + void describe('timeout property', () => { + void it('sets valid timeout', () => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [], + timeoutSeconds: 124, + }); + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + Timeout: 124, + }); + }); + + void it('sets default timeout', () => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [], + }); + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + Timeout: 60, + }); + }); + + void it('throws on timeout below 1', () => { + assert.throws(() => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [], + timeoutSeconds: 0, + }); + }, new Error('timeoutSeconds must be a whole number between 1 and 900 inclusive')); + }); + + void it('throws on timeout above 15 minutes', () => { + assert.throws(() => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [], + timeoutSeconds: 60 * 15 + 1, + }); + }, new Error('timeoutSeconds must be a whole number between 1 and 900 inclusive')); + }); + + void it('throws on fractional memory', () => { + assert.throws(() => { + const app = new App(); + const stack = new Stack(app); + new ConversationHandlerFunction(stack, 'conversationHandler', { + models: [], + memoryMB: 256.2, + }); + }, new Error('memoryMB must be a whole number between 128 and 10240 inclusive')); + }); + }); + void describe('logging options', () => { void it('sets log level', () => { const app = new App(); diff --git a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts index e5b563a6df9..36c19c66d35 100644 --- a/packages/ai-constructs/src/conversation/conversation_handler_construct.ts +++ b/packages/ai-constructs/src/conversation/conversation_handler_construct.ts @@ -42,6 +42,13 @@ export type ConversationHandlerFunctionProps = { */ memoryMB?: number; + /** + * An amount of time in seconds between 1 second and 15 minutes. + * Must be a whole number. + * Default is 60 seconds. + */ + timeoutSeconds?: number; + logging?: { level?: ApplicationLogLevel; retention?: RetentionDays; @@ -96,7 +103,7 @@ export class ConversationHandlerFunction `conversationHandlerFunction`, { runtime: LambdaRuntime.NODEJS_18_X, - timeout: Duration.seconds(60), + timeout: Duration.seconds(this.resolveTimeout()), entry: this.props.entry ?? defaultHandlerFilePath, handler: 'handler', memorySize: this.resolveMemory(), @@ -185,6 +192,28 @@ export class ConversationHandlerFunction } return this.props.memoryMB; }; + + private resolveTimeout = () => { + const timeoutMin = 1; + const timeoutMax = 60 * 15; // 15 minutes in seconds + const timeoutDefault = 60; + if (this.props.timeoutSeconds === undefined) { + return timeoutDefault; + } + + if ( + !isWholeNumberBetweenInclusive( + this.props.timeoutSeconds, + timeoutMin, + timeoutMax + ) + ) { + throw new Error( + `timeoutSeconds must be a whole number between ${timeoutMin} and ${timeoutMax} inclusive` + ); + } + return this.props.timeoutSeconds; + }; } const isWholeNumberBetweenInclusive = ( diff --git a/packages/backend-ai/API.md b/packages/backend-ai/API.md index fcacb75c61c..a5b3a31b1f4 100644 --- a/packages/backend-ai/API.md +++ b/packages/backend-ai/API.md @@ -71,6 +71,7 @@ type DefineConversationHandlerFunctionProps = { region?: string; }>; memoryMB?: number; + timeoutSeconds?: number; logging?: ConversationHandlerFunctionLoggingOptions; }; diff --git a/packages/backend-ai/src/conversation/factory.test.ts b/packages/backend-ai/src/conversation/factory.test.ts index a264731ffb2..94fc6fefe48 100644 --- a/packages/backend-ai/src/conversation/factory.test.ts +++ b/packages/backend-ai/src/conversation/factory.test.ts @@ -16,6 +16,7 @@ import { customEntryHandler } from './test-assets/with-custom-entry/resource.js' import { Template } from 'aws-cdk-lib/assertions'; import { defineConversationHandlerFunction } from './factory.js'; import { ConversationHandlerFunction } from '@aws-amplify/ai-constructs/conversation'; +import { AmplifyError } from '@aws-amplify/platform-core'; const createStackAndSetContext = (): Stack => { const app = new App(); @@ -204,6 +205,55 @@ void describe('ConversationHandlerFactory', () => { }); }); + void it('maps invalid memory error', () => { + const factory = defineConversationHandlerFunction({ + entry: './test-assets/with-default-entry/handler.ts', + name: 'testHandlerName', + models: [], + memoryMB: -1, + }); + assert.throws( + () => factory.getInstance(getInstanceProps), + (error: Error) => { + assert.ok(AmplifyError.isAmplifyError(error)); + assert.strictEqual(error.name, 'InvalidMemoryMBError'); + return true; + } + ); + }); + + void it('passes timeout setting to construct', () => { + const factory = defineConversationHandlerFunction({ + entry: './test-assets/with-default-entry/handler.ts', + name: 'testHandlerName', + models: [], + timeoutSeconds: 124, + }); + const lambda = factory.getInstance(getInstanceProps); + const template = Template.fromStack(Stack.of(lambda.resources.lambda)); + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('AWS::Lambda::Function', { + Timeout: 124, + }); + }); + + void it('maps invalid timeout error', () => { + const factory = defineConversationHandlerFunction({ + entry: './test-assets/with-default-entry/handler.ts', + name: 'testHandlerName', + models: [], + timeoutSeconds: -1, + }); + assert.throws( + () => factory.getInstance(getInstanceProps), + (error: Error) => { + assert.ok(AmplifyError.isAmplifyError(error)); + assert.strictEqual(error.name, 'InvalidTimeoutError'); + return true; + } + ); + }); + void it('passes log level to construct', () => { const factory = defineConversationHandlerFunction({ entry: './test-assets/with-default-entry/handler.ts', diff --git a/packages/backend-ai/src/conversation/factory.ts b/packages/backend-ai/src/conversation/factory.ts index d5a88c3f03e..fb488a8ac51 100644 --- a/packages/backend-ai/src/conversation/factory.ts +++ b/packages/backend-ai/src/conversation/factory.ts @@ -17,7 +17,10 @@ import { ConversationTurnEventVersion, } from '@aws-amplify/ai-constructs/conversation'; import path from 'path'; -import { CallerDirectoryExtractor } from '@aws-amplify/platform-core'; +import { + AmplifyUserError, + CallerDirectoryExtractor, +} from '@aws-amplify/platform-core'; import { AiModel } from '@aws-amplify/data-schema-types'; import { LogLevelConverter, @@ -52,6 +55,7 @@ class ConversationHandlerFunctionGenerator }), outputStorageStrategy: this.outputStorageStrategy, memoryMB: this.props.memoryMB, + timeoutSeconds: this.props.timeoutSeconds, }; const logging: typeof constructProps.logging = {}; if (this.props.logging?.level) { @@ -65,12 +69,34 @@ class ConversationHandlerFunctionGenerator ); } constructProps.logging = logging; - const conversationHandlerFunction = new ConversationHandlerFunction( - scope, - this.props.name, - constructProps - ); - return conversationHandlerFunction; + try { + return new ConversationHandlerFunction( + scope, + this.props.name, + constructProps + ); + } catch (e) { + throw this.mapConstructErrors(e); + } + }; + + private mapConstructErrors = (e: unknown) => { + if (!(e instanceof Error)) { + return e; + } + if (e.message.startsWith('memoryMB must be')) { + return new AmplifyUserError('InvalidMemoryMBError', { + message: `Invalid memoryMB of ${this.props.memoryMB}`, + resolution: e.message, + }); + } + if (e.message.startsWith('timeoutSeconds must be')) { + return new AmplifyUserError('InvalidTimeoutError', { + message: `Invalid timeout of ${this.props.timeoutSeconds} seconds`, + resolution: e.message, + }); + } + return e; }; } @@ -155,6 +181,12 @@ export type DefineConversationHandlerFunctionProps = { * Default is 512MB. */ memoryMB?: number; + /** + * An amount of time in seconds between 1 second and 15 minutes. + * Must be a whole number. + * Default is 60 seconds. + */ + timeoutSeconds?: number; logging?: ConversationHandlerFunctionLoggingOptions; }; From 6dd93d17fd04df3bcc2984682a2aa6b383c9a445 Mon Sep 17 00:00:00 2001 From: Kamil Sobol Date: Thu, 19 Dec 2024 12:24:58 -0800 Subject: [PATCH 173/199] Test on Node 22. (#2347) * Node 22 * this * this * this * this * this * Revert "this" This reverts commit 476231dcb0ff2374dcee49d05718feece5fbde76. * Revert "this" This reverts commit b909e6a78a3766f6900de86f9234523dddd3e04e. * Revert "this" This reverts commit c68ed7c67f0a672a3c638614f4e3b1d67ffce0fa. * Revert "this" This reverts commit 1206b3777f75f29a672ad0569af303a1134d091d. * Revert "this" This reverts commit 486b8b1802a0482e743ae2938b04bf82f0a60c12. * try this * try this * boop * boop * try this --- .github/workflows/health_checks.yml | 12 ++++++++---- package.json | 2 +- scripts/get_unit_test_dir_list.ts | 1 + scripts/run_tests.ts | 21 +++++++++++++++++++++ 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 scripts/run_tests.ts diff --git a/.github/workflows/health_checks.yml b/.github/workflows/health_checks.yml index 22bc67ab861..f7340267475 100644 --- a/.github/workflows/health_checks.yml +++ b/.github/workflows/health_checks.yml @@ -47,7 +47,7 @@ on: description: 'Node versions list (as JSON array).' required: false type: string - default: '["18", "20"]' + default: '["18", "20", "22"]' workflow_call: inputs: desired-cdk-version: @@ -78,7 +78,7 @@ on: description: 'Node versions list (as JSON array).' required: false type: string - default: '["18", "20"]' + default: '["18", "20", "22"]' env: # Health checks can run on un-released code. Often work in progress. @@ -121,7 +121,7 @@ jobs: echo "os=$os" >> "$GITHUB_OUTPUT" echo "os_for_e2e=$os_for_e2e" >> "$GITHUB_OUTPUT" if [ -z "${{ inputs.node }}" ]; then - echo 'node=["18", "20"]' >> "$GITHUB_OUTPUT" + echo 'node=["18", "20", "22"]' >> "$GITHUB_OUTPUT" else echo 'node=${{ inputs.node }}' >> "$GITHUB_OUTPUT" fi @@ -512,6 +512,10 @@ jobs: node-version: 20 - os: windows-latest node-version: 20 + - os: macos-14 + node-version: 22 + - os: windows-latest + node-version: 22 runs-on: ${{ matrix.os }} timeout-minutes: ${{ matrix.os == 'windows-latest' && 35 || 25 }} needs: @@ -538,7 +542,7 @@ jobs: matrix: os: ${{ fromJSON(needs.resolve_inputs.outputs.os) }} pkg-manager: [npm, yarn-classic, yarn-modern, pnpm] - node-version: ['20'] + node-version: ['22'] env: PACKAGE_MANAGER: ${{ matrix.pkg-manager }} runs-on: ${{ matrix.os }} diff --git a/package.json b/package.json index 56f0ad37f5a..064c496cf6d 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "test": "npm run test:dir $(tsx scripts/get_unit_test_dir_list.ts)", "test:coverage:generate": "NODE_V8_COVERAGE=coverage/ npm run test", "test:coverage:threshold": "c8 npm run test", - "test:dir": "tsx --test --test-reporter spec", + "test:dir": "tsx scripts/run_tests.ts", "test:scripts": "npm run test:dir $(glob --cwd=scripts --absolute **/*.test.ts)", "update:api": "tsx scripts/concurrent_workspace_script.ts update:api --if-present", "update:tsconfig-refs": "tsx scripts/update_tsconfig_refs.ts", diff --git a/scripts/get_unit_test_dir_list.ts b/scripts/get_unit_test_dir_list.ts index 50b4def5f17..0c962d47d12 100644 --- a/scripts/get_unit_test_dir_list.ts +++ b/scripts/get_unit_test_dir_list.ts @@ -6,4 +6,5 @@ result = result.filter((result) => !result.includes('integration-tests')); result.push( path.join('packages', 'integration-tests', 'lib', 'test-in-memory') ); + console.log(result.join(' ')); diff --git a/scripts/run_tests.ts b/scripts/run_tests.ts new file mode 100644 index 00000000000..47f0c02fdfa --- /dev/null +++ b/scripts/run_tests.ts @@ -0,0 +1,21 @@ +import { execa } from 'execa'; +import fs from 'fs'; +import semver from 'semver'; + +let testPaths = process.argv.slice(2); + +const nodeVersion = semver.parse(process.versions.node); +if (nodeVersion && nodeVersion.major >= 21) { + // Starting from version 21. Node test runner's cli changed how inputs to test CLI work. + // See https://github.com/nodejs/node/issues/50219. + testPaths = testPaths.map((path) => { + if (fs.existsSync(path) && fs.statSync(path).isDirectory()) { + return `${path}/**/*.test.?(c|m)js`; + } + return path; + }); +} + +await execa('tsx', ['--test', '--test-reporter', 'spec'].concat(testPaths), { + stdio: 'inherit', +}); From e7f415cee5d84f4494538c2583af62a058707cdb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:28:27 -0800 Subject: [PATCH 174/199] Version Packages (#2343) Co-authored-by: github-actions[bot] --- .changeset/few-oranges-tell.md | 6 ------ .changeset/itchy-flowers-reply.md | 6 ------ .changeset/old-hornets-type.md | 5 ----- .changeset/plenty-mugs-learn.md | 2 -- .changeset/strong-toes-sniff.md | 6 ------ .changeset/swift-mirrors-enjoy.md | 5 ----- packages/ai-constructs/CHANGELOG.md | 6 ++++++ packages/ai-constructs/package.json | 2 +- packages/backend-ai/CHANGELOG.md | 11 +++++++++++ packages/backend-ai/package.json | 4 ++-- packages/backend-data/CHANGELOG.md | 10 ++++++++++ packages/backend-data/package.json | 2 +- packages/backend/CHANGELOG.md | 13 +++++++++++++ packages/backend/package.json | 4 ++-- packages/cli/CHANGELOG.md | 8 ++++++++ packages/cli/package.json | 4 ++-- packages/model-generator/CHANGELOG.md | 6 ++++++ packages/model-generator/package.json | 2 +- 18 files changed, 63 insertions(+), 39 deletions(-) delete mode 100644 .changeset/few-oranges-tell.md delete mode 100644 .changeset/itchy-flowers-reply.md delete mode 100644 .changeset/old-hornets-type.md delete mode 100644 .changeset/plenty-mugs-learn.md delete mode 100644 .changeset/strong-toes-sniff.md delete mode 100644 .changeset/swift-mirrors-enjoy.md diff --git a/.changeset/few-oranges-tell.md b/.changeset/few-oranges-tell.md deleted file mode 100644 index d0ed9aebf0a..00000000000 --- a/.changeset/few-oranges-tell.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/ai-constructs': minor -'@aws-amplify/backend-ai': minor ---- - -Expose timeout property diff --git a/.changeset/itchy-flowers-reply.md b/.changeset/itchy-flowers-reply.md deleted file mode 100644 index 1717d3691c8..00000000000 --- a/.changeset/itchy-flowers-reply.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/backend-data': patch -'@aws-amplify/backend': patch ---- - -Allow apiKeyAuthorizationMode to be undefined if defaultAuthorizationMode is apiKey diff --git a/.changeset/old-hornets-type.md b/.changeset/old-hornets-type.md deleted file mode 100644 index b0403c5576c..00000000000 --- a/.changeset/old-hornets-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-cli': patch ---- - -add validation for sandbox set command secret-name diff --git a/.changeset/plenty-mugs-learn.md b/.changeset/plenty-mugs-learn.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/plenty-mugs-learn.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/strong-toes-sniff.md b/.changeset/strong-toes-sniff.md deleted file mode 100644 index f20c42c831f..00000000000 --- a/.changeset/strong-toes-sniff.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/backend-data': minor -'@aws-amplify/backend': minor ---- - -Add GraphQL API ID and Amplify environment name to custom JS resolver stash diff --git a/.changeset/swift-mirrors-enjoy.md b/.changeset/swift-mirrors-enjoy.md deleted file mode 100644 index ae118767c3e..00000000000 --- a/.changeset/swift-mirrors-enjoy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/model-generator': patch ---- - -Updated error handling with S3 Client diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index bddc654159b..e5726bb44cd 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/ai-constructs +## 1.2.0 + +### Minor Changes + +- a66f5f2: Expose timeout property + ## 1.1.0 ### Minor Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index 93f1966ed21..d8692ff3bac 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "1.1.0", + "version": "1.2.0", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index 9e5bac6381b..a46001a7e52 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend-ai +## 1.2.0 + +### Minor Changes + +- a66f5f2: Expose timeout property + +### Patch Changes + +- Updated dependencies [a66f5f2] + - @aws-amplify/ai-constructs@1.2.0 + ## 1.1.0 ### Minor Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 40507169dc1..8e00334af05 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "1.1.0", + "version": "1.2.0", "type": "module", "publishConfig": { "access": "public" @@ -22,7 +22,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^1.1.0", + "@aws-amplify/ai-constructs": "^1.2.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/data-schema-types": "^1.2.0", diff --git a/packages/backend-data/CHANGELOG.md b/packages/backend-data/CHANGELOG.md index 51252b80104..6acf615618d 100644 --- a/packages/backend-data/CHANGELOG.md +++ b/packages/backend-data/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/backend-data +## 1.3.0 + +### Minor Changes + +- fbf209e: Add GraphQL API ID and Amplify environment name to custom JS resolver stash + +### Patch Changes + +- 07fe7d4: Allow apiKeyAuthorizationMode to be undefined if defaultAuthorizationMode is apiKey + ## 1.2.3 ### Patch Changes diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index 98612cfa782..e13edcf0be6 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-data", - "version": "1.2.3", + "version": "1.3.0", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index e11c77e6b59..cf0f756dd50 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/backend +## 1.11.0 + +### Minor Changes + +- fbf209e: Add GraphQL API ID and Amplify environment name to custom JS resolver stash + +### Patch Changes + +- 07fe7d4: Allow apiKeyAuthorizationMode to be undefined if defaultAuthorizationMode is apiKey +- Updated dependencies [07fe7d4] +- Updated dependencies [fbf209e] + - @aws-amplify/backend-data@1.3.0 + ## 1.10.0 ### Minor Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 94784fd2d85..834966d38a1 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.10.0", + "version": "1.11.0", "type": "module", "publishConfig": { "access": "public" @@ -33,7 +33,7 @@ "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/backend-auth": "^1.4.2", "@aws-amplify/backend-function": "^1.10.0", - "@aws-amplify/backend-data": "^1.2.3", + "@aws-amplify/backend-data": "^1.3.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/backend-secret": "^1.1.4", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 74f5a07e09a..f90b68b64d3 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/backend-cli +## 1.4.5 + +### Patch Changes + +- 0ef53e2: add validation for sandbox set command secret-name +- Updated dependencies [107600b] + - @aws-amplify/model-generator@1.0.11 + ## 1.4.4 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index e385909af54..72122edab6d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-cli", - "version": "1.4.4", + "version": "1.4.5", "description": "Command line interface for various Amplify tools", "bin": { "ampx": "lib/ampx.js", @@ -38,7 +38,7 @@ "@aws-amplify/client-config": "^1.5.4", "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/form-generator": "^1.0.3", - "@aws-amplify/model-generator": "^1.0.10", + "@aws-amplify/model-generator": "^1.0.11", "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.6.0", "@aws-amplify/sandbox": "^1.2.8", diff --git a/packages/model-generator/CHANGELOG.md b/packages/model-generator/CHANGELOG.md index 7563e878910..e85bfc57a75 100644 --- a/packages/model-generator/CHANGELOG.md +++ b/packages/model-generator/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/model-generator +## 1.0.11 + +### Patch Changes + +- 107600b: Updated error handling with S3 Client + ## 1.0.10 ### Patch Changes diff --git a/packages/model-generator/package.json b/packages/model-generator/package.json index 21647b8726b..8dd4ed67c33 100644 --- a/packages/model-generator/package.json +++ b/packages/model-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/model-generator", - "version": "1.0.10", + "version": "1.0.11", "type": "module", "publishConfig": { "access": "public" From b574bce26ec2d55a4391aeceecc1d4957e79fba3 Mon Sep 17 00:00:00 2001 From: Leena <81589006+ShadowCat567@users.noreply.github.com> Date: Thu, 19 Dec 2024 18:50:59 -0500 Subject: [PATCH 175/199] adjustment to copy-template (#2351) Co-authored-by: Vieltojarvi --- scripts/copy_template.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/copy_template.ts b/scripts/copy_template.ts index 40cb9ccf907..edb1a45a082 100644 --- a/scripts/copy_template.ts +++ b/scripts/copy_template.ts @@ -24,14 +24,12 @@ if (!values?.name || !values?.template) { } const sourcePath = path.resolve( - new URL('.', import.meta.url).pathname, - '..', + new URL('.', import.meta.url).host, 'templates', values.template as string ); const destPath = path.resolve( - new URL('.', import.meta.url).pathname, - '..', + new URL('.', import.meta.url).host, 'packages', values.name as string ); From abff5a0f10922cdd83a612131b8bdfb006eb2c53 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Mon, 23 Dec 2024 19:55:38 +0100 Subject: [PATCH 176/199] chore: wrap out of memory errors (#2365) --- .changeset/short-schools-act.md | 5 +++++ .../src/errors/amplify_error.test.ts | 11 +++++++++++ .../platform-core/src/errors/amplify_error.ts | 15 +++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 .changeset/short-schools-act.md diff --git a/.changeset/short-schools-act.md b/.changeset/short-schools-act.md new file mode 100644 index 00000000000..da6857ad9e2 --- /dev/null +++ b/.changeset/short-schools-act.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/platform-core': patch +--- + +add InsufficientMemorySpaceError wrapping diff --git a/packages/platform-core/src/errors/amplify_error.test.ts b/packages/platform-core/src/errors/amplify_error.test.ts index 8ecc5d838d7..bfb9feec56e 100644 --- a/packages/platform-core/src/errors/amplify_error.test.ts +++ b/packages/platform-core/src/errors/amplify_error.test.ts @@ -234,4 +234,15 @@ void describe('AmplifyError.fromError', async () => { assert.deepStrictEqual(error, actual); assert.strictEqual(actual.resolution, error.resolution); }); + void it('wraps InsufficientMemorySpaceError in AmplifyUserError', () => { + const error = new Error( + 'FATAL ERROR: Zone Allocation failed - process out of memory.' + ); + const actual = AmplifyError.fromError(error); + assert.ok( + AmplifyError.isAmplifyError(actual) && + actual.name === 'InsufficientMemorySpaceError', + `Failed the test for error ${error.message}` + ); + }); }); diff --git a/packages/platform-core/src/errors/amplify_error.ts b/packages/platform-core/src/errors/amplify_error.ts index 76821f8c63f..e2a2c1c1a46 100644 --- a/packages/platform-core/src/errors/amplify_error.ts +++ b/packages/platform-core/src/errors/amplify_error.ts @@ -186,6 +186,17 @@ export abstract class AmplifyError extends Error { error ); } + if (error instanceof Error && isOutOfMemoryError(error)) { + return new AmplifyUserError( + 'InsufficientMemorySpaceError', + { + message: error.message, + resolution: + 'There appears to be insufficient memory on your system to finish. Close other applications or restart your system and try again.', + }, + error + ); + } return new AmplifyFault( 'UnknownFault', { @@ -245,6 +256,10 @@ const isInsufficientDiskSpaceError = (err?: Error): boolean => { ); }; +const isOutOfMemoryError = (err?: Error): boolean => { + return !!err && err.message.includes('process out of memory'); +}; + /** * Amplify exception classifications */ From 642b44176d5b9dc6159727fafaeab3f120769ef3 Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Mon, 23 Dec 2024 19:55:47 +0100 Subject: [PATCH 177/199] chore: catch and wrap deployment in progress when deleting the backend (#2366) --- .changeset/new-maps-lay.md | 5 +++++ packages/backend-deployer/src/cdk_error_mapper.test.ts | 7 +++++++ packages/backend-deployer/src/cdk_error_mapper.ts | 10 ++++++++++ 3 files changed, 22 insertions(+) create mode 100644 .changeset/new-maps-lay.md diff --git a/.changeset/new-maps-lay.md b/.changeset/new-maps-lay.md new file mode 100644 index 00000000000..e39a85c9457 --- /dev/null +++ b/.changeset/new-maps-lay.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +catch and wrap deployment in progress when deleting the backend diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 3b5611bf157..fbb5a331070 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -679,6 +679,13 @@ npm error enoent`, errorName: 'CloudformationResourceCircularDependencyError', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: `destroy failed Error: Stack [someStackArn] cannot be deleted while in status UPDATE_COMPLETE_CLEANUP_IN_PROGRESS`, + expectedTopLevelErrorMessage: + 'Backend failed to be deleted since the previous deployment is still in progress.', + errorName: 'DeleteFailedWhileDeploymentInProgressError', + expectedDownstreamErrorMessage: undefined, + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index bf1694e4ec9..f1917069268 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -471,6 +471,15 @@ export class CdkErrorMapper { errorName: 'CDKAssetPublishError', classification: 'ERROR', }, + { + // We capture the parameter name to show relevant error message + errorRegex: + /destroy failed Error: Stack \[(?.*)\] cannot be deleted while in status /, + humanReadableErrorMessage: `Backend failed to be deleted since the previous deployment is still in progress.`, + resolutionMessage: `Wait for the previous deployment for stack {stackArn} to be completed before attempting to delete again.`, + errorName: 'DeleteFailedWhileDeploymentInProgressError', + classification: 'ERROR', + }, // Generic error printed by CDK. Order matters so keep this towards the bottom of this list { // Error: .* is printed to stderr during cdk synth @@ -554,6 +563,7 @@ export type CDKDeploymentError = | 'CloudFormationDeletionError' | 'CloudFormationDeploymentError' | 'CommonNPMError' + | 'DeleteFailedWhileDeploymentInProgressError' | 'FilePermissionsError' | 'MissingDefineBackendError' | 'MultipleSandboxInstancesError' From aaeda9b0926de1f9e49c7000dbb18438160442ab Mon Sep 17 00:00:00 2001 From: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> Date: Mon, 30 Dec 2024 12:11:59 +0100 Subject: [PATCH 178/199] chore: wraps no outputs found error from backend output client (#2368) --- .changeset/chilly-pillows-live.md | 8 ++ .../forms/generate_forms_command.test.ts | 54 +++++++ .../generate/forms/generate_forms_command.ts | 114 +++++++-------- .../unified_client_config_generator.test.ts | 33 +++++ .../src/unified_client_config_generator.ts | 136 +++++++++--------- .../create_graphql_document_generator.test.ts | 31 ++++ .../create_graphql_models_generator.test.ts | 31 ++++ .../create_graphql_types_generator.test.ts | 31 ++++ .../get_backend_output_with_error_handling.ts | 112 +++++++-------- .../src/lambda_function_log_streamer.test.ts | 17 +++ .../src/lambda_function_log_streamer.ts | 7 +- 11 files changed, 388 insertions(+), 186 deletions(-) create mode 100644 .changeset/chilly-pillows-live.md diff --git a/.changeset/chilly-pillows-live.md b/.changeset/chilly-pillows-live.md new file mode 100644 index 00000000000..da53db4b3b7 --- /dev/null +++ b/.changeset/chilly-pillows-live.md @@ -0,0 +1,8 @@ +--- +'@aws-amplify/model-generator': patch +'@aws-amplify/client-config': patch +'@aws-amplify/sandbox': patch +'@aws-amplify/backend-cli': patch +--- + +wraps no outputs found error from backend output client diff --git a/packages/cli/src/commands/generate/forms/generate_forms_command.test.ts b/packages/cli/src/commands/generate/forms/generate_forms_command.test.ts index d5bcd498c74..de53013e1b0 100644 --- a/packages/cli/src/commands/generate/forms/generate_forms_command.test.ts +++ b/packages/cli/src/commands/generate/forms/generate_forms_command.test.ts @@ -344,6 +344,60 @@ void describe('generate forms command', () => { ); }); + void it('throws user error if the stack outputs are undefined', async () => { + const fakeSandboxId = 'my-fake-app-my-fake-username'; + const backendIdResolver = { + resolveDeployedBackendIdentifier: mock.fn(() => + Promise.resolve({ + namespace: fakeSandboxId, + name: fakeSandboxId, + type: 'sandbox', + }) + ), + resolveBackendIdentifier: mock.fn(() => + Promise.resolve({ + namespace: fakeSandboxId, + name: fakeSandboxId, + type: 'sandbox', + }) + ), + } as BackendIdentifierResolver; + const formGenerationHandler = new FormGenerationHandler({ + awsClientProvider, + }); + + const fakedBackendOutputClient = { + getOutput: mock.fn(() => { + throw new BackendOutputClientError( + BackendOutputClientErrorType.NO_OUTPUTS_FOUND, + 'stack outputs are undefined' + ); + }), + }; + + const generateFormsCommand = new GenerateFormsCommand( + backendIdResolver, + () => fakedBackendOutputClient, + formGenerationHandler + ); + + const parser = yargs().command( + generateFormsCommand as unknown as CommandModule + ); + const commandRunner = new TestCommandRunner(parser); + await assert.rejects( + () => commandRunner.runCommand('forms'), + (error: TestCommandError) => { + assert.strictEqual(error.error.name, 'AmplifyOutputsNotFoundError'); + assert.strictEqual( + error.error.message, + 'Amplify outputs not found in stack metadata' + ); + return true; + } + ); + }); + void it('throws user error if credentials are expired when getting backend outputs', async () => { const fakeSandboxId = 'my-fake-app-my-fake-username'; const backendIdResolver = { diff --git a/packages/cli/src/commands/generate/forms/generate_forms_command.ts b/packages/cli/src/commands/generate/forms/generate_forms_command.ts index b9597e769ac..1977c74f346 100644 --- a/packages/cli/src/commands/generate/forms/generate_forms_command.ts +++ b/packages/cli/src/commands/generate/forms/generate_forms_command.ts @@ -82,64 +82,64 @@ export class GenerateFormsCommand try { output = await backendOutputClient.getOutput(backendIdentifier); } catch (error) { - if ( - BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS - ) { - throw new AmplifyUserError( - 'DeploymentInProgressError', - { - message: 'Deployment is currently in progress.', - resolution: 'Re-run this command once the deployment completes.', - }, - error - ); + if (BackendOutputClientError.isBackendOutputClientError(error)) { + switch (error.code) { + case BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS: + throw new AmplifyUserError( + 'DeploymentInProgressError', + { + message: 'Deployment is currently in progress.', + resolution: + 'Re-run this command once the deployment completes.', + }, + error + ); + case BackendOutputClientErrorType.NO_STACK_FOUND: + throw new AmplifyUserError( + 'StackDoesNotExistError', + { + message: 'Stack does not exist.', + resolution: + 'Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists, then re-run this command.', + }, + error + ); + case BackendOutputClientErrorType.NO_OUTPUTS_FOUND: + throw new AmplifyUserError( + 'AmplifyOutputsNotFoundError', + { + message: 'Amplify outputs not found in stack metadata', + resolution: `Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists. + If this is a new sandbox or branch deployment, wait for the deployment to be successfully finished and try again.`, + }, + error + ); + case BackendOutputClientErrorType.CREDENTIALS_ERROR: + throw new AmplifyUserError( + 'CredentialsError', + { + message: + 'Unable to get backend outputs due to invalid credentials.', + resolution: + 'Ensure your AWS credentials are correctly set and refreshed.', + }, + error + ); + case BackendOutputClientErrorType.ACCESS_DENIED: + throw new AmplifyUserError( + 'AccessDeniedError', + { + message: + 'Unable to get backend outputs due to insufficient permissions.', + resolution: + 'Ensure you have permissions to call cloudformation:GetTemplateSummary.', + }, + error + ); + default: + throw error; + } } - if ( - BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.NO_STACK_FOUND - ) { - throw new AmplifyUserError( - 'StackDoesNotExistError', - { - message: 'Stack does not exist.', - resolution: - 'Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists, then re-run this command.', - }, - error - ); - } - if ( - BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.CREDENTIALS_ERROR - ) { - throw new AmplifyUserError( - 'CredentialsError', - { - message: - 'Unable to get backend outputs due to invalid credentials.', - resolution: - 'Ensure your AWS credentials are correctly set and refreshed.', - }, - error - ); - } - if ( - BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.ACCESS_DENIED - ) { - throw new AmplifyUserError( - 'AccessDeniedError', - { - message: - 'Unable to get backend outputs due to insufficient permissions.', - resolution: - 'Ensure you have permissions to call cloudformation:GetTemplateSummary.', - }, - error - ); - } - throw error; } diff --git a/packages/client-config/src/unified_client_config_generator.test.ts b/packages/client-config/src/unified_client_config_generator.test.ts index 496d59df9d1..b82c9620963 100644 --- a/packages/client-config/src/unified_client_config_generator.test.ts +++ b/packages/client-config/src/unified_client_config_generator.test.ts @@ -692,6 +692,39 @@ void describe('UnifiedClientConfigGenerator', () => { ); }); + void it('throws user error if the stack outputs are undefined', async () => { + const outputRetrieval = mock.fn(() => { + throw new BackendOutputClientError( + BackendOutputClientErrorType.NO_OUTPUTS_FOUND, + 'stack outputs are undefined' + ); + }); + const modelSchemaAdapter = new ModelIntrospectionSchemaAdapter( + stubClientProvider + ); + + const configContributors = new ClientConfigContributorFactory( + modelSchemaAdapter + ).getContributors('1.3'); + + const clientConfigGenerator = new UnifiedClientConfigGenerator( + outputRetrieval, + configContributors + ); + + await assert.rejects( + () => clientConfigGenerator.generateClientConfig(), + (error: AmplifyUserError) => { + assert.strictEqual( + error.message, + 'Amplify outputs not found in stack metadata' + ); + assert.ok(error.resolution); + return true; + } + ); + }); + void it('throws user error if the stack is missing metadata', async () => { const outputRetrieval = mock.fn(() => { throw new BackendOutputClientError( diff --git a/packages/client-config/src/unified_client_config_generator.ts b/packages/client-config/src/unified_client_config_generator.ts index fc33c316cca..068b2c03493 100644 --- a/packages/client-config/src/unified_client_config_generator.ts +++ b/packages/client-config/src/unified_client_config_generator.ts @@ -39,78 +39,72 @@ export class UnifiedClientConfigGenerator implements ClientConfigGenerator { try { output = await this.fetchOutput(); } catch (error) { - if ( - BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS - ) { - throw new AmplifyUserError( - 'DeploymentInProgressError', - { - message: 'Deployment is currently in progress.', - resolution: 'Re-run this command once the deployment completes.', - }, - error - ); - } - if ( - BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.NO_STACK_FOUND - ) { - throw new AmplifyUserError( - 'StackDoesNotExistError', - { - message: 'Stack does not exist.', - resolution: - 'Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists, then re-run this command.', - }, - error - ); - } - if ( - BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.METADATA_RETRIEVAL_ERROR - ) { - throw new AmplifyUserError( - 'NonAmplifyStackError', - { - message: 'Stack was not created with Amplify.', - resolution: - 'Ensure the CloudFormation stack ID references a main stack created with Amplify, then re-run this command.', - }, - error - ); - } - if ( - BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.CREDENTIALS_ERROR - ) { - throw new AmplifyUserError( - 'CredentialsError', - { - message: - 'Unable to get backend outputs due to invalid credentials.', - resolution: - 'Ensure your AWS credentials are correctly set and refreshed.', - }, - error - ); - } - if ( - BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.ACCESS_DENIED - ) { - throw new AmplifyUserError( - 'AccessDeniedError', - { - message: - 'Unable to get backend outputs due to insufficient permissions.', - resolution: - 'Ensure you have permissions to call cloudformation:GetTemplateSummary.', - }, - error - ); + if (BackendOutputClientError.isBackendOutputClientError(error)) { + switch (error.code) { + case BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS: + throw new AmplifyUserError( + 'DeploymentInProgressError', + { + message: 'Deployment is currently in progress.', + resolution: + 'Re-run this command once the deployment completes.', + }, + error + ); + case BackendOutputClientErrorType.NO_STACK_FOUND: + throw new AmplifyUserError( + 'StackDoesNotExistError', + { + message: 'Stack does not exist.', + resolution: + 'Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists, then re-run this command.', + }, + error + ); + case BackendOutputClientErrorType.METADATA_RETRIEVAL_ERROR: + throw new AmplifyUserError( + 'NonAmplifyStackError', + { + message: 'Stack was not created with Amplify.', + resolution: + 'Ensure the CloudFormation stack ID references a main stack created with Amplify, then re-run this command.', + }, + error + ); + case BackendOutputClientErrorType.NO_OUTPUTS_FOUND: + throw new AmplifyUserError( + 'AmplifyOutputsNotFoundError', + { + message: 'Amplify outputs not found in stack metadata', + resolution: `Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists. + If this is a new sandbox or branch deployment, wait for the deployment to be successfully finished and try again.`, + }, + error + ); + case BackendOutputClientErrorType.CREDENTIALS_ERROR: + throw new AmplifyUserError( + 'CredentialsError', + { + message: + 'Unable to get backend outputs due to invalid credentials.', + resolution: + 'Ensure your AWS credentials are correctly set and refreshed.', + }, + error + ); + case BackendOutputClientErrorType.ACCESS_DENIED: + throw new AmplifyUserError( + 'AccessDeniedError', + { + message: + 'Unable to get backend outputs due to insufficient permissions.', + resolution: + 'Ensure you have permissions to call cloudformation:GetTemplateSummary.', + }, + error + ); + } } - throw error; } const backendOutput = unifiedBackendOutputSchema.parse(output); diff --git a/packages/model-generator/src/create_graphql_document_generator.test.ts b/packages/model-generator/src/create_graphql_document_generator.test.ts index d3071f0d027..5df2c3a43c3 100644 --- a/packages/model-generator/src/create_graphql_document_generator.test.ts +++ b/packages/model-generator/src/create_graphql_document_generator.test.ts @@ -100,6 +100,37 @@ void describe('model generator factory', () => { ); }); + void it('throws an error if outputs do not exist', async () => { + const fakeBackendOutputClient = { + getOutput: mock.fn(() => { + throw new BackendOutputClientError( + BackendOutputClientErrorType.NO_OUTPUTS_FOUND, + 'stack outputs are undefined' + ); + }), + }; + mock.method( + BackendOutputClientFactory, + 'getInstance', + () => fakeBackendOutputClient + ); + const generator = createGraphqlDocumentGenerator({ + backendIdentifier: { stackName: 'stackThatDoesNotHaveOutputs' }, + awsClientProvider, + }); + await assert.rejects( + () => generator.generateModels({ targetFormat: 'javascript' }), + (error: AmplifyUserError) => { + assert.strictEqual( + error.message, + 'Amplify outputs not found in stack metadata' + ); + assert.ok(error.resolution); + return true; + } + ); + }); + void it('throws an error if credentials are expired when getting backend outputs', async () => { const fakeBackendOutputClient = { getOutput: mock.fn(() => { diff --git a/packages/model-generator/src/create_graphql_models_generator.test.ts b/packages/model-generator/src/create_graphql_models_generator.test.ts index 615ac7e655c..86aeb3deb25 100644 --- a/packages/model-generator/src/create_graphql_models_generator.test.ts +++ b/packages/model-generator/src/create_graphql_models_generator.test.ts @@ -105,6 +105,37 @@ void describe('models generator factory', () => { ); }); + void it('throws an error if stack outputs are undefined', async () => { + const fakeBackendOutputClient = { + getOutput: mock.fn(() => { + throw new BackendOutputClientError( + BackendOutputClientErrorType.NO_OUTPUTS_FOUND, + 'stack outputs are undefined' + ); + }), + }; + mock.method( + BackendOutputClientFactory, + 'getInstance', + () => fakeBackendOutputClient + ); + const generator = createGraphqlModelsGenerator({ + backendIdentifier: { stackName: 'stackThatDoesNotHaveOutputs' }, + awsClientProvider, + }); + await assert.rejects( + () => generator.generateModels({ target: 'javascript' }), + (error: AmplifyUserError) => { + assert.strictEqual( + error.message, + 'Amplify outputs not found in stack metadata' + ); + assert.ok(error.resolution); + return true; + } + ); + }); + void it('throws an error if credentials are expired when getting backend outputs', async () => { const fakeBackendOutputClient = { getOutput: mock.fn(() => { diff --git a/packages/model-generator/src/create_graphql_types_generator.test.ts b/packages/model-generator/src/create_graphql_types_generator.test.ts index 50981b498d1..ab889cff597 100644 --- a/packages/model-generator/src/create_graphql_types_generator.test.ts +++ b/packages/model-generator/src/create_graphql_types_generator.test.ts @@ -99,6 +99,37 @@ void describe('types generator factory', () => { ); }); + void it('throws an AmplifyUserError if stack outputs are undefined', async () => { + const fakeBackendOutputClient = { + getOutput: mock.fn(() => { + throw new BackendOutputClientError( + BackendOutputClientErrorType.NO_OUTPUTS_FOUND, + 'stack outputs are undefined' + ); + }), + }; + mock.method( + BackendOutputClientFactory, + 'getInstance', + () => fakeBackendOutputClient + ); + const generator = createGraphqlTypesGenerator({ + backendIdentifier: { stackName: 'stackThatDoesNotHaveOutputs' }, + awsClientProvider, + }); + await assert.rejects( + () => generator.generateTypes({ target: 'json' }), + (error: AmplifyUserError) => { + assert.strictEqual( + error.message, + 'Amplify outputs not found in stack metadata' + ); + assert.ok(error.resolution); + return true; + } + ); + }); + void it('throws an AmplifyUserError if credentials are expired when getting backend outputs', async () => { const fakeBackendOutputClient = { getOutput: mock.fn(() => { diff --git a/packages/model-generator/src/get_backend_output_with_error_handling.ts b/packages/model-generator/src/get_backend_output_with_error_handling.ts index fc45ed295f9..6dba118eae8 100644 --- a/packages/model-generator/src/get_backend_output_with_error_handling.ts +++ b/packages/model-generator/src/get_backend_output_with_error_handling.ts @@ -16,63 +16,63 @@ export const getBackendOutputWithErrorHandling = async ( try { return await backendOutputClient.getOutput(backendIdentifier); } catch (error) { - if ( - BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS - ) { - throw new AmplifyUserError( - 'DeploymentInProgressError', - { - message: 'Deployment is currently in progress.', - resolution: 'Re-run this command once the deployment completes.', - }, - error - ); + if (BackendOutputClientError.isBackendOutputClientError(error)) { + switch (error.code) { + case BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS: + throw new AmplifyUserError( + 'DeploymentInProgressError', + { + message: 'Deployment is currently in progress.', + resolution: 'Re-run this command once the deployment completes.', + }, + error + ); + case BackendOutputClientErrorType.NO_STACK_FOUND: + throw new AmplifyUserError( + 'StackDoesNotExistError', + { + message: 'Stack does not exist.', + resolution: + 'Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists, then re-run this command.', + }, + error + ); + case BackendOutputClientErrorType.NO_OUTPUTS_FOUND: + throw new AmplifyUserError( + 'AmplifyOutputsNotFoundError', + { + message: 'Amplify outputs not found in stack metadata', + resolution: `Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists. + If this is a new sandbox or branch deployment, wait for the deployment to be successfully finished and try again.`, + }, + error + ); + case BackendOutputClientErrorType.CREDENTIALS_ERROR: + throw new AmplifyUserError( + 'CredentialsError', + { + message: + 'Unable to get backend outputs due to invalid credentials.', + resolution: + 'Ensure your AWS credentials are correctly set and refreshed.', + }, + error + ); + case BackendOutputClientErrorType.ACCESS_DENIED: + throw new AmplifyUserError( + 'AccessDeniedError', + { + message: + 'Unable to get backend outputs due to insufficient permissions.', + resolution: + 'Ensure you have permissions to call cloudformation:GetTemplateSummary.', + }, + error + ); + default: + throw error; + } } - if ( - BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.NO_STACK_FOUND - ) { - throw new AmplifyUserError( - 'StackDoesNotExistError', - { - message: 'Stack does not exist.', - resolution: - 'Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists, then re-run this command.', - }, - error - ); - } - if ( - BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.CREDENTIALS_ERROR - ) { - throw new AmplifyUserError( - 'CredentialsError', - { - message: 'Unable to get backend outputs due to invalid credentials.', - resolution: - 'Ensure your AWS credentials are correctly set and refreshed.', - }, - error - ); - } - if ( - BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.ACCESS_DENIED - ) { - throw new AmplifyUserError( - 'AccessDeniedError', - { - message: - 'Unable to get backend outputs due to insufficient permissions.', - resolution: - 'Ensure you have permissions to call cloudformation:GetTemplateSummary.', - }, - error - ); - } - throw error; } }; diff --git a/packages/sandbox/src/lambda_function_log_streamer.test.ts b/packages/sandbox/src/lambda_function_log_streamer.test.ts index dfd98eb1198..a6e24b1a4f8 100644 --- a/packages/sandbox/src/lambda_function_log_streamer.test.ts +++ b/packages/sandbox/src/lambda_function_log_streamer.test.ts @@ -168,6 +168,23 @@ void describe('LambdaFunctionLogStreamer', () => { assert.strictEqual(lambdaClientSendMock.mock.callCount(), 0); }); + void it('return early if backend output client throws with outputs do not exist', async () => { + backendOutputClientMock.getOutput.mock.mockImplementationOnce(() => { + return Promise.reject( + new BackendOutputClientError( + BackendOutputClientErrorType.NO_OUTPUTS_FOUND, + 'Stack outputs are undefined' + ) + ); + }); + await classUnderTest.startStreamingLogs(testSandboxBackendId, { + enabled: true, + }); + + // No lambda calls to retrieve tags + assert.strictEqual(lambdaClientSendMock.mock.callCount(), 0); + }); + void it('calls logs monitor with all the customer defined functions and conversation handlers if no function name filter is provided', async () => { await classUnderTest.startStreamingLogs(testSandboxBackendId, { enabled: true, diff --git a/packages/sandbox/src/lambda_function_log_streamer.ts b/packages/sandbox/src/lambda_function_log_streamer.ts index 12858111e77..aac9f2a8334 100644 --- a/packages/sandbox/src/lambda_function_log_streamer.ts +++ b/packages/sandbox/src/lambda_function_log_streamer.ts @@ -47,10 +47,13 @@ export class LambdaFunctionLogStreamer { sandboxBackendId ); } catch (error) { - // If stack does not exist, we do not want to go further to start streaming logs + // If stack does not exist or hasn't deployed successfully, we do not want to go further to start streaming logs if ( BackendOutputClientError.isBackendOutputClientError(error) && - error.code === BackendOutputClientErrorType.NO_STACK_FOUND + [ + BackendOutputClientErrorType.NO_STACK_FOUND, + BackendOutputClientErrorType.NO_OUTPUTS_FOUND, + ].some((code) => (error as BackendOutputClientError).code === code) ) { this.enabled = false; return; From 2e5562d0b54638af2d2e40d92fed2c596d7d0925 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Mon, 30 Dec 2024 09:06:44 -0800 Subject: [PATCH 179/199] bump verdaccio (#2373) --- package-lock.json | 582 +++++++++++++++++++++++++++++++--------------- 1 file changed, 393 insertions(+), 189 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6d53bff8ec5..11967e17b2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13882,9 +13882,9 @@ } }, "node_modules/@cypress/request": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.5.tgz", - "integrity": "sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.7.tgz", + "integrity": "sha512-LzxlLEMbBOPYB85uXrDqvD4MgcenjRBLIns3zyhx7vTPj/0u2eQhzXvPiGcaJrV38Q9dbkExWp6cOHPJ+EtFYg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -13901,9 +13901,9 @@ "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "6.13.0", + "qs": "6.13.1", "safe-buffer": "^5.1.2", - "tough-cookie": "^4.1.3", + "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" }, @@ -18251,19 +18251,20 @@ "license": "ISC" }, "node_modules/@verdaccio/auth": { - "version": "8.0.0-next-8.4", - "resolved": "https://registry.npmjs.org/@verdaccio/auth/-/auth-8.0.0-next-8.4.tgz", - "integrity": "sha512-Bv+du+eIMK2/KU2wIjha2FHQrhBT5RuDOVi1nyRyjEyjmJrUt2RWU0Cb7ASxzQy61nkcJ3bs7kuu9dPHK/Z+jw==", + "version": "8.0.0-next-8.7", + "resolved": "https://registry.npmjs.org/@verdaccio/auth/-/auth-8.0.0-next-8.7.tgz", + "integrity": "sha512-CSLBAsCJT1oOpJ4OWnVGmN6o/ZilDNa7Aa5+AU1LI2lbRblqgr4BVRn07GFqimJ//6+tPzl8BHgyiCbBhh1ZiA==", "dev": true, + "license": "MIT", "dependencies": { - "@verdaccio/config": "8.0.0-next-8.4", - "@verdaccio/core": "8.0.0-next-8.4", - "@verdaccio/loaders": "8.0.0-next-8.3", + "@verdaccio/config": "8.0.0-next-8.7", + "@verdaccio/core": "8.0.0-next-8.7", + "@verdaccio/loaders": "8.0.0-next-8.4", "@verdaccio/signature": "8.0.0-next-8.1", - "@verdaccio/utils": "8.1.0-next-8.4", - "debug": "4.3.7", + "@verdaccio/utils": "8.1.0-next-8.7", + "debug": "4.4.0", "lodash": "4.17.21", - "verdaccio-htpasswd": "13.0.0-next-8.4" + "verdaccio-htpasswd": "13.0.0-next-8.7" }, "engines": { "node": ">=18" @@ -18274,12 +18275,13 @@ } }, "node_modules/@verdaccio/auth/node_modules/@verdaccio/utils": { - "version": "8.1.0-next-8.4", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", - "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", + "version": "8.1.0-next-8.7", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.7.tgz", + "integrity": "sha512-4eqPCnPAJsL6gdVs0/oqZNgs2PnQW3HHBMgBHyEbb5A/ESI10TvRp+B7MRl9glUmy/aR5B6YSI68rgXvAFjdxA==", "dev": true, + "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.7", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -18297,15 +18299,35 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, + "node_modules/@verdaccio/auth/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/@verdaccio/auth/node_modules/minimatch": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -18342,14 +18364,15 @@ "license": "MIT" }, "node_modules/@verdaccio/config": { - "version": "8.0.0-next-8.4", - "resolved": "https://registry.npmjs.org/@verdaccio/config/-/config-8.0.0-next-8.4.tgz", - "integrity": "sha512-9CTYYhaO4xrGbiYjLqRy8EMFPm0YL4a7P6ae8Zm4Dx85Dd6i8XqZ7tlU/+a6qf1g/qggYloolU8pcjaLWNDKAQ==", + "version": "8.0.0-next-8.7", + "resolved": "https://registry.npmjs.org/@verdaccio/config/-/config-8.0.0-next-8.7.tgz", + "integrity": "sha512-pA0WCWvvWY6vPRav+X0EuFmuK6M08zIpRzTKkqSriCWk6JUCZ07TDnN054AS8TSSOy6EaWgHxnUw3nTd34Z4Sg==", "dev": true, + "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.4", - "@verdaccio/utils": "8.1.0-next-8.4", - "debug": "4.3.7", + "@verdaccio/core": "8.0.0-next-8.7", + "@verdaccio/utils": "8.1.0-next-8.7", + "debug": "4.4.0", "js-yaml": "4.1.0", "lodash": "4.17.21", "minimatch": "7.4.6" @@ -18363,12 +18386,13 @@ } }, "node_modules/@verdaccio/config/node_modules/@verdaccio/utils": { - "version": "8.1.0-next-8.4", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", - "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", + "version": "8.1.0-next-8.7", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.7.tgz", + "integrity": "sha512-4eqPCnPAJsL6gdVs0/oqZNgs2PnQW3HHBMgBHyEbb5A/ESI10TvRp+B7MRl9glUmy/aR5B6YSI68rgXvAFjdxA==", "dev": true, + "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.7", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -18385,22 +18409,43 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/@verdaccio/config/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, + "node_modules/@verdaccio/config/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/@verdaccio/config/node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -18413,6 +18458,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -18424,10 +18470,11 @@ } }, "node_modules/@verdaccio/core": { - "version": "8.0.0-next-8.4", - "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-8.0.0-next-8.4.tgz", - "integrity": "sha512-TCaHwIpr97f4YQkU25E6pk1dbfWTQwYPos1tMb9Q7k6IapoxN0c1s+SyF5FBuMOIfJpTHoWJDo/z7QCouQC3lw==", + "version": "8.0.0-next-8.7", + "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-8.0.0-next-8.7.tgz", + "integrity": "sha512-pf8M2Z5EI/5Zdhdcm3aadb9Q9jiDsIredPD3+cIoDum8x3di2AIYvQD7i5BEramfzZlLXVICmFAulU7nUY11qg==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "8.17.1", "core-js": "3.37.1", @@ -18449,6 +18496,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -18466,6 +18514,7 @@ "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -18475,7 +18524,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@verdaccio/file-locking": { "version": "10.3.1", @@ -18495,10 +18545,11 @@ } }, "node_modules/@verdaccio/loaders": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/loaders/-/loaders-8.0.0-next-8.3.tgz", - "integrity": "sha512-7bIOdi+U1xSLRu0s1XxQwrV3zzzFaVaTX7JKFgj2tQvMy9AgzlpjbW1CqaH8OTVEqq03Pwvwj5hQlcvyzCwm1A==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/loaders/-/loaders-8.0.0-next-8.4.tgz", + "integrity": "sha512-Powlqb4SuMbe6RVgxyyOXaCjuHCcK7oZA+lygaKZDpV9NSHJtbkkV4L+rXyCfTX3b0tKsBh7FzaIdgWc1rDeGQ==", "dev": true, + "license": "MIT", "dependencies": { "debug": "4.3.7", "lodash": "4.17.21" @@ -18568,13 +18619,14 @@ "license": "MIT" }, "node_modules/@verdaccio/logger": { - "version": "8.0.0-next-8.4", - "resolved": "https://registry.npmjs.org/@verdaccio/logger/-/logger-8.0.0-next-8.4.tgz", - "integrity": "sha512-zJIFpKYNR/api/mxj5HqJSlEMFh9J4sVKk+3QYlPmppW68beZLLzqwchb5+c/V559lnSrGy5HvDUEGLXvp6reA==", + "version": "8.0.0-next-8.7", + "resolved": "https://registry.npmjs.org/@verdaccio/logger/-/logger-8.0.0-next-8.7.tgz", + "integrity": "sha512-5EMPdZhz2V08BP2rjhtN/Fz5KxCfPJBkYDitbk/eo+FCZ9nVdMCQE3WRbHEaXyJQcIso/LJ6RnL/zKN20E/rPg==", "dev": true, + "license": "MIT", "dependencies": { - "@verdaccio/logger-commons": "8.0.0-next-8.4", - "pino": "9.4.0" + "@verdaccio/logger-commons": "8.0.0-next-8.7", + "pino": "9.5.0" }, "engines": { "node": ">=18" @@ -18585,15 +18637,16 @@ } }, "node_modules/@verdaccio/logger-commons": { - "version": "8.0.0-next-8.4", - "resolved": "https://registry.npmjs.org/@verdaccio/logger-commons/-/logger-commons-8.0.0-next-8.4.tgz", - "integrity": "sha512-neDbq5IIRoidFT4Rv3zH9YydICDCJEybb06BzCGVOzlhZ7F+fBzJH1qlBhAEISfbONugDgfuUQ2jbRCKEkHezQ==", + "version": "8.0.0-next-8.7", + "resolved": "https://registry.npmjs.org/@verdaccio/logger-commons/-/logger-commons-8.0.0-next-8.7.tgz", + "integrity": "sha512-sXNx57G1LVp81xF4qHer3AOcMEZ90W4FjxtYF0vmULcVg3ybdtStKAT/9ocZtVMvLWTPAauhqylfnXoRZYf32A==", "dev": true, + "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.7", "@verdaccio/logger-prettify": "8.0.0-next-8.1", "colorette": "2.0.20", - "debug": "4.3.7" + "debug": "4.4.0" }, "engines": { "node": ">=18" @@ -18603,11 +18656,30 @@ "url": "https://opencollective.com/verdaccio" } }, + "node_modules/@verdaccio/logger-commons/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/@verdaccio/logger-prettify": { "version": "8.0.0-next-8.1", "resolved": "https://registry.npmjs.org/@verdaccio/logger-prettify/-/logger-prettify-8.0.0-next-8.1.tgz", "integrity": "sha512-vLhaGq0q7wtMCcqa0aQY6QOsMNarhTu/l4e6Z8mG/5LUH95GGLsBwpXLnKS94P3deIjsHhc9ycnEmG39txbQ1w==", "dev": true, + "license": "MIT", "dependencies": { "colorette": "2.0.20", "dayjs": "1.11.13", @@ -18624,17 +18696,18 @@ } }, "node_modules/@verdaccio/middleware": { - "version": "8.0.0-next-8.4", - "resolved": "https://registry.npmjs.org/@verdaccio/middleware/-/middleware-8.0.0-next-8.4.tgz", - "integrity": "sha512-tzpfSpeLKUeyTsQ+fvUsokgdh1NrjDJX/oz2ya8wTYSInKAt1Ld9MRzRVSHJwIQc7wfg46zSjpcKZVLA/YkJ6w==", + "version": "8.0.0-next-8.7", + "resolved": "https://registry.npmjs.org/@verdaccio/middleware/-/middleware-8.0.0-next-8.7.tgz", + "integrity": "sha512-Zad7KcdOsI1DUBt1TjQb08rIi/IFFaJKdPhj7M6oy5BX9l/4OM0TtbBueHFNS1+aU+t5eo8ue7ZHbqmjDY/6VQ==", "dev": true, + "license": "MIT", "dependencies": { - "@verdaccio/config": "8.0.0-next-8.4", - "@verdaccio/core": "8.0.0-next-8.4", - "@verdaccio/url": "13.0.0-next-8.4", - "@verdaccio/utils": "8.1.0-next-8.4", - "debug": "4.3.7", - "express": "4.21.1", + "@verdaccio/config": "8.0.0-next-8.7", + "@verdaccio/core": "8.0.0-next-8.7", + "@verdaccio/url": "13.0.0-next-8.7", + "@verdaccio/utils": "8.1.0-next-8.7", + "debug": "4.4.0", + "express": "4.21.2", "express-rate-limit": "5.5.1", "lodash": "4.17.21", "lru-cache": "7.18.3", @@ -18649,12 +18722,13 @@ } }, "node_modules/@verdaccio/middleware/node_modules/@verdaccio/utils": { - "version": "8.1.0-next-8.4", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", - "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", + "version": "8.1.0-next-8.7", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.7.tgz", + "integrity": "sha512-4eqPCnPAJsL6gdVs0/oqZNgs2PnQW3HHBMgBHyEbb5A/ESI10TvRp+B7MRl9glUmy/aR5B6YSI68rgXvAFjdxA==", "dev": true, + "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.7", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -18672,15 +18746,35 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, + "node_modules/@verdaccio/middleware/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/@verdaccio/middleware/node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } @@ -18690,6 +18784,7 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -18702,6 +18797,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -18730,6 +18826,7 @@ "resolved": "https://registry.npmjs.org/@verdaccio/signature/-/signature-8.0.0-next-8.1.tgz", "integrity": "sha512-lHD/Z2FoPQTtDYz6ZlXhj/lrg0SFirHrwCGt/cibl1GlePpx78WPdo03tgAyl0Qf+I35n484/gR1l9eixBQqYw==", "dev": true, + "license": "MIT", "dependencies": { "debug": "4.3.7", "jsonwebtoken": "9.0.2" @@ -18758,15 +18855,16 @@ } }, "node_modules/@verdaccio/tarball": { - "version": "13.0.0-next-8.4", - "resolved": "https://registry.npmjs.org/@verdaccio/tarball/-/tarball-13.0.0-next-8.4.tgz", - "integrity": "sha512-zizQwACK+P9sHtArbuW5MJluRpc3lC6bilGTFNc0TLkHbwL73F8wxkKr5VLzWV7H54+sVKMDs1lCnaoHa0ygmw==", + "version": "13.0.0-next-8.7", + "resolved": "https://registry.npmjs.org/@verdaccio/tarball/-/tarball-13.0.0-next-8.7.tgz", + "integrity": "sha512-EWRuEOLgb3UETxUsYg6+Mml6DDRiwQqKIEsE4Ys6y6rcH2vgW6XMnTt+s/v5pFI+zlbi6fxjOgQB1e6IJAwxVA==", "dev": true, + "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.4", - "@verdaccio/url": "13.0.0-next-8.4", - "@verdaccio/utils": "8.1.0-next-8.4", - "debug": "4.3.7", + "@verdaccio/core": "8.0.0-next-8.7", + "@verdaccio/url": "13.0.0-next-8.7", + "@verdaccio/utils": "8.1.0-next-8.7", + "debug": "4.4.0", "gunzip-maybe": "^1.4.2", "lodash": "4.17.21", "tar-stream": "^3.1.7" @@ -18780,12 +18878,13 @@ } }, "node_modules/@verdaccio/tarball/node_modules/@verdaccio/utils": { - "version": "8.1.0-next-8.4", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", - "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", + "version": "8.1.0-next-8.7", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.7.tgz", + "integrity": "sha512-4eqPCnPAJsL6gdVs0/oqZNgs2PnQW3HHBMgBHyEbb5A/ESI10TvRp+B7MRl9glUmy/aR5B6YSI68rgXvAFjdxA==", "dev": true, + "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.7", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -18803,15 +18902,35 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, + "node_modules/@verdaccio/tarball/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/@verdaccio/tarball/node_modules/minimatch": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -18823,19 +18942,21 @@ } }, "node_modules/@verdaccio/ui-theme": { - "version": "8.0.0-next-8.4", - "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-8.0.0-next-8.4.tgz", - "integrity": "sha512-j3STxUuIgvn058LqfXWv+SwRi1fQ7HapMSfVKXAhi09+f4zlD2mtvKLth0WbuzU1NqJPTGLAP9ueBf1210C1Hw==", - "dev": true + "version": "8.0.0-next-8.7", + "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-8.0.0-next-8.7.tgz", + "integrity": "sha512-+7f7XqqIU+TVCHjsP6lWzCdsD4sM7MEhn4cu3mLW1kJZ7eenWKEltoqixQnoXJzaBjCiz+yXW1WkjMyEFLNbpg==", + "dev": true, + "license": "MIT" }, "node_modules/@verdaccio/url": { - "version": "13.0.0-next-8.4", - "resolved": "https://registry.npmjs.org/@verdaccio/url/-/url-13.0.0-next-8.4.tgz", - "integrity": "sha512-Xo+9DUcwYTBV6d0n4vjLAN2k92J33XM/9JNltWM6140oI8lz+VJKiajtejG/hRBi82RioRdWJ0RZDDY6FsbS3Q==", + "version": "13.0.0-next-8.7", + "resolved": "https://registry.npmjs.org/@verdaccio/url/-/url-13.0.0-next-8.7.tgz", + "integrity": "sha512-biFvwH3zIXYicA+SXNGvjMAe8oIQ5VddsfbO0ZXWlFs0lIz8cgI7QYPeSiCkU2VKpGzZ8pEKgqkxFsfFkU5kGA==", "dev": true, + "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.4", - "debug": "4.3.7", + "@verdaccio/core": "8.0.0-next-8.7", + "debug": "4.4.0", "lodash": "4.17.21", "validator": "13.12.0" }, @@ -18847,6 +18968,24 @@ "url": "https://opencollective.com/verdaccio" } }, + "node_modules/@verdaccio/url/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/@verdaccio/utils": { "version": "7.0.1-next-8.1", "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-7.0.1-next-8.1.tgz", @@ -20339,6 +20478,22 @@ "dev": true, "license": "MIT" }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", @@ -23449,9 +23604,9 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dev": true, "license": "MIT", "dependencies": { @@ -23474,7 +23629,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -23489,6 +23644,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express-rate-limit": { @@ -23515,6 +23674,22 @@ "dev": true, "license": "MIT" }, + "node_modules/express/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -27728,9 +27903,9 @@ "license": "ISC" }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "dev": true, "license": "MIT" }, @@ -27899,16 +28074,16 @@ } }, "node_modules/pino": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-9.4.0.tgz", - "integrity": "sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.5.0.tgz", + "integrity": "sha512-xSEmD4pLnV54t0NOUN16yCl7RIB1c5UUOse5HSyEXtBp+FgFQyPeDutc+Q2ZO7/22vImV7VfEjH/1zV2QuqvYw==", "dev": true, "license": "MIT", "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.2.0", + "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^4.0.0", "quick-format-unescaped": "^4.0.3", @@ -27989,9 +28164,9 @@ "license": "BSD-3-Clause" }, "node_modules/pino-abstract-transport/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.6.0.tgz", + "integrity": "sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw==", "dev": true, "license": "MIT", "dependencies": { @@ -28022,6 +28197,16 @@ "dev": true, "license": "MIT" }, + "node_modules/pino/node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, "node_modules/pino/node_modules/process-warning": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz", @@ -28301,13 +28486,6 @@ "node": ">= 0.10" } }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true, - "license": "MIT" - }, "node_modules/pump": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", @@ -28341,9 +28519,9 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz", + "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -28366,13 +28544,6 @@ "node": ">=0.4.x" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true, - "license": "MIT" - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -28740,13 +28911,6 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "license": "ISC" }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true, - "license": "MIT" - }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -29648,9 +29812,9 @@ } }, "node_modules/streamx": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", - "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", + "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==", "dev": true, "license": "MIT", "dependencies": { @@ -30087,11 +30251,14 @@ } }, "node_modules/text-decoder": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz", - "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } }, "node_modules/text-table": { "version": "0.2.0", @@ -30157,6 +30324,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/tldts": { + "version": "6.1.70", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.70.tgz", + "integrity": "sha512-/W1YVgYVJd9ZDjey5NXadNh0mJXkiUMUue9Zebd0vpdo1sU+H4zFFTaJ1RKD4N6KFoHfcXy6l+Vu7bh+bdWCzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.70" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.70", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.70.tgz", + "integrity": "sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -30207,29 +30394,16 @@ "license": "MIT" }, "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "tldts": "^6.1.32" }, "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" + "node": ">=16" } }, "node_modules/tr46": { @@ -30781,17 +30955,6 @@ "querystring": "0.2.0" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/url/node_modules/punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", @@ -30902,32 +31065,33 @@ } }, "node_modules/verdaccio": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-6.0.2.tgz", - "integrity": "sha512-XthgJlF1hGW+GR/apRLZ7DQw26XpLI+xjMGb7dhJKxI4Pz2gSiEY1RXP9T9I/rlIBr9Zx6rYOgRk7A9Aeq/kpg==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-6.0.5.tgz", + "integrity": "sha512-hv+v4mtG/rcNidGUHXAtNuVySiPE3/PM+7dYye5jCDrhCUmRJYOtnvDe/Ym1ZE/twti39g6izVRxEkjnSp52gA==", "dev": true, + "license": "MIT", "dependencies": { - "@cypress/request": "3.0.5", - "@verdaccio/auth": "8.0.0-next-8.4", - "@verdaccio/config": "8.0.0-next-8.4", - "@verdaccio/core": "8.0.0-next-8.4", + "@cypress/request": "3.0.7", + "@verdaccio/auth": "8.0.0-next-8.7", + "@verdaccio/config": "8.0.0-next-8.7", + "@verdaccio/core": "8.0.0-next-8.7", "@verdaccio/local-storage-legacy": "11.0.2", - "@verdaccio/logger": "8.0.0-next-8.4", - "@verdaccio/middleware": "8.0.0-next-8.4", + "@verdaccio/logger": "8.0.0-next-8.7", + "@verdaccio/middleware": "8.0.0-next-8.7", "@verdaccio/search-indexer": "8.0.0-next-8.2", "@verdaccio/signature": "8.0.0-next-8.1", "@verdaccio/streams": "10.2.1", - "@verdaccio/tarball": "13.0.0-next-8.4", - "@verdaccio/ui-theme": "8.0.0-next-8.4", - "@verdaccio/url": "13.0.0-next-8.4", + "@verdaccio/tarball": "13.0.0-next-8.7", + "@verdaccio/ui-theme": "8.0.0-next-8.7", + "@verdaccio/url": "13.0.0-next-8.7", "@verdaccio/utils": "7.0.1-next-8.1", "async": "3.2.6", "clipanion": "4.0.0-rc.4", "compression": "1.7.5", "cors": "2.8.5", - "debug": "4.3.7", + "debug": "4.4.0", "envinfo": "7.14.0", - "express": "4.21.1", + "express": "4.21.2", "express-rate-limit": "5.5.1", "fast-safe-stringify": "2.1.1", "handlebars": "4.7.8", @@ -30942,8 +31106,8 @@ "pkginfo": "0.4.1", "semver": "7.6.3", "validator": "13.12.0", - "verdaccio-audit": "13.0.0-next-8.4", - "verdaccio-htpasswd": "13.0.0-next-8.4" + "verdaccio-audit": "13.0.0-next-8.7", + "verdaccio-htpasswd": "13.0.0-next-8.7" }, "bin": { "verdaccio": "bin/verdaccio" @@ -30957,14 +31121,15 @@ } }, "node_modules/verdaccio-audit": { - "version": "13.0.0-next-8.4", - "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-13.0.0-next-8.4.tgz", - "integrity": "sha512-T4yi/46fLngllx5mvFtXsGcW3MxGZZ9IkHYPK1OQw9+Xj9aOuMec2eFdztTRo9SgqZfgblGSY1ESZYy19sQLvw==", + "version": "13.0.0-next-8.7", + "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-13.0.0-next-8.7.tgz", + "integrity": "sha512-kd6YdrDztkP1/GDZT7Ue2u41iGPvM9y+5aaUbIBUPvTY/YVv57K6MaCMfn9C/I+ZL4R7XOTSxTtWvz3JK4QrNg==", "dev": true, + "license": "MIT", "dependencies": { - "@verdaccio/config": "8.0.0-next-8.4", - "@verdaccio/core": "8.0.0-next-8.4", - "express": "4.21.1", + "@verdaccio/config": "8.0.0-next-8.7", + "@verdaccio/core": "8.0.0-next-8.7", + "express": "4.21.2", "https-proxy-agent": "5.0.1", "node-fetch": "cjs" }, @@ -30977,17 +31142,18 @@ } }, "node_modules/verdaccio-htpasswd": { - "version": "13.0.0-next-8.4", - "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-13.0.0-next-8.4.tgz", - "integrity": "sha512-w0knjKz8SdBGSv0Kt61LoUOCYBZ2/iig3bVbGFWTj4MwCUG6eNewMoQ6nbrk+kyHNFVq75IzT1eIhXDEysx15Q==", + "version": "13.0.0-next-8.7", + "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-13.0.0-next-8.7.tgz", + "integrity": "sha512-znyFnwt59mLKTAu6eHJrfWP07iaHUlYiQN7QoBo8KMAOT1AecUYreBqs93oKHdIOzjTI8j6tQLg57DpeVS5vgg==", "dev": true, + "license": "MIT", "dependencies": { - "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.7", "@verdaccio/file-locking": "13.0.0-next-8.2", "apache-md5": "1.1.8", "bcryptjs": "2.4.3", "core-js": "3.37.1", - "debug": "4.3.7", + "debug": "4.4.0", "http-errors": "2.0.0", "unix-crypt-td-js": "1.1.4" }, @@ -31004,6 +31170,7 @@ "resolved": "https://registry.npmjs.org/@verdaccio/file-locking/-/file-locking-13.0.0-next-8.2.tgz", "integrity": "sha512-TcHgN3I/N28WBSvtukpGrJhBljl4jyIXq0vEv94vXAG6nUE3saK+vtgo8PfYA3Ueo88v/1zyAbiZM4uxwojCmQ==", "dev": true, + "license": "MIT", "dependencies": { "lockfile": "1.0.4" }, @@ -31021,11 +31188,30 @@ "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" } }, + "node_modules/verdaccio-htpasswd/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/verdaccio/node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -31033,6 +31219,24 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/verdaccio/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/verdaccio/node_modules/handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", @@ -31607,7 +31811,7 @@ }, "packages/ai-constructs": { "name": "@aws-amplify/ai-constructs", - "version": "1.1.0", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", @@ -31647,11 +31851,11 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.10.0", + "version": "1.11.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-auth": "^1.4.2", - "@aws-amplify/backend-data": "^1.2.3", + "@aws-amplify/backend-data": "^1.3.0", "@aws-amplify/backend-function": "^1.10.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", @@ -31675,10 +31879,10 @@ }, "packages/backend-ai": { "name": "@aws-amplify/backend-ai", - "version": "1.1.0", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^1.1.0", + "@aws-amplify/ai-constructs": "^1.2.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/data-schema-types": "^1.2.0", @@ -31715,7 +31919,7 @@ }, "packages/backend-data": { "name": "@aws-amplify/backend-data", - "version": "1.2.3", + "version": "1.3.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", @@ -32092,7 +32296,7 @@ }, "packages/cli": { "name": "@aws-amplify/backend-cli", - "version": "1.4.4", + "version": "1.4.5", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-deployer": "^1.1.12", @@ -32102,7 +32306,7 @@ "@aws-amplify/client-config": "^1.5.4", "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/form-generator": "^1.0.3", - "@aws-amplify/model-generator": "^1.0.10", + "@aws-amplify/model-generator": "^1.0.11", "@aws-amplify/platform-core": "^1.4.0", "@aws-amplify/plugin-types": "^1.6.0", "@aws-amplify/sandbox": "^1.2.8", @@ -32973,7 +33177,7 @@ }, "packages/model-generator": { "name": "@aws-amplify/model-generator", - "version": "1.0.10", + "version": "1.0.11", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", From 22089b7a7321b8fc99e178a8dc777ccd77690e12 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Mon, 30 Dec 2024 09:46:05 -0800 Subject: [PATCH 180/199] add retries to hotswapping resources for sandbox tests (#2372) * add retries to hotswapping resources for sandbox tests * add comment --- .changeset/hungry-dogs-juggle.md | 2 + .../test-e2e/sandbox/sandbox.test.template.ts | 46 +++++++++++-------- 2 files changed, 29 insertions(+), 19 deletions(-) create mode 100644 .changeset/hungry-dogs-juggle.md diff --git a/.changeset/hungry-dogs-juggle.md b/.changeset/hungry-dogs-juggle.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/hungry-dogs-juggle.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts b/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts index 599cb4dae82..716c7835104 100644 --- a/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts +++ b/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts @@ -21,6 +21,7 @@ import { amplifySharedSecretNameKey, createAmplifySharedSecretName, } from '../../shared_secret.js'; +import { runWithRetry } from '../../retry.js'; /** * Defines sandbox test @@ -83,27 +84,34 @@ export const defineSandboxTest = (testProjectCreator: TestProjectCreator) => { void it(`[${testProjectCreator.name}] hot-swaps a change`, async () => { const updates = await testProject.getUpdates(); if (updates.length > 0) { - const processController = ampxCli( - ['sandbox', '--dirToWatch', 'amplify'], - testProject.projectDirPath, - { - env: sharedSecretsEnv, - } - ); - - for (const update of updates) { - processController - .do(replaceFiles(update.replacements)) - .do(waitForSandboxToBeginHotswappingResources()); - if (update.deployThresholdSec) { - processController.do( - ensureDeploymentTimeLessThan(update.deployThresholdSec) + // retry hotswapping resources if deployment time is higher than the threshold + await runWithRetry( + async () => { + // keeping initial deployment in retry loop to reset app state for each hotswap to be a non no-op + const processController = ampxCli( + ['sandbox', '--dirToWatch', 'amplify'], + testProject.projectDirPath, + { + env: sharedSecretsEnv, + } ); - } - } - // Execute the process. - await processController.do(interruptSandbox()).run(); + for (const update of updates) { + processController + .do(replaceFiles(update.replacements)) + .do(waitForSandboxToBeginHotswappingResources()); + if (update.deployThresholdSec) { + processController.do( + ensureDeploymentTimeLessThan(update.deployThresholdSec) + ); + } + } + + // Execute the process. + await processController.do(interruptSandbox()).run(); + }, + (error) => error.message.includes('Deployment time') + ); await testProject.assertPostDeployment(sandboxBackendIdentifier); } From b1c0f0d88c0fdbbb4c6411d2aa04252125e7ef3c Mon Sep 17 00:00:00 2001 From: Caio Quirino da Silva Date: Tue, 31 Dec 2024 15:13:34 +0100 Subject: [PATCH 181/199] Adds Architecture Property on defineFunction (#2370) * Adds Architecture Property on defineFunction * Apply suggestions from code review Co-authored-by: Kamil Sobol Co-authored-by: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> * Fixed error in tests --------- Co-authored-by: Kamil Sobol Co-authored-by: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> --- .changeset/tall-parrots-sort.md | 6 ++++ packages/backend-function/API.md | 4 +++ packages/backend-function/src/factory.test.ts | 36 +++++++++++++++++-- packages/backend-function/src/factory.ts | 35 ++++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 .changeset/tall-parrots-sort.md diff --git a/.changeset/tall-parrots-sort.md b/.changeset/tall-parrots-sort.md new file mode 100644 index 00000000000..c14c30fd69c --- /dev/null +++ b/.changeset/tall-parrots-sort.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/backend-function': minor +'@aws-amplify/backend': minor +--- + +adds support for architecture property on defineFunction diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index d5453c7eb39..1a424e3ad84 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -64,6 +64,9 @@ type DataClientReturn = T extends DataClientEnv ? DataClientConfig : DataClie // @public export const defineFunction: (props?: FunctionProps) => ConstructFactory & ResourceAccessAcceptorFactory & AddEnvironmentFactory & StackProvider>; +// @public (undocumented) +export type FunctionArchitecture = 'x86_64' | 'arm64'; + // @public (undocumented) export type FunctionBundlingOptions = { minify?: boolean; @@ -94,6 +97,7 @@ export type FunctionProps = { ephemeralStorageSizeMB?: number; environment?: Record; runtime?: NodeVersion; + architecture?: FunctionArchitecture; schedule?: FunctionSchedule | FunctionSchedule[]; layers?: Record; bundling?: FunctionBundlingOptions; diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index 6bb63f14d2a..568d77d2c37 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -13,9 +13,13 @@ import { } from '@aws-amplify/backend-platform-test-stubs'; import { defaultLambda } from './test-assets/default-lambda/resource.js'; import { Template } from 'aws-cdk-lib/assertions'; -import { NodeVersion, defineFunction } from './factory.js'; +import { + FunctionArchitecture, + NodeVersion, + defineFunction, +} from './factory.js'; import { lambdaWithDependencies } from './test-assets/lambda-with-dependencies/resource.js'; -import { Runtime } from 'aws-cdk-lib/aws-lambda'; +import { Architecture, Runtime } from 'aws-cdk-lib/aws-lambda'; import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; import fsp from 'fs/promises'; import path from 'node:path'; @@ -454,6 +458,34 @@ void describe('AmplifyFunctionFactory', () => { }); }); + void describe('architecture property', () => { + void it('sets valid architecture', () => { + const lambda = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + architecture: 'arm64', + }).getInstance(getInstanceProps); + const template = Template.fromStack(lambda.stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + Architectures: [Architecture.ARM_64.name], + }); + }); + }); + + void it('throws on invalid architecture', () => { + assert.throws( + () => + defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + architecture: 'invalid' as FunctionArchitecture, + }).getInstance(getInstanceProps), + new AmplifyUserError('InvalidArchitectureError', { + message: `Invalid function architecture of invalid`, + resolution: 'architecture must be one of the following: arm64, x86_64', + }) + ); + }); + void describe('schedule property', () => { void it('sets valid schedule - rate', () => { const lambda = defineFunction({ diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 7f2f04b0815..f6df34cd8c1 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -31,6 +31,7 @@ import { Rule } from 'aws-cdk-lib/aws-events'; import * as targets from 'aws-cdk-lib/aws-events-targets'; import { Policy } from 'aws-cdk-lib/aws-iam'; import { + Architecture, CfnFunction, ILayerVersion, LayerVersion, @@ -134,6 +135,12 @@ export type FunctionProps = { */ runtime?: NodeVersion; + /** + * The architecture of the target platform for the lambda environment. + * Defaults to X86_64. + */ + architecture?: FunctionArchitecture; + /** * A time interval string to periodically run the function. * This can be either a string of `"every "`, `"every day|week|month|year"` or cron expression. @@ -246,6 +253,7 @@ class FunctionFactory implements ConstructFactory { ephemeralStorageSizeMB: this.resolveEphemeralStorageSize(), environment: this.resolveEnvironment(), runtime: this.resolveRuntime(), + architecture: this.resolveArchitecture(), schedule: this.resolveSchedule(), bundling: this.resolveBundling(), layers: this.props.layers ?? {}, @@ -401,6 +409,25 @@ class FunctionFactory implements ConstructFactory { return this.props.runtime; }; + private resolveArchitecture = () => { + const architectureDefault = 'x86_64'; + + if (!this.props.architecture) { + return architectureDefault; + } + + if (!(this.props.architecture in architectureMap)) { + throw new AmplifyUserError('InvalidArchitectureError', { + message: `Invalid function architecture of ${this.props.architecture}`, + resolution: `architecture must be one of the following: ${Object.keys( + architectureMap + ).join(', ')}`, + }); + } + + return this.props.architecture; + }; + private resolveSchedule = () => { if (!this.props.schedule) { return []; @@ -539,6 +566,7 @@ class AmplifyFunction entry: props.entry, timeout: Duration.seconds(props.timeoutSeconds), memorySize: props.memoryMB, + architecture: architectureMap[props.architecture], ephemeralStorageSize: Size.mebibytes(props.ephemeralStorageSizeMB), runtime: nodeVersionMap[props.runtime], layers: props.resolvedLayers, @@ -678,3 +706,10 @@ const nodeVersionMap: Record = { 20: Runtime.NODEJS_20_X, 22: Runtime.NODEJS_22_X, }; + +export type FunctionArchitecture = 'x86_64' | 'arm64'; + +const architectureMap: Record = { + arm64: Architecture.ARM_64, + x86_64: Architecture.X86_64, +}; From 866bc761dae4c92259a28b511519233f0f34c5bc Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Tue, 31 Dec 2024 10:05:50 -0800 Subject: [PATCH 182/199] add e2e test project reset and add reset to hotswap retries (#2380) --- .changeset/stale-pianos-marry.md | 2 ++ .../src/test-e2e/sandbox/sandbox.test.template.ts | 8 ++++++-- .../src/test-project-setup/test_project_base.ts | 12 ++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 .changeset/stale-pianos-marry.md diff --git a/.changeset/stale-pianos-marry.md b/.changeset/stale-pianos-marry.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/stale-pianos-marry.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts b/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts index 716c7835104..8685f1382c1 100644 --- a/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts +++ b/packages/integration-tests/src/test-e2e/sandbox/sandbox.test.template.ts @@ -86,8 +86,12 @@ export const defineSandboxTest = (testProjectCreator: TestProjectCreator) => { if (updates.length > 0) { // retry hotswapping resources if deployment time is higher than the threshold await runWithRetry( - async () => { - // keeping initial deployment in retry loop to reset app state for each hotswap to be a non no-op + async (attempt) => { + if (attempt > 1) { + // reset test project to pre-update state to retry hotswap + await testProject.reset(); + } + const processController = ampxCli( ['sandbox', '--dirToWatch', 'amplify'], testProject.projectDirPath, diff --git a/packages/integration-tests/src/test-project-setup/test_project_base.ts b/packages/integration-tests/src/test-project-setup/test_project_base.ts index d1de5df76ab..c56209a6d4c 100644 --- a/packages/integration-tests/src/test-project-setup/test_project_base.ts +++ b/packages/integration-tests/src/test-project-setup/test_project_base.ts @@ -26,6 +26,7 @@ import path from 'path'; import { AmplifyClient } from '@aws-sdk/client-amplify'; import { pathToFileURL } from 'url'; import isMatch from 'lodash.ismatch'; +import { setupDirAsEsmModule } from './setup_dir_as_esm_module.js'; export type PlatformDeploymentThresholds = { onWindows: number; @@ -247,4 +248,15 @@ export abstract class TestProjectBase { assert.ok(isMatch(currentCodebaseOutputs, npmOutputs)); } + + /** + * Resets the project to its initial state + */ + async reset() { + await fsp.rm(this.projectAmplifyDirPath, { recursive: true, force: true }); + await fsp.cp(this.sourceProjectAmplifyDirURL, this.projectAmplifyDirPath, { + recursive: true, + }); + await setupDirAsEsmModule(this.projectAmplifyDirPath); + } } From 9baf6e968d09e9d7fe47a7af0cb7947baba24dc4 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Thu, 2 Jan 2025 09:39:02 -0800 Subject: [PATCH 183/199] add dependabot.yml (#2353) * add dependabot.yml * add increase-if-necessary strategy * Update .github/dependabot.yml Co-authored-by: Kamil Sobol * dependabot adds e2e label and action to add changesets to dependabot PRs * fix health checks * fix this * try this * add to eslint dictionary * PR feedback * add testing * try this * try that * mock git push * fix changeset file path * update way ghContext is mocked * try this * try this * PR feedback * Delete scripts/components/test-resources/github_pull_request_event.json --------- Co-authored-by: Kamil Sobol --- .changeset/blue-meals-kneel.md | 2 + .eslint_dictionary.json | 2 + .github/dependabot.yml | 16 ++ .github/workflows/health_checks.yml | 21 ++ .../dependabot_version_update_handler.test.ts | 199 ++++++++++++++++++ .../dependabot_version_update_handler.ts | 135 ++++++++++++ scripts/components/git_client.ts | 16 ++ scripts/components/github_client.ts | 8 + scripts/dependabot_handle_version_update.ts | 27 +++ 9 files changed, 426 insertions(+) create mode 100644 .changeset/blue-meals-kneel.md create mode 100644 .github/dependabot.yml create mode 100644 scripts/components/dependabot_version_update_handler.test.ts create mode 100644 scripts/components/dependabot_version_update_handler.ts create mode 100644 scripts/dependabot_handle_version_update.ts diff --git a/.changeset/blue-meals-kneel.md b/.changeset/blue-meals-kneel.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/blue-meals-kneel.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index 86df2644354..21c64ec6a86 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -41,6 +41,7 @@ "debounce", "declarator", "decrypt", + "dependabot", "deployer", "deprecations", "deprecator", @@ -62,6 +63,7 @@ "formatter", "frontend", "frontends", + "frontmatter", "fullname", "func", "geofence", diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..1013809baa7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +# Dependabot version updates raises a maximum of five pull requests each time it checks dependencies. +# Note that there is some overlap with Dependabot security updates so some options can effect security updates as well, +# see https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file. + +version: 2 +updates: + # Maintain dependencies for npm ecosystem + - package-ecosystem: 'npm' + # Checks all directories from the current layer and below recursively for package.json files + directories: + - '**/*' + schedule: + # Runs every Monday + interval: 'weekly' + # Update package.json files if new version is outside of version range specified there. Otherwise lock file only. + versioning-strategy: increase-if-necessary diff --git a/.github/workflows/health_checks.yml b/.github/workflows/health_checks.yml index f7340267475..816a79547dd 100644 --- a/.github/workflows/health_checks.yml +++ b/.github/workflows/health_checks.yml @@ -269,10 +269,31 @@ jobs: run: | mkdir api-validation-projects npx tsx scripts/check_api_changes.ts base-branch-content api-validation-projects + generate_changeset: + if: github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]' + runs-on: ubuntu-latest + needs: + - install + - resolve_inputs + steps: + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 + - uses: ./.github/actions/setup_node + with: + node-version: 18 + - uses: ./.github/actions/restore_install_cache + with: + node-version: 18 + cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} + - name: Generate changesets for packages with version updates + run: npx tsx scripts/dependabot_handle_version_update.ts "$BASE_SHA" "$HEAD_SHA" + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} do_include_e2e: needs: - install - resolve_inputs + - generate_changeset runs-on: ubuntu-latest permissions: # This is required so that the step can read the labels on the pull request diff --git a/scripts/components/dependabot_version_update_handler.test.ts b/scripts/components/dependabot_version_update_handler.test.ts new file mode 100644 index 00000000000..8d9b612f1e2 --- /dev/null +++ b/scripts/components/dependabot_version_update_handler.test.ts @@ -0,0 +1,199 @@ +import { randomUUID } from 'crypto'; +import { $ as chainableExeca } from 'execa'; +import fsp from 'fs/promises'; +import { after, before, beforeEach, describe, it, mock } from 'node:test'; +import { EOL, tmpdir } from 'os'; +import path from 'path'; +import { GitClient } from './git_client.js'; +import { GithubClient } from './github_client.js'; +import { NpmClient } from './npm_client.js'; +import { + readPackageJson, + writePackageJson, +} from './package-json/package_json.js'; +import { DependabotVersionUpdateHandler } from './dependabot_version_update_handler.js'; +import assert from 'assert'; + +const originalEnv = process.env; + +/** + * This test suite is more of an integration test than a unit test. + * It uses the real file system and git repo but mocks the GitHub API client and GitHub context + */ +void describe('dependabot version update handler', async () => { + let testWorkingDir: string; + let gitClient: GitClient; + let npmClient: NpmClient; + + let cantaloupePackageName: string; + let cantaloupePackagePath: string; + let platypusPackageName: string; + let platypusPackagePath: string; + + let baseRef: string; + + const pullRequestBody = 'Bumps testDep from 1.0.0 to 1.1.0.'; + + before(async () => { + process.env.GITHUB_TOKEN = 'testToken'; + }); + + after(async () => { + process.env = originalEnv; + }); + + beforeEach(async ({ name: testName }) => { + // create temp dir + const shortId = randomUUID().split('-')[0]; + const testNameNormalized = testName.slice(0, 15).replaceAll(/\s/g, ''); + testWorkingDir = await fsp.mkdtemp( + path.join(tmpdir(), `${testNameNormalized}-${shortId}`) + ); + console.log(testWorkingDir); + + gitClient = new GitClient(testWorkingDir); + npmClient = new NpmClient(null, testWorkingDir); + + const $ = chainableExeca({ stdio: 'inherit', cwd: testWorkingDir }); + + // converting to lowercase because npm init creates packages with all lowercase + cantaloupePackageName = + `${testNameNormalized}-cantaloupe-${shortId}`.toLocaleLowerCase(); + platypusPackageName = + `${testNameNormalized}-platypus-${shortId}`.toLocaleLowerCase(); + + cantaloupePackagePath = path.join( + testWorkingDir, + 'packages', + cantaloupePackageName + ); + platypusPackagePath = path.join( + testWorkingDir, + 'packages', + platypusPackageName + ); + + await gitClient.init(); + await gitClient.switchToBranch('main'); + await npmClient.init(); + + await npmClient.initWorkspacePackage(cantaloupePackageName); + await setPackageToPublic(cantaloupePackagePath); + + await npmClient.initWorkspacePackage(platypusPackageName); + await setPackageToPublic(platypusPackagePath); + + await npmClient.install(['@changesets/cli']); + await setPackageDependencies(cantaloupePackagePath, { testDep: '^1.0.0' }); + await setPackageDependencies(platypusPackagePath, { testDep: '^1.0.0' }); + + await $`npx changeset init`; + await gitClient.commitAllChanges('Initial setup'); + baseRef = await gitClient.getHashForCurrentCommit(); + }); + + void it('can generate changeset with version updates', async () => { + const githubClient = new GithubClient('garbage'); + const labelPullRequestMocked = mock.method( + githubClient, + 'labelPullRequest', + async () => {} + ); + const gitPushMocked = mock.method(gitClient, 'push', async () => {}); + const ghContextMocked = { + eventName: '', + sha: '', + ref: '', + workflow: '', + action: '', + actor: '', + job: '', + runNumber: 0, + runId: 0, + apiUrl: '', + serverUrl: '', + graphqlUrl: '', + payload: { + pull_request: { + number: 1, + body: pullRequestBody, + }, + }, + issue: { + owner: '', + repo: '', + number: 0, + }, + repo: { + owner: '', + repo: '', + }, + }; + + // Update package.json files for both packages and commit to match what Dependabot will do for a version update PR + await gitClient.switchToBranch('dependabot/test_update'); + await setPackageDependencies(cantaloupePackagePath, { testDep: '^1.1.0' }); + await setPackageDependencies(platypusPackagePath, { testDep: '^1.1.0' }); + await gitClient.commitAllChanges('Bump dependencies'); + const headRef = await gitClient.getHashForCurrentCommit(); + + const dependabotVersionUpdateHandler = new DependabotVersionUpdateHandler( + baseRef, + headRef, + gitClient, + githubClient, + testWorkingDir, + ghContextMocked + ); + + await dependabotVersionUpdateHandler.handleVersionUpdate(); + + const changesetFilePath = path.join( + testWorkingDir, + `.changeset/dependabot-${headRef}.md` + ); + + await assertChangesetFile( + changesetFilePath, + [cantaloupePackageName, platypusPackageName], + pullRequestBody + ); + assert.deepEqual(labelPullRequestMocked.mock.calls[0].arguments, [ + 1, + ['run-e2e'], + ]); + assert.deepEqual(gitPushMocked.mock.callCount(), 1); + }); +}); + +const setPackageToPublic = async (packagePath: string) => { + const packageJson = await readPackageJson(packagePath); + packageJson.publishConfig = { + access: 'public', + }; + await writePackageJson(packagePath, packageJson); +}; + +const setPackageDependencies = async ( + packagePath: string, + dependencies: Record +) => { + const packageJson = await readPackageJson(packagePath); + packageJson.dependencies = dependencies; + await writePackageJson(packagePath, packageJson); +}; + +const assertChangesetFile = async ( + filePath: string, + packageNames: string[], + message: string +) => { + const changesetFileContent = await fsp.readFile(filePath, 'utf-8'); + const frontmatterContent = packageNames + .map((name) => `'${name}': patch`) + .join(EOL); + + const expectedContent = `---${EOL}${frontmatterContent}${EOL}---${EOL}${EOL}${message}${EOL}`; + + assert.deepEqual(changesetFileContent, expectedContent); +}; diff --git a/scripts/components/dependabot_version_update_handler.ts b/scripts/components/dependabot_version_update_handler.ts new file mode 100644 index 00000000000..6e8739c8054 --- /dev/null +++ b/scripts/components/dependabot_version_update_handler.ts @@ -0,0 +1,135 @@ +import { context as ghContext } from '@actions/github'; +import { Context } from '@actions/github/lib/context.js'; +import fsp from 'fs/promises'; +import { EOL } from 'os'; +import { GitClient } from './git_client.js'; +import { GithubClient } from './github_client.js'; +import { readPackageJson } from './package-json/package_json.js'; +import path from 'path'; + +/** + * Handles the follow up processes of Dependabot opening a version update PR + */ +export class DependabotVersionUpdateHandler { + /** + * Initialize with version update config and necessary clients + */ + constructor( + private readonly baseRef: string, + private readonly headRef: string, + private readonly gitClient: GitClient, + private readonly ghClient: GithubClient, + private readonly _rootDir: string = process.cwd(), + private readonly _ghContext: Context = ghContext + ) {} + + /** + * This method handles all of the follow up processes we would want run when Dependabot opens a version update PR. + * + * We would want a changeset file generated for Dependabot version update PRs in order for these version updates to be part of our release process. + * We also want to run our E2E tests on these version updates to ensure new dependency updates don't break us. + * + * Running this method when either of the following are true results in a no-op: + * - GitHub context event is not a pull request + * - Branch PR does not start with `dependabot/`, meaning the branch isn't for a Dependabot PR + * - PR already has a changeset in list of files changed + */ + handleVersionUpdate = async () => { + if (!this._ghContext.payload.pull_request) { + // event is not a pull request, return early + return; + } + + const branch = await this.gitClient.getCurrentBranch(); + if (!branch.startsWith('dependabot/')) { + // if branch is not a dependabot branch, return early + return; + } + + const changedFiles = await this.gitClient.getChangedFiles(this.baseRef); + if (changedFiles.find((file) => file.startsWith('.changeset'))) { + // if changeset file already exists, return early + return; + } + + // Get all of the public packages with version updates (where 'package.json' is modified) + const modifiedPackageDirs = new Set(); + const packageJsonFiles = changedFiles.filter( + (changedFile) => + changedFile.startsWith('packages/') && + changedFile.endsWith('package.json') + ); + packageJsonFiles.forEach((changedPackageFile) => { + modifiedPackageDirs.add( + changedPackageFile.split('/').slice(0, 2).join('/') + ); + }); + + const packageNames = []; + for (const modifiedPackageDir of modifiedPackageDirs) { + const packageJson = await readPackageJson( + path.join(this._rootDir, modifiedPackageDir) + ); + if (!packageJson.private) { + packageNames.push(packageJson.name); + } + } + + // Create and commit the changeset file, then add the 'run-e2e' label and force push to the PR + const fileName = path.join( + this._rootDir, + `.changeset/dependabot-${this.headRef}.md` + ); + const versionUpdates = await this.getVersionUpdates(); + await this.createChangesetFile(fileName, versionUpdates, packageNames); + await this.gitClient.status(); + await this.gitClient.commitAllChanges('add changeset'); + await this.ghClient.labelPullRequest( + this._ghContext.payload.pull_request.number, + ['run-e2e'] + ); + await this.gitClient.push({ force: true }); + }; + + private createChangesetFile = async ( + fileName: string, + versionUpdates: string[], + packageNames: string[] + ) => { + let message = ''; + let content = ''; + + for (const update of versionUpdates) { + message += `${update}${EOL}`; + } + + const frontmatterContent = packageNames + .map((name) => `'${name}': patch`) + .join(EOL); + + if (packageNames.length === 0 || versionUpdates.length === 0) { + content = `---${EOL}---${EOL}`; + } else { + content = `---${EOL}${frontmatterContent}${EOL}---${EOL}${EOL}${message.trim()}${EOL}`; + } + await fsp.writeFile(fileName, content); + }; + + private getVersionUpdates = async () => { + const updates: string[] = []; + const prBody = this._ghContext.payload.pull_request?.body; + + // Match lines in PR body that are one of the following: + // Updates `` from to + // Bumps []() from to . + const matches = prBody?.match( + /(Updates|Bumps) (.*) from [0-9.]+ to [0-9.]+/g + ); + + for (const match of matches ?? []) { + updates.push(match); + } + + return updates; + }; +} diff --git a/scripts/components/git_client.ts b/scripts/components/git_client.ts index 3183660ec0a..5d90514298d 100644 --- a/scripts/components/git_client.ts +++ b/scripts/components/git_client.ts @@ -65,6 +65,14 @@ export class GitClient { return filenameDiffOutput.toString().split(EOL); }; + /** + * Get changes made in a specific modified file from getChangedFiles + */ + getFileChanges = async (file: string) => { + const { stdout: changes } = await this.exec`git show ${file}`; + return changes; + }; + /** * Switches to branchName. Creates the branch if it does not exist. */ @@ -189,6 +197,14 @@ export class GitClient { return previousReleaseTags; }; + /** + * Get commit hash at HEAD for the current branch + */ + getHashForCurrentCommit = async () => { + const { stdout: currentCommitHash } = await this.exec`git rev-parse HEAD`; + return currentCommitHash; + }; + private validateReleaseCommitHash = async (releaseCommitHash: string) => { // check that the hash points to a valid commit const { stdout: hashType } = await this diff --git a/scripts/components/github_client.ts b/scripts/components/github_client.ts index 56eb7e18e85..7c294c0ec1b 100644 --- a/scripts/components/github_client.ts +++ b/scripts/components/github_client.ts @@ -57,6 +57,14 @@ export class GithubClient { }); return response.data; }; + + labelPullRequest = async (pullRequestNumber: number, labels: string[]) => { + await this.ghClient.issues.addLabels({ + issue_number: pullRequestNumber, + labels, + ...ghContext.repo, + }); + }; } /** diff --git a/scripts/dependabot_handle_version_update.ts b/scripts/dependabot_handle_version_update.ts new file mode 100644 index 00000000000..da9c92de1b9 --- /dev/null +++ b/scripts/dependabot_handle_version_update.ts @@ -0,0 +1,27 @@ +import { GitClient } from './components/git_client.js'; +import { GithubClient } from './components/github_client.js'; +import { DependabotVersionUpdateHandler } from './components/dependabot_version_update_handler.js'; + +const baseRef = process.argv[2]; +if (baseRef === undefined) { + throw new Error('No base ref specified for generate changeset check'); +} + +const headRef = process.argv[3]; +if (headRef === undefined) { + throw new Error('No head ref specified for generate changeset check'); +} + +const dependabotVersionUpdateHandler = new DependabotVersionUpdateHandler( + baseRef, + headRef, + new GitClient(), + new GithubClient() +); + +try { + await dependabotVersionUpdateHandler.handleVersionUpdate(); +} catch (error) { + console.error(error); + process.exitCode = 1; +} From 38b5da389c2bf55190ebffaaa4b35b97e344552b Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Thu, 2 Jan 2025 10:16:11 -0800 Subject: [PATCH 184/199] update github workflow for handling dependabot version update pull requests (#2392) * update github workflow for handling dependabot version update pull requests * update name --- .github/workflows/health_checks.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/health_checks.yml b/.github/workflows/health_checks.yml index 816a79547dd..bf2d63289bf 100644 --- a/.github/workflows/health_checks.yml +++ b/.github/workflows/health_checks.yml @@ -269,7 +269,7 @@ jobs: run: | mkdir api-validation-projects npx tsx scripts/check_api_changes.ts base-branch-content api-validation-projects - generate_changeset: + handle_dependabot_version_update: if: github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]' runs-on: ubuntu-latest needs: @@ -284,16 +284,18 @@ jobs: with: node-version: 18 cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - - name: Generate changesets for packages with version updates + - name: Handle Dependabot version update pull request run: npx tsx scripts/dependabot_handle_version_update.ts "$BASE_SHA" "$HEAD_SHA" env: BASE_SHA: ${{ github.event.pull_request.base.sha }} HEAD_SHA: ${{ github.event.pull_request.head.sha }} + # The dependabot_handler_version_update script needs to add the 'run-e2e' pull request label + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} do_include_e2e: needs: - install - resolve_inputs - - generate_changeset + - handle_dependabot_version_update runs-on: ubuntu-latest permissions: # This is required so that the step can read the labels on the pull request From 23f1240cf366b801fe633296cb69993f79f395d1 Mon Sep 17 00:00:00 2001 From: bobbyu99 Date: Thu, 2 Jan 2025 12:15:24 -0800 Subject: [PATCH 185/199] feat: add data logging api in `defineData` (#2359) * feat: add data logging api to defineData * chore: update API.md * chore: update API.md * addressed PR comments * addressed PR comments * updated ConversationHandlerFunctionLogLevel * chore: update changeset * chore: remove extra changeset * Update .changeset/quick-dingos-whisper.md Co-authored-by: Kamil Sobol * chore: updated API.md * chore: rename `level` to `fieldLogLevel` * chore: update API.md * chore: bump data-construct version --------- Co-authored-by: Kamil Sobol --- .changeset/quick-dingos-whisper.md | 10 + package-lock.json | 4950 +++++++++++------ packages/backend-ai/API.md | 186 +- .../backend-ai/src/conversation/factory.ts | 5 +- packages/backend-data/API.md | 16 + packages/backend-data/package.json | 2 +- packages/backend-data/src/factory.test.ts | 143 +- packages/backend-data/src/factory.ts | 7 +- packages/backend-data/src/index.ts | 3 + .../src/logging_options_parser.test.ts | 87 + .../src/logging_options_parser.ts | 65 + packages/backend-data/src/types.ts | 92 +- packages/backend-function/API.md | 2 +- packages/backend-function/src/factory.ts | 5 +- packages/platform-core/API.md | 3 + .../src/cdk/enum_converters.test.ts | 41 +- .../platform-core/src/cdk/enum_converters.ts | 24 + packages/plugin-types/API.md | 2 +- packages/plugin-types/src/log_level.ts | 10 +- 19 files changed, 3967 insertions(+), 1686 deletions(-) create mode 100644 .changeset/quick-dingos-whisper.md create mode 100644 packages/backend-data/src/logging_options_parser.test.ts create mode 100644 packages/backend-data/src/logging_options_parser.ts diff --git a/.changeset/quick-dingos-whisper.md b/.changeset/quick-dingos-whisper.md new file mode 100644 index 00000000000..3e4c117eda2 --- /dev/null +++ b/.changeset/quick-dingos-whisper.md @@ -0,0 +1,10 @@ +--- +'@aws-amplify/backend-function': minor +'@aws-amplify/platform-core': minor +'@aws-amplify/backend-data': minor +'@aws-amplify/plugin-types': minor +'@aws-amplify/backend-ai': minor +'@aws-amplify/backend': minor +--- + +added data logging api to defineData diff --git a/package-lock.json b/package-lock.json index 11967e17b2a..889039955a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -756,10 +756,11 @@ } }, "node_modules/@aws-amplify/data-construct": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/data-construct/-/data-construct-1.10.1.tgz", - "integrity": "sha512-fG8EHT+LYpBGIOwXx2uw4IKTyZv5IWTRtnSSVBM6AYT76FsRe2qhvNnaDUWP7S5SEROQrfLxMnuWiozfeknTgg==", + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/@aws-amplify/data-construct/-/data-construct-1.14.5.tgz", + "integrity": "sha512-QKwnf/6Cksts8tpDiHw2gl9aapN3ii4/4Cl2a2RELoTrWfXvaih9WDJmFSsgM2a2VdhJ1bjS6GGzb8GrN1vdSg==", "bundleDependencies": [ + "@aws-amplify/ai-constructs", "@aws-amplify/backend-output-schemas", "@aws-amplify/backend-output-storage", "@aws-amplify/graphql-auth-transformer", @@ -767,6 +768,7 @@ "@aws-amplify/graphql-default-value-transformer", "@aws-amplify/graphql-directives", "@aws-amplify/graphql-function-transformer", + "@aws-amplify/graphql-generation-transformer", "@aws-amplify/graphql-http-transformer", "@aws-amplify/graphql-index-transformer", "@aws-amplify/graphql-maps-to-transformer", @@ -775,62 +777,52 @@ "@aws-amplify/graphql-relational-transformer", "@aws-amplify/graphql-searchable-transformer", "@aws-amplify/graphql-sql-transformer", - "@aws-amplify/graphql-generation-transformer", - "@aws-amplify/ai-constructs", "@aws-amplify/graphql-transformer", "@aws-amplify/graphql-transformer-core", "@aws-amplify/graphql-transformer-interfaces", "@aws-amplify/platform-core", "@aws-amplify/plugin-types", - "@aws-sdk/client-bedrock-runtime", - "@smithy/eventstream-serde-browser", - "@smithy/eventstream-serde-config-resolver", - "@smithy/eventstream-serde-node", - "@smithy/eventstream-serde-universal", - "@smithy/eventstream-codec", "@aws-crypto/crc32", - "charenc", - "crypt", - "fs-extra", - "graceful-fs", - "graphql", - "graphql-mapping-template", - "graphql-transformer-common", - "hjson", - "immer", - "is-buffer", - "jsonfile", - "libphonenumber-js", - "lodash", - "md5", - "object-hash", - "pluralize", - "ts-dedent", - "universalify", - "zod", - "@aws-sdk/client-sts", - "is-ci", - "lodash.mergewith", - "uuid", "@aws-crypto/sha256-browser", "@aws-crypto/sha256-js", + "@aws-crypto/supports-web-crypto", + "@aws-crypto/util", + "@aws-sdk/client-bedrock-runtime", + "@aws-sdk/client-sso", "@aws-sdk/client-sso-oidc", + "@aws-sdk/client-sts", "@aws-sdk/core", + "@aws-sdk/credential-provider-env", + "@aws-sdk/credential-provider-http", + "@aws-sdk/credential-provider-ini", "@aws-sdk/credential-provider-node", + "@aws-sdk/credential-provider-process", + "@aws-sdk/credential-provider-sso", + "@aws-sdk/credential-provider-web-identity", "@aws-sdk/middleware-host-header", "@aws-sdk/middleware-logger", "@aws-sdk/middleware-recursion-detection", "@aws-sdk/middleware-user-agent", "@aws-sdk/region-config-resolver", + "@aws-sdk/token-providers", "@aws-sdk/types", "@aws-sdk/util-endpoints", + "@aws-sdk/util-locate-window", "@aws-sdk/util-user-agent-browser", "@aws-sdk/util-user-agent-node", + "@smithy/abort-controller", "@smithy/config-resolver", "@smithy/core", + "@smithy/credential-provider-imds", + "@smithy/eventstream-codec", + "@smithy/eventstream-serde-browser", + "@smithy/eventstream-serde-config-resolver", + "@smithy/eventstream-serde-node", + "@smithy/eventstream-serde-universal", "@smithy/fetch-http-handler", "@smithy/hash-node", "@smithy/invalid-dependency", + "@smithy/is-array-buffer", "@smithy/middleware-content-length", "@smithy/middleware-endpoint", "@smithy/middleware-retry", @@ -838,73 +830,82 @@ "@smithy/middleware-stack", "@smithy/node-config-provider", "@smithy/node-http-handler", + "@smithy/property-provider", "@smithy/protocol-http", + "@smithy/querystring-builder", + "@smithy/querystring-parser", + "@smithy/service-error-classification", + "@smithy/shared-ini-file-loader", + "@smithy/signature-v4", "@smithy/smithy-client", "@smithy/types", "@smithy/url-parser", "@smithy/util-base64", "@smithy/util-body-length-browser", "@smithy/util-body-length-node", + "@smithy/util-buffer-from", + "@smithy/util-config-provider", "@smithy/util-defaults-mode-browser", "@smithy/util-defaults-mode-node", "@smithy/util-endpoints", + "@smithy/util-hex-encoding", "@smithy/util-middleware", "@smithy/util-retry", + "@smithy/util-stream", + "@smithy/util-uri-escape", "@smithy/util-utf8", - "tslib", + "bowser", + "charenc", "ci-info", - "@aws-crypto/supports-web-crypto", - "@aws-crypto/util", - "@aws-sdk/util-locate-window", - "@smithy/signature-v4", + "crypt", "fast-xml-parser", - "@aws-sdk/credential-provider-env", - "@aws-sdk/credential-provider-http", - "@aws-sdk/credential-provider-ini", - "@aws-sdk/credential-provider-process", - "@aws-sdk/credential-provider-sso", - "@aws-sdk/credential-provider-web-identity", - "@smithy/credential-provider-imds", - "@smithy/property-provider", - "@smithy/shared-ini-file-loader", - "@smithy/util-config-provider", - "bowser", - "@smithy/querystring-builder", - "@smithy/util-buffer-from", - "@smithy/service-error-classification", - "@smithy/abort-controller", - "@smithy/util-stream", - "@smithy/querystring-parser", - "@smithy/is-array-buffer", - "@smithy/util-hex-encoding", - "@smithy/util-uri-escape", + "fs-extra", + "graceful-fs", + "graphql", + "graphql-mapping-template", + "graphql-transformer-common", + "hjson", + "immer", + "is-buffer", + "is-ci", + "jsonfile", + "libphonenumber-js", + "lodash", + "lodash.mergewith", + "md5", + "object-hash", + "pluralize", + "semver", "strnum", - "@aws-sdk/token-providers", - "@aws-sdk/client-sso", - "semver" + "ts-dedent", + "tslib", + "universalify", + "uuid", + "zod" ], + "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.1.4", + "@aws-amplify/ai-constructs": "^1.0.0", "@aws-amplify/backend-output-schemas": "^1.0.0", "@aws-amplify/backend-output-storage": "^1.0.0", - "@aws-amplify/graphql-api-construct": "1.13.0", - "@aws-amplify/graphql-auth-transformer": "4.1.1", - "@aws-amplify/graphql-conversation-transformer": "0.2.1", - "@aws-amplify/graphql-default-value-transformer": "3.0.3", - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-function-transformer": "3.1.0", - "@aws-amplify/graphql-generation-transformer": "0.2.1", - "@aws-amplify/graphql-http-transformer": "3.0.3", - "@aws-amplify/graphql-index-transformer": "3.0.3", - "@aws-amplify/graphql-maps-to-transformer": "4.0.3", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-predictions-transformer": "3.0.3", - "@aws-amplify/graphql-relational-transformer": "3.0.3", - "@aws-amplify/graphql-searchable-transformer": "3.0.3", - "@aws-amplify/graphql-sql-transformer": "0.4.3", - "@aws-amplify/graphql-transformer": "2.1.1", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/graphql-api-construct": "1.18.5", + "@aws-amplify/graphql-auth-transformer": "4.1.9", + "@aws-amplify/graphql-conversation-transformer": "1.1.4", + "@aws-amplify/graphql-default-value-transformer": "3.1.6", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-function-transformer": "3.1.8", + "@aws-amplify/graphql-generation-transformer": "1.1.2", + "@aws-amplify/graphql-http-transformer": "3.0.11", + "@aws-amplify/graphql-index-transformer": "3.0.11", + "@aws-amplify/graphql-maps-to-transformer": "4.0.11", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-predictions-transformer": "3.0.11", + "@aws-amplify/graphql-relational-transformer": "3.1.3", + "@aws-amplify/graphql-searchable-transformer": "3.0.11", + "@aws-amplify/graphql-sql-transformer": "0.4.11", + "@aws-amplify/graphql-transformer": "2.2.4", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "@aws-amplify/platform-core": "^1.0.0", "@aws-amplify/plugin-types": "^1.0.0", "@aws-crypto/crc32": "5.2.0", @@ -987,8 +988,8 @@ "fs-extra": "^8.1.0", "graceful-fs": "^4.2.0", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", "hjson": "^3.2.2", "immer": "^9.0.12", "is-buffer": "~1.1.6", @@ -1009,26 +1010,29 @@ "zod": "^3.22.2" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/ai-constructs": { - "version": "0.1.4", + "version": "1.0.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.0.1", + "@aws-amplify/backend-output-schemas": "^1.4.0", + "@aws-amplify/platform-core": "^1.2.0", + "@aws-amplify/plugin-types": "^1.3.1", "@aws-sdk/client-bedrock-runtime": "^3.622.0", - "@smithy/types": "^3.3.0" + "@smithy/types": "^3.3.0", + "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/backend-output-schemas": { - "version": "1.2.0", + "version": "1.4.0", "inBundle": true, "license": "Apache-2.0", "peerDependencies": { @@ -1036,306 +1040,296 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/backend-output-storage": { - "version": "1.1.1", + "version": "1.1.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/platform-core": "^1.0.6" + "@aws-amplify/platform-core": "^1.0.6", + "@aws-amplify/plugin-types": "^1.3.1" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0" - } - }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/backend-output-storage/node_modules/@aws-amplify/platform-core": { - "version": "1.0.7", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-amplify/plugin-types": "^1.2.1", - "@aws-sdk/client-sts": "^3.624.0", - "is-ci": "^3.0.1", - "lodash.mergewith": "^4.6.2", - "semver": "^7.6.3", - "uuid": "^9.0.1", - "zod": "^3.22.2" + "aws-cdk-lib": "^2.158.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-auth-transformer": { - "version": "4.1.1", + "version": "4.1.9", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-relational-transformer": "3.0.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-relational-transformer": "3.1.3", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", "lodash": "^4.17.21", "md5": "^2.3.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-conversation-transformer": { - "version": "0.2.1", + "version": "1.1.4", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.1.4", - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-index-transformer": "3.0.3", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-relational-transformer": "3.0.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/ai-constructs": "^1.0.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-index-transformer": "3.0.11", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-relational-transformer": "3.1.3", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "@aws-amplify/plugin-types": "^1.0.0", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "immer": "^9.0.12" + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", + "immer": "^9.0.12", + "semver": "^7.6.3" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-default-value-transformer": { - "version": "3.0.3", + "version": "3.1.6", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", "libphonenumber-js": "1.9.47" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-directives": { - "version": "2.2.0", + "version": "2.6.1", "inBundle": true, "license": "Apache-2.0" }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-function-transformer": { - "version": "3.1.0", + "version": "3.1.8", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-generation-transformer": { - "version": "0.2.1", + "version": "1.1.2", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", "immer": "^9.0.12" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-http-transformer": { - "version": "3.0.3", + "version": "3.0.11", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-index-transformer": { - "version": "3.0.3", + "version": "3.0.11", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-maps-to-transformer": { - "version": "4.0.3", + "version": "4.0.11", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-model-transformer": { - "version": "3.0.3", + "version": "3.1.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-predictions-transformer": { - "version": "3.0.3", + "version": "3.0.11", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-relational-transformer": { - "version": "3.0.3", + "version": "3.1.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-index-transformer": "3.0.3", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-index-transformer": "3.0.11", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", "immer": "^9.0.12" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-searchable-transformer": { - "version": "3.0.3", + "version": "3.0.11", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-sql-transformer": { - "version": "0.4.3", + "version": "0.4.11", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-transformer": { - "version": "2.1.1", + "version": "2.2.4", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-auth-transformer": "4.1.1", - "@aws-amplify/graphql-conversation-transformer": "0.2.1", - "@aws-amplify/graphql-default-value-transformer": "3.0.3", - "@aws-amplify/graphql-function-transformer": "3.1.0", - "@aws-amplify/graphql-generation-transformer": "0.2.1", - "@aws-amplify/graphql-http-transformer": "3.0.3", - "@aws-amplify/graphql-index-transformer": "3.0.3", - "@aws-amplify/graphql-maps-to-transformer": "4.0.3", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-predictions-transformer": "3.0.3", - "@aws-amplify/graphql-relational-transformer": "3.0.3", - "@aws-amplify/graphql-searchable-transformer": "3.0.3", - "@aws-amplify/graphql-sql-transformer": "0.4.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0" + "@aws-amplify/graphql-auth-transformer": "4.1.9", + "@aws-amplify/graphql-conversation-transformer": "1.1.4", + "@aws-amplify/graphql-default-value-transformer": "3.1.6", + "@aws-amplify/graphql-function-transformer": "3.1.8", + "@aws-amplify/graphql-generation-transformer": "1.1.2", + "@aws-amplify/graphql-http-transformer": "3.0.11", + "@aws-amplify/graphql-index-transformer": "3.0.11", + "@aws-amplify/graphql-maps-to-transformer": "4.0.11", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-predictions-transformer": "3.0.11", + "@aws-amplify/graphql-relational-transformer": "3.1.3", + "@aws-amplify/graphql-searchable-transformer": "3.0.11", + "@aws-amplify/graphql-sql-transformer": "0.4.11", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "@aws-amplify/plugin-types": "^1.0.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-transformer-core": { - "version": "3.1.1", + "version": "3.3.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "fs-extra": "^8.1.0", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", "hjson": "^3.2.2", "lodash": "^4.17.21", "md5": "^2.3.0", @@ -1343,24 +1337,24 @@ "ts-dedent": "^2.0.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/graphql-transformer-interfaces": { - "version": "4.1.0", + "version": "4.2.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { "graphql": "^15.5.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/platform-core": { - "version": "1.1.0", + "version": "1.2.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -1374,12 +1368,12 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-amplify/plugin-types": { - "version": "1.2.1", + "version": "1.4.0", "inBundle": true, "license": "Apache-2.0", "peerDependencies": { "@aws-sdk/types": "^3.609.0", - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.158.0", "constructs": "^10.0.0" } }, @@ -1396,6 +1390,18 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/crc32/node_modules/@aws-sdk/types": { + "version": "3.692.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", "inBundle": true, @@ -1410,6 +1416,18 @@ "tslib": "^2.6.2" } }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@aws-sdk/types": { + "version": "3.692.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { "version": "2.2.0", "inBundle": true, @@ -1458,6 +1476,18 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/sha256-js/node_modules/@aws-sdk/types": { + "version": "3.692.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/supports-web-crypto": { "version": "5.2.0", "inBundle": true, @@ -1476,6 +1506,18 @@ "tslib": "^2.6.2" } }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/util/node_modules/@aws-sdk/types": { + "version": "3.692.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { "version": "2.2.0", "inBundle": true, @@ -1512,53 +1554,53 @@ } }, "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime": { - "version": "3.642.0", + "version": "3.693.0", "inBundle": true, "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.637.0", - "@aws-sdk/client-sts": "3.637.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.637.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/eventstream-serde-browser": "^3.0.6", - "@smithy/eventstream-serde-config-resolver": "^3.0.3", - "@smithy/eventstream-serde-node": "^3.0.5", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/eventstream-serde-browser": "^3.0.12", + "@smithy/eventstream-serde-config-resolver": "^3.0.9", + "@smithy/eventstream-serde-node": "^3.0.11", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", "@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.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-stream": "^3.1.3", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-stream": "^3.3.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -1566,47 +1608,47 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sso": { - "version": "3.637.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", "@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.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -1614,48 +1656,48 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.637.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.637.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", "@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.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -1663,72 +1705,23 @@ "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.637.0" - } - }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts": { - "version": "3.637.0", - "inBundle": true, - "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.637.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.637.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@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.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/core": { - "version": "3.635.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/core": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^2.4.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/signature-v4": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, @@ -1736,280 +1729,278 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.620.1", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.635.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.1.3", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.637.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.620.1", - "@aws-sdk/credential-provider-http": "3.635.0", - "@aws-sdk/credential-provider-process": "3.620.1", - "@aws-sdk/credential-provider-sso": "3.637.0", - "@aws-sdk/credential-provider-web-identity": "3.621.0", - "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.637.0" + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.637.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.620.1", - "@aws-sdk/credential-provider-http": "3.635.0", - "@aws-sdk/credential-provider-ini": "3.637.0", - "@aws-sdk/credential-provider-process": "3.620.1", - "@aws-sdk/credential-provider-sso": "3.637.0", - "@aws-sdk/credential-provider-web-identity": "3.621.0", - "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.620.1", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.637.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.637.0", - "@aws-sdk/token-providers": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.621.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.621.0" + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.620.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/middleware-logger": { - "version": "3.609.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.620.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.637.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.614.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.9", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/token-providers": { - "version": "3.614.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.614.0" + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/types": { - "version": "3.609.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/types": { + "version": "3.692.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/util-endpoints": { - "version": "3.637.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", - "@smithy/util-endpoints": "^2.0.5", + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/util-locate-window": { - "version": "3.568.0", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.609.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.614.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { @@ -2024,376 +2015,632 @@ } } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/abort-controller": { - "version": "3.1.1", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^4.1.7", + "@smithy/querystring-builder": "^3.0.10", + "@smithy/types": "^3.7.1", + "@smithy/util-base64": "^3.0.0", "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/config-resolver": { - "version": "3.0.5", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sso": { + "version": "3.637.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", - "@smithy/util-config-provider": "^3.0.0", + "@smithy/url-parser": "^3.0.3", + "@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.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/core": { - "version": "2.4.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.637.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.637.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", "@smithy/protocol-http": "^4.1.0", "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@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.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.637.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/credential-provider-imds": { - "version": "3.2.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@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.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-codec": { - "version": "3.1.2", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-hex-encoding": "^3.0.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@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.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-serde-browser": { - "version": "3.0.6", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.5", - "@smithy/types": "^3.3.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@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.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "3.0.3", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/core": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-serde-node": { - "version": "3.0.5", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.5", - "@smithy/types": "^3.3.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-serde-universal": { - "version": "3.0.5", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^3.1.2", - "@smithy/types": "^3.3.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.4", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", - "@smithy/util-base64": "^3.0.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/hash-node": { - "version": "3.0.3", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/invalid-dependency": { - "version": "3.0.3", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-content-length": { - "version": "3.0.5", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-endpoint": { - "version": "3.1.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-middleware": "^3.0.3", + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-retry": { - "version": "3.0.15", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/service-error-classification": "^3.0.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "tslib": "^2.6.2", - "uuid": "^9.0.1" + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-serde": { - "version": "3.0.3", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-stack": { - "version": "3.0.3", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/node-config-provider": { - "version": "3.1.4", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/node-http-handler": { - "version": "3.1.4", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^3.1.1", - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/property-provider": { - "version": "3.1.3", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/types": { + "version": "3.692.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/protocol-http": { - "version": "4.1.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/querystring-builder": { - "version": "3.0.3", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", - "@smithy/util-uri-escape": "^3.0.0", + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/querystring-parser": { - "version": "3.0.3", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/service-error-classification": { - "version": "3.0.3", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/client-sts/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0" - }, - "engines": { - "node": ">=16.0.0" + "@smithy/protocol-http": "^4.1.7", + "@smithy/querystring-builder": "^3.0.10", + "@smithy/types": "^3.7.1", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.4", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/core": { + "version": "3.635.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@smithy/core": "^2.4.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/signature-v4": { - "version": "4.1.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.620.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", "@smithy/types": "^3.3.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-uri-escape": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/smithy-client": { - "version": "3.2.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.635.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-stack": "^3.0.3", + "@aws-sdk/types": "3.609.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "@smithy/util-stream": "^3.1.3", "tslib": "^2.6.2" @@ -2402,140 +2649,193 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/types": { - "version": "3.3.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.637.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.637.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.637.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/url-parser": { - "version": "3.0.3", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.637.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^3.0.3", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.637.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.637.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-base64": { - "version": "3.0.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.620.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-body-length-browser": { - "version": "3.0.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.637.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/client-sso": "3.637.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-body-length-node": { - "version": "3.0.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.621.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.620.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-config-provider": { - "version": "3.0.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/middleware-logger": { + "version": "3.609.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.15", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.620.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", - "bowser": "^2.11.0", "tslib": "^2.6.2" }, "engines": { - "node": ">= 10.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.15", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.637.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^3.0.5", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">= 10.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-endpoints": { - "version": "2.0.5", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.614.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/types": "3.609.0", "@smithy/node-config-provider": "^3.1.4", "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-hex-encoding": { - "version": "3.0.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/token-providers": { + "version": "3.614.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.614.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-middleware": { - "version": "3.0.3", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/types": { + "version": "3.609.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -2546,360 +2846,953 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-retry": { - "version": "3.0.3", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/util-endpoints": { + "version": "3.637.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^3.0.3", + "@aws-sdk/types": "3.609.0", "@smithy/types": "^3.3.0", + "@smithy/util-endpoints": "^2.0.5", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-stream": { - "version": "3.1.3", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/util-locate-window": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/types": "^3.3.0", - "@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": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-uri-escape": { - "version": "3.0.0", + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.609.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.614.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-utf8": { - "version": "3.0.0", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/abort-controller": { + "version": "3.1.8", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/bowser": { - "version": "2.11.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@aws-amplify/data-construct/node_modules/charenc": { - "version": "0.0.2", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/config-resolver": { + "version": "3.0.12", "inBundle": true, - "license": "BSD-3-Clause", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.11", + "@smithy/types": "^3.7.1", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.10", + "tslib": "^2.6.2" + }, "engines": { - "node": "*" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/ci-info": { - "version": "3.9.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/core": { + "version": "2.5.3", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.10", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-stream": "^3.3.1", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/crypt": { - "version": "0.0.2", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/credential-provider-imds": { + "version": "3.2.7", "inBundle": true, - "license": "BSD-3-Clause", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.11", + "@smithy/property-provider": "^3.1.10", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", + "tslib": "^2.6.2" + }, "engines": { - "node": "*" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/fast-xml-parser": { - "version": "4.4.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - } - ], + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-codec": { + "version": "3.1.9", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^3.7.1", + "@smithy/util-hex-encoding": "^3.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@aws-amplify/data-construct/node_modules/fs-extra": { - "version": "8.1.0", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-serde-browser": { + "version": "3.0.13", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "@smithy/eventstream-serde-universal": "^3.0.12", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/graceful-fs": { - "version": "4.2.11", - "inBundle": true, - "license": "ISC" - }, - "node_modules/@aws-amplify/data-construct/node_modules/graphql": { - "version": "15.9.0", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "3.0.10", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 10.x" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/graphql-mapping-template": { - "version": "5.0.1", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-serde-node": { + "version": "3.0.12", "inBundle": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.12", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@aws-amplify/data-construct/node_modules/graphql-transformer-common": { - "version": "5.0.1", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/eventstream-serde-universal": { + "version": "3.0.12", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "md5": "^2.2.1", - "pluralize": "8.0.0" + "@smithy/eventstream-codec": "^3.1.9", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/hjson": { - "version": "3.2.2", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.9", "inBundle": true, - "license": "MIT", - "bin": { - "hjson": "bin/hjson" + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@aws-amplify/data-construct/node_modules/immer": { - "version": "9.0.21", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/hash-node": { + "version": "3.0.10", "inBundle": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.1", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/is-buffer": { - "version": "1.1.6", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/invalid-dependency": { + "version": "3.0.10", "inBundle": true, - "license": "MIT" + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + } }, - "node_modules/@aws-amplify/data-construct/node_modules/is-ci": { - "version": "3.0.1", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "ci-info": "^3.2.0" + "tslib": "^2.6.2" }, - "bin": { - "is-ci": "bin.js" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/jsonfile": { - "version": "4.0.0", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-content-length": { + "version": "3.0.12", "inBundle": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/libphonenumber-js": { - "version": "1.9.47", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-endpoint": { + "version": "3.2.3", "inBundle": true, - "license": "MIT" + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.3", + "@smithy/middleware-serde": "^3.0.10", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.11", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", + "@smithy/util-middleware": "^3.0.10", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@aws-amplify/data-construct/node_modules/lodash": { - "version": "4.17.21", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-retry": { + "version": "3.0.27", "inBundle": true, - "license": "MIT" + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.11", + "@smithy/protocol-http": "^4.1.7", + "@smithy/service-error-classification": "^3.0.10", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-retry": "^3.0.10", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@aws-amplify/data-construct/node_modules/lodash.mergewith": { - "version": "4.6.2", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-serde": { + "version": "3.0.10", "inBundle": true, - "license": "MIT" + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@aws-amplify/data-construct/node_modules/md5": { - "version": "2.3.0", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/middleware-stack": { + "version": "3.0.10", "inBundle": true, - "license": "BSD-3-Clause", + "license": "Apache-2.0", "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/object-hash": { - "version": "3.0.0", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/node-config-provider": { + "version": "3.1.11", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.10", + "@smithy/shared-ini-file-loader": "^3.1.11", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 6" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/pluralize": { - "version": "8.0.0", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/node-http-handler": { + "version": "3.3.1", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.8", + "@smithy/protocol-http": "^4.1.7", + "@smithy/querystring-builder": "^3.0.10", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/semver": { - "version": "7.6.3", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/property-provider": { + "version": "3.1.10", "inBundle": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/strnum": { - "version": "1.0.5", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/protocol-http": { + "version": "4.1.7", "inBundle": true, - "license": "MIT" + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@aws-amplify/data-construct/node_modules/ts-dedent": { - "version": "2.2.0", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/querystring-builder": { + "version": "3.0.10", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.1", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.10" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/tslib": { - "version": "2.7.0", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/querystring-parser": { + "version": "3.0.10", "inBundle": true, - "license": "0BSD" + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@aws-amplify/data-construct/node_modules/universalify": { - "version": "0.1.2", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/service-error-classification": { + "version": "3.0.10", "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.1" + }, "engines": { - "node": ">= 4.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/uuid": { - "version": "9.0.1", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.11", "inBundle": true, - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-construct/node_modules/zod": { - "version": "3.23.8", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/signature-v4": { + "version": "4.2.3", "inBundle": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-schema": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema/-/data-schema-1.16.1.tgz", - "integrity": "sha512-ThEiEoDbGfU03a2wVpdW4VORLrUkrlWMb9Xc6kI6I296+Gk0DHKNmQUFov4nlqxUIBe3lntJUcZSCMWJZTq4ZQ==", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/smithy-client": { + "version": "3.4.4", + "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/data-schema-types": "*", + "@smithy/core": "^2.5.3", + "@smithy/middleware-endpoint": "^3.2.3", + "@smithy/middleware-stack": "^3.0.10", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", + "@smithy/util-stream": "^3.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/types": { + "version": "3.7.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/url-parser": { + "version": "3.0.10", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.10", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-base64": { + "version": "3.0.0", + "inBundle": true, + "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/@aws-amplify/data-construct/node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.27", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.10", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.27", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^3.0.12", + "@smithy/credential-provider-imds": "^3.2.7", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/property-provider": "^3.1.10", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-endpoints": { + "version": "2.1.6", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.11", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-middleware": { + "version": "3.0.10", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-retry": { + "version": "3.0.10", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.10", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-stream": { + "version": "3.3.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/types": "^3.7.1", "@smithy/util-base64": "^3.0.0", - "@types/aws-lambda": "^8.10.134", - "@types/json-schema": "^7.0.15", - "rxjs": "^7.8.1" + "@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": ">=16.0.0" } }, - "node_modules/@aws-amplify/data-schema-types": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema-types/-/data-schema-types-1.2.0.tgz", - "integrity": "sha512-1hy2r7jl3hQ5J/CGjhmPhFPcdGSakfme1ZLjlTMJZILfYifZLSlGRKNCelMb3J5N9203hyeT5XDi5yR47JL1TQ==", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.1", + "inBundle": true, + "license": "Apache-2.0", "dependencies": { - "graphql": "15.8.0", - "rxjs": "^7.8.1" + "@smithy/protocol-http": "^4.1.7", + "@smithy/querystring-builder": "^3.0.10", + "@smithy/types": "^3.7.1", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@aws-amplify/data-schema-types/node_modules/graphql": { - "version": "15.8.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz", - "integrity": "sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==", - "license": "MIT", + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 10.x" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/datastore": { - "version": "5.0.49", - "resolved": "https://registry.npmjs.org/@aws-amplify/datastore/-/datastore-5.0.49.tgz", - "integrity": "sha512-7ESzc1/5rOth3gPi65IJyx4dQbzRTvRxpefcA132C3vAm5BFqKEcDXyZX5sHbSf5OOMmQDsa68GfzhAM10rWPg==", - "dev": true, + "node_modules/@aws-amplify/data-construct/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/api": "6.0.49", - "buffer": "4.9.2", - "idb": "5.0.6", - "immer": "9.0.6", - "rxjs": "^7.8.1", - "ulid": "^2.3.0" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@aws-amplify/core": "^6.1.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/deployed-backend-client": { - "resolved": "packages/deployed-backend-client", - "link": true + "node_modules/@aws-amplify/data-construct/node_modules/bowser": { + "version": "2.11.0", + "inBundle": true, + "license": "MIT" }, - "node_modules/@aws-amplify/form-generator": { - "resolved": "packages/form-generator", - "link": true + "node_modules/@aws-amplify/data-construct/node_modules/charenc": { + "version": "0.0.2", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } }, - "node_modules/@aws-amplify/graphql-api-construct": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@aws-amplify/graphql-api-construct/-/graphql-api-construct-1.13.0.tgz", - "integrity": "sha512-4cv4Ko7OP7lZTDhojM/ibKglP1Ospy+/iGMnKguuYoISqhmL2sgnyVHWw+1XnRVc8eUf6gnip2KXMQKnaPU0Lw==", - "bundleDependencies": [ - "@aws-amplify/backend-output-schemas", + "node_modules/@aws-amplify/data-construct/node_modules/ci-info": { + "version": "3.9.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/crypt": { + "version": "0.0.2", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/fast-xml-parser": { + "version": "4.4.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/fs-extra": { + "version": "8.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@aws-amplify/data-construct/node_modules/graphql": { + "version": "15.9.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/graphql-mapping-template": { + "version": "5.0.2", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/@aws-amplify/data-construct/node_modules/graphql-transformer-common": { + "version": "5.1.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "md5": "^2.2.1", + "pluralize": "8.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/hjson": { + "version": "3.2.2", + "inBundle": true, + "license": "MIT", + "bin": { + "hjson": "bin/hjson" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/immer": { + "version": "9.0.21", + "inBundle": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/is-buffer": { + "version": "1.1.6", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-amplify/data-construct/node_modules/is-ci": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/jsonfile": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/libphonenumber-js": { + "version": "1.9.47", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-amplify/data-construct/node_modules/lodash": { + "version": "4.17.21", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-amplify/data-construct/node_modules/lodash.mergewith": { + "version": "4.6.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-amplify/data-construct/node_modules/md5": { + "version": "2.3.0", + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/object-hash": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/pluralize": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/semver": { + "version": "7.6.3", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/strnum": { + "version": "1.0.5", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-amplify/data-construct/node_modules/ts-dedent": { + "version": "2.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/tslib": { + "version": "2.8.1", + "inBundle": true, + "license": "0BSD" + }, + "node_modules/@aws-amplify/data-construct/node_modules/universalify": { + "version": "0.1.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "inBundle": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@aws-amplify/data-construct/node_modules/zod": { + "version": "3.23.8", + "inBundle": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@aws-amplify/data-schema": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema/-/data-schema-1.16.1.tgz", + "integrity": "sha512-ThEiEoDbGfU03a2wVpdW4VORLrUkrlWMb9Xc6kI6I296+Gk0DHKNmQUFov4nlqxUIBe3lntJUcZSCMWJZTq4ZQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/data-schema-types": "*", + "@smithy/util-base64": "^3.0.0", + "@types/aws-lambda": "^8.10.134", + "@types/json-schema": "^7.0.15", + "rxjs": "^7.8.1" + } + }, + "node_modules/@aws-amplify/data-schema-types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema-types/-/data-schema-types-1.2.0.tgz", + "integrity": "sha512-1hy2r7jl3hQ5J/CGjhmPhFPcdGSakfme1ZLjlTMJZILfYifZLSlGRKNCelMb3J5N9203hyeT5XDi5yR47JL1TQ==", + "dependencies": { + "graphql": "15.8.0", + "rxjs": "^7.8.1" + } + }, + "node_modules/@aws-amplify/data-schema-types/node_modules/graphql": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz", + "integrity": "sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==", + "license": "MIT", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/@aws-amplify/datastore": { + "version": "5.0.49", + "resolved": "https://registry.npmjs.org/@aws-amplify/datastore/-/datastore-5.0.49.tgz", + "integrity": "sha512-7ESzc1/5rOth3gPi65IJyx4dQbzRTvRxpefcA132C3vAm5BFqKEcDXyZX5sHbSf5OOMmQDsa68GfzhAM10rWPg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/api": "6.0.49", + "buffer": "4.9.2", + "idb": "5.0.6", + "immer": "9.0.6", + "rxjs": "^7.8.1", + "ulid": "^2.3.0" + }, + "peerDependencies": { + "@aws-amplify/core": "^6.1.0" + } + }, + "node_modules/@aws-amplify/deployed-backend-client": { + "resolved": "packages/deployed-backend-client", + "link": true + }, + "node_modules/@aws-amplify/form-generator": { + "resolved": "packages/form-generator", + "link": true + }, + "node_modules/@aws-amplify/graphql-api-construct": { + "version": "1.18.5", + "resolved": "https://registry.npmjs.org/@aws-amplify/graphql-api-construct/-/graphql-api-construct-1.18.5.tgz", + "integrity": "sha512-QZXZJ7R15eFfILjwbawcLDtuOX9J7JQvDhVv3/AJqAWWsLCk2nAXcL/rBPdZ27P6OO53LPGWGBzPccsWMj7S2w==", + "bundleDependencies": [ + "@aws-amplify/ai-constructs", + "@aws-amplify/backend-output-schemas", "@aws-amplify/backend-output-storage", "@aws-amplify/graphql-auth-transformer", "@aws-amplify/graphql-conversation-transformer", @@ -2920,56 +3813,47 @@ "@aws-amplify/graphql-transformer-interfaces", "@aws-amplify/platform-core", "@aws-amplify/plugin-types", - "@aws-amplify/ai-constructs", - "@aws-sdk/client-bedrock-runtime", - "@smithy/eventstream-serde-browser", - "@smithy/eventstream-serde-config-resolver", - "@smithy/eventstream-serde-node", - "@smithy/eventstream-serde-universal", - "@smithy/eventstream-codec", "@aws-crypto/crc32", - "charenc", - "crypt", - "fs-extra", - "graceful-fs", - "graphql", - "graphql-mapping-template", - "graphql-transformer-common", - "hjson", - "immer", - "is-buffer", - "jsonfile", - "libphonenumber-js", - "lodash", - "md5", - "object-hash", - "pluralize", - "ts-dedent", - "universalify", - "zod", - "@aws-sdk/client-sts", - "is-ci", - "lodash.mergewith", - "uuid", "@aws-crypto/sha256-browser", "@aws-crypto/sha256-js", + "@aws-crypto/supports-web-crypto", + "@aws-crypto/util", + "@aws-sdk/client-bedrock-runtime", + "@aws-sdk/client-sso", "@aws-sdk/client-sso-oidc", + "@aws-sdk/client-sts", "@aws-sdk/core", + "@aws-sdk/credential-provider-env", + "@aws-sdk/credential-provider-http", + "@aws-sdk/credential-provider-ini", "@aws-sdk/credential-provider-node", + "@aws-sdk/credential-provider-process", + "@aws-sdk/credential-provider-sso", + "@aws-sdk/credential-provider-web-identity", "@aws-sdk/middleware-host-header", "@aws-sdk/middleware-logger", "@aws-sdk/middleware-recursion-detection", "@aws-sdk/middleware-user-agent", "@aws-sdk/region-config-resolver", + "@aws-sdk/token-providers", "@aws-sdk/types", "@aws-sdk/util-endpoints", + "@aws-sdk/util-locate-window", "@aws-sdk/util-user-agent-browser", "@aws-sdk/util-user-agent-node", + "@smithy/abort-controller", "@smithy/config-resolver", "@smithy/core", + "@smithy/credential-provider-imds", + "@smithy/eventstream-codec", + "@smithy/eventstream-serde-browser", + "@smithy/eventstream-serde-config-resolver", + "@smithy/eventstream-serde-node", + "@smithy/eventstream-serde-universal", "@smithy/fetch-http-handler", "@smithy/hash-node", "@smithy/invalid-dependency", + "@smithy/is-array-buffer", "@smithy/middleware-content-length", "@smithy/middleware-endpoint", "@smithy/middleware-retry", @@ -2977,689 +3861,1210 @@ "@smithy/middleware-stack", "@smithy/node-config-provider", "@smithy/node-http-handler", + "@smithy/property-provider", "@smithy/protocol-http", + "@smithy/querystring-builder", + "@smithy/querystring-parser", + "@smithy/service-error-classification", + "@smithy/shared-ini-file-loader", + "@smithy/signature-v4", "@smithy/smithy-client", "@smithy/types", "@smithy/url-parser", "@smithy/util-base64", "@smithy/util-body-length-browser", "@smithy/util-body-length-node", + "@smithy/util-buffer-from", + "@smithy/util-config-provider", "@smithy/util-defaults-mode-browser", "@smithy/util-defaults-mode-node", "@smithy/util-endpoints", + "@smithy/util-hex-encoding", "@smithy/util-middleware", "@smithy/util-retry", + "@smithy/util-stream", + "@smithy/util-uri-escape", "@smithy/util-utf8", - "tslib", + "bowser", + "charenc", "ci-info", - "@aws-crypto/supports-web-crypto", - "@aws-crypto/util", - "@aws-sdk/util-locate-window", - "@smithy/signature-v4", + "crypt", "fast-xml-parser", - "@aws-sdk/credential-provider-env", - "@aws-sdk/credential-provider-http", - "@aws-sdk/credential-provider-ini", - "@aws-sdk/credential-provider-process", - "@aws-sdk/credential-provider-sso", - "@aws-sdk/credential-provider-web-identity", - "@smithy/credential-provider-imds", - "@smithy/property-provider", - "@smithy/shared-ini-file-loader", - "@smithy/util-config-provider", - "bowser", - "@smithy/querystring-builder", - "@smithy/util-buffer-from", - "@smithy/service-error-classification", - "@smithy/abort-controller", - "@smithy/util-stream", - "@smithy/querystring-parser", - "@smithy/is-array-buffer", - "@smithy/util-hex-encoding", - "@smithy/util-uri-escape", + "fs-extra", + "graceful-fs", + "graphql", + "graphql-mapping-template", + "graphql-transformer-common", + "hjson", + "immer", + "is-buffer", + "is-ci", + "jsonfile", + "libphonenumber-js", + "lodash", + "lodash.mergewith", + "md5", + "object-hash", + "pluralize", + "semver", "strnum", - "@aws-sdk/token-providers", - "@aws-sdk/client-sso", - "semver" + "ts-dedent", + "tslib", + "universalify", + "uuid", + "zod" ], + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/ai-constructs": "^1.0.0", + "@aws-amplify/backend-output-schemas": "^1.0.0", + "@aws-amplify/backend-output-storage": "^1.0.0", + "@aws-amplify/graphql-auth-transformer": "4.1.9", + "@aws-amplify/graphql-conversation-transformer": "1.1.4", + "@aws-amplify/graphql-default-value-transformer": "3.1.6", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-function-transformer": "3.1.8", + "@aws-amplify/graphql-generation-transformer": "1.1.2", + "@aws-amplify/graphql-http-transformer": "3.0.11", + "@aws-amplify/graphql-index-transformer": "3.0.11", + "@aws-amplify/graphql-maps-to-transformer": "4.0.11", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-predictions-transformer": "3.0.11", + "@aws-amplify/graphql-relational-transformer": "3.1.3", + "@aws-amplify/graphql-searchable-transformer": "3.0.11", + "@aws-amplify/graphql-sql-transformer": "0.4.11", + "@aws-amplify/graphql-transformer": "2.2.4", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "@aws-amplify/platform-core": "^1.0.0", + "@aws-amplify/plugin-types": "^1.0.0", + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/client-bedrock-runtime": "^3.622.0", + "@aws-sdk/client-sso": "3.637.0", + "@aws-sdk/client-sso-oidc": "3.637.0", + "@aws-sdk/client-sts": "^3.624.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.637.0", + "@aws-sdk/credential-provider-node": "3.637.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.637.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/abort-controller": "^3.1.1", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/eventstream-codec": "^3.1.2", + "@smithy/eventstream-serde-browser": "^3.0.6", + "@smithy/eventstream-serde-config-resolver": "^3.0.3", + "@smithy/eventstream-serde-node": "^3.0.5", + "@smithy/eventstream-serde-universal": "^3.0.5", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/querystring-parser": "^3.0.3", + "@smithy/service-error-classification": "^3.0.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "bowser": "^2.11.0", + "charenc": "^0.0.2", + "ci-info": "^3.2.0", + "crypt": "^0.0.2", + "fast-xml-parser": "4.4.1", + "fs-extra": "^8.1.0", + "graceful-fs": "^4.2.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", + "hjson": "^3.2.2", + "immer": "^9.0.12", + "is-buffer": "~1.1.6", + "is-ci": "^3.0.1", + "jsonfile": "^4.0.0", + "libphonenumber-js": "1.9.47", + "lodash": "^4.17.21", + "lodash.mergewith": "^4.6.2", + "md5": "^2.2.1", + "object-hash": "^3.0.0", + "pluralize": "8.0.0", + "semver": "^7.6.3", + "strnum": "^1.0.5", + "ts-dedent": "^2.0.0", + "tslib": "^2.6.2", + "universalify": "^0.1.0", + "uuid": "^9.0.1", + "zod": "^3.22.2" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/ai-constructs": { + "version": "1.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/backend-output-schemas": "^1.4.0", + "@aws-amplify/platform-core": "^1.2.0", + "@aws-amplify/plugin-types": "^1.3.1", + "@aws-sdk/client-bedrock-runtime": "^3.622.0", + "@smithy/types": "^3.3.0", + "json-schema-to-ts": "^3.1.1" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.158.0", + "constructs": "^10.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/backend-output-schemas": { + "version": "1.4.0", + "inBundle": true, + "license": "Apache-2.0", + "peerDependencies": { + "zod": "^3.22.2" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/backend-output-storage": { + "version": "1.1.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/backend-output-schemas": "^1.2.0", + "@aws-amplify/platform-core": "^1.0.6", + "@aws-amplify/plugin-types": "^1.3.1" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.158.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-auth-transformer": { + "version": "4.1.9", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-relational-transformer": "3.1.3", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", + "lodash": "^4.17.21", + "md5": "^2.3.0" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-conversation-transformer": { + "version": "1.1.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/ai-constructs": "^1.0.0", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-index-transformer": "3.0.11", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-relational-transformer": "3.1.3", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "@aws-amplify/plugin-types": "^1.0.0", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", + "immer": "^9.0.12", + "semver": "^7.6.3" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-default-value-transformer": { + "version": "3.1.6", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", + "libphonenumber-js": "1.9.47" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-directives": { + "version": "2.6.1", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-function-transformer": { + "version": "3.1.8", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-generation-transformer": { + "version": "1.1.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", + "immer": "^9.0.12" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-http-transformer": { + "version": "3.0.11", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-index-transformer": { + "version": "3.0.11", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-maps-to-transformer": { + "version": "4.0.11", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-model-transformer": { + "version": "3.1.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-predictions-transformer": { + "version": "3.0.11", + "inBundle": true, + "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.1.4", - "@aws-amplify/backend-output-schemas": "^1.0.0", - "@aws-amplify/backend-output-storage": "^1.0.0", - "@aws-amplify/graphql-auth-transformer": "4.1.1", - "@aws-amplify/graphql-conversation-transformer": "0.2.1", - "@aws-amplify/graphql-default-value-transformer": "3.0.3", - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-function-transformer": "3.1.0", - "@aws-amplify/graphql-generation-transformer": "0.2.1", - "@aws-amplify/graphql-http-transformer": "3.0.3", - "@aws-amplify/graphql-index-transformer": "3.0.3", - "@aws-amplify/graphql-maps-to-transformer": "4.0.3", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-predictions-transformer": "3.0.3", - "@aws-amplify/graphql-relational-transformer": "3.0.3", - "@aws-amplify/graphql-searchable-transformer": "3.0.3", - "@aws-amplify/graphql-sql-transformer": "0.4.3", - "@aws-amplify/graphql-transformer": "2.1.1", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "@aws-amplify/platform-core": "^1.0.0", - "@aws-amplify/plugin-types": "^1.0.0", - "@aws-crypto/crc32": "5.2.0", - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/client-bedrock-runtime": "^3.622.0", - "@aws-sdk/client-sso": "3.637.0", - "@aws-sdk/client-sso-oidc": "3.637.0", - "@aws-sdk/client-sts": "^3.624.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-env": "3.620.1", - "@aws-sdk/credential-provider-http": "3.635.0", - "@aws-sdk/credential-provider-ini": "3.637.0", - "@aws-sdk/credential-provider-node": "3.637.0", - "@aws-sdk/credential-provider-process": "3.620.1", - "@aws-sdk/credential-provider-sso": "3.637.0", - "@aws-sdk/credential-provider-web-identity": "3.621.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/token-providers": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/abort-controller": "^3.1.1", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/eventstream-codec": "^3.1.2", - "@smithy/eventstream-serde-browser": "^3.0.6", - "@smithy/eventstream-serde-config-resolver": "^3.0.3", - "@smithy/eventstream-serde-node": "^3.0.5", - "@smithy/eventstream-serde-universal": "^3.0.5", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/querystring-parser": "^3.0.3", - "@smithy/service-error-classification": "^3.0.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/signature-v4": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-stream": "^3.1.3", - "@smithy/util-uri-escape": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "bowser": "^2.11.0", - "charenc": "^0.0.2", - "ci-info": "^3.2.0", - "crypt": "^0.0.2", - "fast-xml-parser": "4.4.1", + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-relational-transformer": { + "version": "3.1.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-index-transformer": "3.0.11", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", + "immer": "^9.0.12" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-searchable-transformer": { + "version": "3.0.11", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-sql-transformer": { + "version": "0.4.11", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "graphql": "^15.5.0", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-transformer": { + "version": "2.2.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-auth-transformer": "4.1.9", + "@aws-amplify/graphql-conversation-transformer": "1.1.4", + "@aws-amplify/graphql-default-value-transformer": "3.1.6", + "@aws-amplify/graphql-function-transformer": "3.1.8", + "@aws-amplify/graphql-generation-transformer": "1.1.2", + "@aws-amplify/graphql-http-transformer": "3.0.11", + "@aws-amplify/graphql-index-transformer": "3.0.11", + "@aws-amplify/graphql-maps-to-transformer": "4.0.11", + "@aws-amplify/graphql-model-transformer": "3.1.3", + "@aws-amplify/graphql-predictions-transformer": "3.0.11", + "@aws-amplify/graphql-relational-transformer": "3.1.3", + "@aws-amplify/graphql-searchable-transformer": "3.0.11", + "@aws-amplify/graphql-sql-transformer": "0.4.11", + "@aws-amplify/graphql-transformer-core": "3.3.3", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", + "@aws-amplify/plugin-types": "^1.0.0" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-transformer-core": { + "version": "3.3.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/graphql-directives": "2.6.1", + "@aws-amplify/graphql-transformer-interfaces": "4.2.1", "fs-extra": "^8.1.0", - "graceful-fs": "^4.2.0", "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", + "graphql-mapping-template": "5.0.2", + "graphql-transformer-common": "5.1.2", "hjson": "^3.2.2", - "immer": "^9.0.12", - "is-buffer": "~1.1.6", - "is-ci": "^3.0.1", - "jsonfile": "^4.0.0", - "libphonenumber-js": "1.9.47", "lodash": "^4.17.21", - "lodash.mergewith": "^4.6.2", - "md5": "^2.2.1", + "md5": "^2.3.0", "object-hash": "^3.0.0", - "pluralize": "8.0.0", - "semver": "^7.6.3", - "strnum": "^1.0.5", - "ts-dedent": "^2.0.0", - "tslib": "^2.6.2", - "universalify": "^0.1.0", - "uuid": "^9.0.1", - "zod": "^3.22.2" + "ts-dedent": "^2.0.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", + "aws-cdk-lib": "^2.168.0", "constructs": "^10.3.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/ai-constructs": { - "version": "0.1.4", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-transformer-interfaces": { + "version": "4.2.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.0.1", - "@aws-sdk/client-bedrock-runtime": "^3.622.0", - "@smithy/types": "^3.3.0" + "graphql": "^15.5.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.0.0" + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.3.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/backend-output-schemas": { + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/platform-core": { "version": "1.2.0", "inBundle": true, "license": "Apache-2.0", - "peerDependencies": { + "dependencies": { + "@aws-amplify/plugin-types": "^1.2.1", + "@aws-sdk/client-sts": "^3.624.0", + "is-ci": "^3.0.1", + "lodash.mergewith": "^4.6.2", + "semver": "^7.6.3", + "uuid": "^9.0.1", "zod": "^3.22.2" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/backend-output-storage": { - "version": "1.1.1", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/plugin-types": { + "version": "1.4.0", + "inBundle": true, + "license": "Apache-2.0", + "peerDependencies": { + "@aws-sdk/types": "^3.609.0", + "aws-cdk-lib": "^2.158.0", + "constructs": "^10.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/crc32/node_modules/@aws-sdk/types": { + "version": "3.692.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "inBundle": true, + "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-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@aws-sdk/types": { + "version": "3.692.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/platform-core": "^1.0.6" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/backend-output-storage/node_modules/@aws-amplify/platform-core": { - "version": "1.0.7", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.2.1", - "@aws-sdk/client-sts": "^3.624.0", - "is-ci": "^3.0.1", - "lodash.mergewith": "^4.6.2", - "semver": "^7.6.3", - "uuid": "^9.0.1", - "zod": "^3.22.2" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-auth-transformer": { - "version": "4.1.1", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-relational-transformer": "3.0.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "lodash": "^4.17.21", - "md5": "^2.3.0" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-conversation-transformer": { - "version": "0.2.1", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-js/node_modules/@aws-sdk/types": { + "version": "3.692.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^0.1.4", - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-index-transformer": "3.0.3", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-relational-transformer": "3.0.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "immer": "^9.0.12" + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-default-value-transformer": { - "version": "3.0.3", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "libphonenumber-js": "1.9.47" + "tslib": "^2.6.2" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-directives": { - "version": "2.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/util": { + "version": "5.2.0", "inBundle": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-function-transformer": { - "version": "3.1.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/util/node_modules/@aws-sdk/types": { + "version": "3.692.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-generation-transformer": { - "version": "0.2.1", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "immer": "^9.0.12" + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-http-transformer": { - "version": "3.0.3", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-index-transformer": { - "version": "3.0.3", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-maps-to-transformer": { - "version": "4.0.3", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/eventstream-serde-browser": "^3.0.12", + "@smithy/eventstream-serde-config-resolver": "^3.0.9", + "@smithy/eventstream-serde-node": "^3.0.11", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@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.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-stream": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-model-transformer": { - "version": "3.0.3", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@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.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-predictions-transformer": { - "version": "3.0.3", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@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.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-relational-transformer": { - "version": "3.0.3", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/core": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-index-transformer": "3.0.3", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "immer": "^9.0.12" + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-searchable-transformer": { - "version": "3.0.3", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-sql-transformer": { - "version": "0.4.3", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-transformer": { - "version": "2.1.1", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-auth-transformer": "4.1.1", - "@aws-amplify/graphql-conversation-transformer": "0.2.1", - "@aws-amplify/graphql-default-value-transformer": "3.0.3", - "@aws-amplify/graphql-function-transformer": "3.1.0", - "@aws-amplify/graphql-generation-transformer": "0.2.1", - "@aws-amplify/graphql-http-transformer": "3.0.3", - "@aws-amplify/graphql-index-transformer": "3.0.3", - "@aws-amplify/graphql-maps-to-transformer": "4.0.3", - "@aws-amplify/graphql-model-transformer": "3.0.3", - "@aws-amplify/graphql-predictions-transformer": "3.0.3", - "@aws-amplify/graphql-relational-transformer": "3.0.3", - "@aws-amplify/graphql-searchable-transformer": "3.0.3", - "@aws-amplify/graphql-sql-transformer": "0.4.3", - "@aws-amplify/graphql-transformer-core": "3.1.1", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-transformer-core": { - "version": "3.1.1", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/graphql-directives": "2.2.0", - "@aws-amplify/graphql-transformer-interfaces": "4.1.0", - "fs-extra": "^8.1.0", - "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", - "graphql-transformer-common": "5.0.1", - "hjson": "^3.2.2", - "lodash": "^4.17.21", - "md5": "^2.3.0", - "object-hash": "^3.0.0", - "ts-dedent": "^2.0.0" + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/graphql-transformer-interfaces": { - "version": "4.1.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "graphql": "^15.5.0" + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.3.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/platform-core": { - "version": "1.1.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.2.1", - "@aws-sdk/client-sts": "^3.624.0", - "is-ci": "^3.0.1", - "lodash.mergewith": "^4.6.2", - "semver": "^7.6.3", - "uuid": "^9.0.1", - "zod": "^3.22.2" + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-amplify/plugin-types": { - "version": "1.2.1", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, "peerDependencies": { - "@aws-sdk/types": "^3.609.0", - "aws-cdk-lib": "^2.152.0", - "constructs": "^10.0.0" + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/crc32": { - "version": "5.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", "inBundle": true, "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", + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/types": { + "version": "3.692.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/util": { - "version": "5.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", + "@smithy/protocol-http": "^4.1.7", + "@smithy/querystring-builder": "^3.0.10", + "@smithy/types": "^3.7.1", + "@smithy/util-base64": "^3.0.0", "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-bedrock-runtime": { - "version": "3.642.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sso": { + "version": "3.637.0", "inBundle": true, "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.637.0", - "@aws-sdk/client-sts": "3.637.0", "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.637.0", "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-recursion-detection": "3.620.0", @@ -3671,9 +5076,6 @@ "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", "@smithy/core": "^2.4.0", - "@smithy/eventstream-serde-browser": "^3.0.6", - "@smithy/eventstream-serde-config-resolver": "^3.0.3", - "@smithy/eventstream-serde-node": "^3.0.5", "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", @@ -3696,7 +5098,6 @@ "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", - "@smithy/util-stream": "^3.1.3", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -3704,7 +5105,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sso": { + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sso-oidc": { "version": "3.637.0", "inBundle": true, "license": "Apache-2.0", @@ -3712,6 +5113,7 @@ "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.637.0", "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-recursion-detection": "3.620.0", @@ -3748,110 +5150,480 @@ "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.637.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "inBundle": true, + "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.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@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.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@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.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@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.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/types": { + "version": "3.692.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.637.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.637.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@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.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-utf8": "^3.0.0", + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.637.0" } }, - "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts": { - "version": "3.637.0", + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", "inBundle": true, "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.637.0", - "@aws-sdk/core": "3.635.0", - "@aws-sdk/credential-provider-node": "3.637.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.637.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.637.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.4.0", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@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.15", - "@smithy/util-defaults-mode-node": "^3.0.15", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-utf8": "^3.0.0", + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/client-sts/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.7", + "@smithy/querystring-builder": "^3.0.10", + "@smithy/types": "^3.7.1", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/core": { @@ -4119,7 +5891,7 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@aws-sdk/util-locate-window": { - "version": "3.568.0", + "version": "3.693.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -4163,11 +5935,11 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/abort-controller": { - "version": "3.1.1", + "version": "3.1.8", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4175,14 +5947,14 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/config-resolver": { - "version": "3.0.5", + "version": "3.0.12", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/types": "^3.7.1", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.10", "tslib": "^2.6.2" }, "engines": { @@ -4190,18 +5962,16 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/core": { - "version": "2.4.0", + "version": "2.5.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "@smithy/middleware-serde": "^3.0.10", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-stream": "^3.3.1", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -4210,14 +5980,14 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/credential-provider-imds": { - "version": "3.2.0", + "version": "3.2.7", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/property-provider": "^3.1.10", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", "tslib": "^2.6.2" }, "engines": { @@ -4225,23 +5995,23 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/eventstream-codec": { - "version": "3.1.2", + "version": "3.1.9", "inBundle": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.1", "@smithy/util-hex-encoding": "^3.0.0", "tslib": "^2.6.2" } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/eventstream-serde-browser": { - "version": "3.0.6", + "version": "3.0.13", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.5", - "@smithy/types": "^3.3.0", + "@smithy/eventstream-serde-universal": "^3.0.12", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4249,11 +6019,11 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "3.0.3", + "version": "3.0.10", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4261,12 +6031,12 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/eventstream-serde-node": { - "version": "3.0.5", + "version": "3.0.12", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.5", - "@smithy/types": "^3.3.0", + "@smithy/eventstream-serde-universal": "^3.0.12", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4274,12 +6044,12 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/eventstream-serde-universal": { - "version": "3.0.5", + "version": "3.0.12", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^3.1.2", - "@smithy/types": "^3.3.0", + "@smithy/eventstream-codec": "^3.1.9", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4287,23 +6057,23 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.4", + "version": "3.2.9", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", "@smithy/util-base64": "^3.0.0", "tslib": "^2.6.2" } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/hash-node": { - "version": "3.0.3", + "version": "3.0.10", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.1", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -4313,11 +6083,11 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/invalid-dependency": { - "version": "3.0.3", + "version": "3.0.10", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" } }, @@ -4333,12 +6103,12 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/middleware-content-length": { - "version": "3.0.5", + "version": "3.0.12", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4346,16 +6116,17 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/middleware-endpoint": { - "version": "3.1.0", + "version": "3.2.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-middleware": "^3.0.3", + "@smithy/core": "^2.5.3", + "@smithy/middleware-serde": "^3.0.10", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.11", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", + "@smithy/util-middleware": "^3.0.10", "tslib": "^2.6.2" }, "engines": { @@ -4363,17 +6134,17 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/middleware-retry": { - "version": "3.0.15", + "version": "3.0.27", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/service-error-classification": "^3.0.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/protocol-http": "^4.1.7", + "@smithy/service-error-classification": "^3.0.10", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-retry": "^3.0.10", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -4382,11 +6153,11 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/middleware-serde": { - "version": "3.0.3", + "version": "3.0.10", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4394,11 +6165,11 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/middleware-stack": { - "version": "3.0.3", + "version": "3.0.10", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4406,13 +6177,13 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/node-config-provider": { - "version": "3.1.4", + "version": "3.1.11", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/property-provider": "^3.1.10", + "@smithy/shared-ini-file-loader": "^3.1.11", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4420,14 +6191,14 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/node-http-handler": { - "version": "3.1.4", + "version": "3.3.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^3.1.1", - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/abort-controller": "^3.1.8", + "@smithy/protocol-http": "^4.1.7", + "@smithy/querystring-builder": "^3.0.10", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4435,11 +6206,11 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/property-provider": { - "version": "3.1.3", + "version": "3.1.10", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4447,11 +6218,11 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/protocol-http": { - "version": "4.1.0", + "version": "4.1.7", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4459,11 +6230,11 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/querystring-builder": { - "version": "3.0.3", + "version": "3.0.10", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.1", "@smithy/util-uri-escape": "^3.0.0", "tslib": "^2.6.2" }, @@ -4472,11 +6243,11 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/querystring-parser": { - "version": "3.0.3", + "version": "3.0.10", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4484,22 +6255,22 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/service-error-classification": { - "version": "3.0.3", + "version": "3.0.10", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0" + "@smithy/types": "^3.7.1" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.4", + "version": "3.1.11", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4507,15 +6278,15 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/signature-v4": { - "version": "4.1.0", + "version": "4.2.3", "inBundle": true, "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.10", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -4525,15 +6296,16 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/smithy-client": { - "version": "3.2.0", + "version": "3.4.4", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.1.3", + "@smithy/core": "^2.5.3", + "@smithy/middleware-endpoint": "^3.2.3", + "@smithy/middleware-stack": "^3.0.10", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", + "@smithy/util-stream": "^3.3.1", "tslib": "^2.6.2" }, "engines": { @@ -4541,7 +6313,7 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/types": { - "version": "3.3.0", + "version": "3.7.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -4552,12 +6324,12 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/url-parser": { - "version": "3.0.3", + "version": "3.0.10", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/querystring-parser": "^3.0.10", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" } }, @@ -4617,13 +6389,13 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.15", + "version": "3.0.27", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "@smithy/property-provider": "^3.1.10", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -4632,16 +6404,16 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.15", + "version": "3.0.27", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^3.0.5", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "@smithy/config-resolver": "^3.0.12", + "@smithy/credential-provider-imds": "^3.2.7", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/property-provider": "^3.1.10", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4649,12 +6421,12 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-endpoints": { - "version": "2.0.5", + "version": "2.1.6", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4673,11 +6445,11 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-middleware": { - "version": "3.0.3", + "version": "3.0.10", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4685,12 +6457,12 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-retry": { - "version": "3.0.3", + "version": "3.0.10", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/service-error-classification": "^3.0.10", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -4698,13 +6470,13 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-stream": { - "version": "3.1.3", + "version": "3.3.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/types": "^3.7.1", "@smithy/util-base64": "^3.0.0", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-hex-encoding": "^3.0.0", @@ -4715,6 +6487,18 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.7", + "@smithy/querystring-builder": "^3.0.10", + "@smithy/types": "^3.7.1", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/@smithy/util-uri-escape": { "version": "3.0.0", "inBundle": true, @@ -4821,17 +6605,17 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/graphql-mapping-template": { - "version": "5.0.1", + "version": "5.0.2", "inBundle": true, "license": "Apache-2.0" }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/graphql-transformer-common": { - "version": "5.0.1", + "version": "5.1.2", "inBundle": true, "license": "Apache-2.0", "dependencies": { "graphql": "^15.5.0", - "graphql-mapping-template": "5.0.1", + "graphql-mapping-template": "5.0.2", "md5": "^2.2.1", "pluralize": "8.0.0" } @@ -4943,7 +6727,7 @@ } }, "node_modules/@aws-amplify/graphql-api-construct/node_modules/tslib": { - "version": "2.7.0", + "version": "2.8.1", "inBundle": true, "license": "0BSD" }, @@ -31924,7 +33708,7 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", - "@aws-amplify/data-construct": "^1.10.1", + "@aws-amplify/data-construct": "^1.14.5", "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/graphql-generator": "^0.5.1", "@aws-amplify/plugin-types": "^1.6.0" diff --git a/packages/backend-ai/API.md b/packages/backend-ai/API.md index a5b3a31b1f4..9232acb5010 100644 --- a/packages/backend-ai/API.md +++ b/packages/backend-ai/API.md @@ -1,93 +1,93 @@ -## API Report File for "@aws-amplify/backend-ai" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import { AiModel } from '@aws-amplify/data-schema-types'; -import { ConstructFactory } from '@aws-amplify/plugin-types'; -import { ConversationTurnEventVersion } from '@aws-amplify/ai-constructs/conversation'; -import { FunctionResources } from '@aws-amplify/plugin-types'; -import { LogLevel } from '@aws-amplify/plugin-types'; -import { LogRetention } from '@aws-amplify/plugin-types'; -import { ResourceProvider } from '@aws-amplify/plugin-types'; -import * as runtime from '@aws-amplify/ai-constructs/conversation/runtime'; - -declare namespace __export__conversation { - export { - ConversationHandlerFunctionFactory, - ConversationHandlerFunctionLogLevel, - ConversationHandlerFunctionLogRetention, - ConversationHandlerFunctionLoggingOptions, - DefineConversationHandlerFunctionProps, - defineConversationHandlerFunction - } -} -export { __export__conversation } - -declare namespace __export__conversation__runtime { - export { - ToolResultContentBlock, - ExecutableTool, - ConversationTurnEvent, - handleConversationTurnEvent, - createExecutableTool - } -} -export { __export__conversation__runtime } - -// @public (undocumented) -type ConversationHandlerFunctionFactory = ConstructFactory> & { - readonly eventVersion: ConversationTurnEventVersion; -}; - -// @public (undocumented) -type ConversationHandlerFunctionLoggingOptions = { - retention?: ConversationHandlerFunctionLogRetention; - level?: ConversationHandlerFunctionLogLevel; -}; - -// @public (undocumented) -type ConversationHandlerFunctionLogLevel = LogLevel; - -// @public (undocumented) -type ConversationHandlerFunctionLogRetention = LogRetention; - -// @public (undocumented) -type ConversationTurnEvent = runtime.ConversationTurnEvent; - -// @public (undocumented) -const createExecutableTool: >(name: string, description: string, inputSchema: runtime.ToolInputSchema, handler: (input: TToolInput) => Promise) => ExecutableTool; - -// @public -const defineConversationHandlerFunction: (props: DefineConversationHandlerFunctionProps) => ConversationHandlerFunctionFactory; - -// @public (undocumented) -type DefineConversationHandlerFunctionProps = { - name: string; - entry?: string; - models: Array<{ - modelId: string | AiModel; - region?: string; - }>; - memoryMB?: number; - timeoutSeconds?: number; - logging?: ConversationHandlerFunctionLoggingOptions; -}; - -// @public (undocumented) -type ExecutableTool> = runtime.ToolDefinition & { - execute: (input: TToolInput) => Promise; -}; - -// @public (undocumented) -const handleConversationTurnEvent: (event: ConversationTurnEvent, props?: { - tools?: Array>; -}) => Promise; - -// @public (undocumented) -type ToolResultContentBlock = runtime.ToolResultContentBlock; - -// (No @packageDocumentation comment for this package) - -``` +## API Report File for "@aws-amplify/backend-ai" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { AiModel } from '@aws-amplify/data-schema-types'; +import { ConstructFactory } from '@aws-amplify/plugin-types'; +import { ConversationTurnEventVersion } from '@aws-amplify/ai-constructs/conversation'; +import { FunctionResources } from '@aws-amplify/plugin-types'; +import { LogLevel } from '@aws-amplify/plugin-types'; +import { LogRetention } from '@aws-amplify/plugin-types'; +import { ResourceProvider } from '@aws-amplify/plugin-types'; +import * as runtime from '@aws-amplify/ai-constructs/conversation/runtime'; + +declare namespace __export__conversation { + export { + ConversationHandlerFunctionFactory, + ConversationHandlerFunctionLogLevel, + ConversationHandlerFunctionLogRetention, + ConversationHandlerFunctionLoggingOptions, + DefineConversationHandlerFunctionProps, + defineConversationHandlerFunction + } +} +export { __export__conversation } + +declare namespace __export__conversation__runtime { + export { + ToolResultContentBlock, + ExecutableTool, + ConversationTurnEvent, + handleConversationTurnEvent, + createExecutableTool + } +} +export { __export__conversation__runtime } + +// @public (undocumented) +type ConversationHandlerFunctionFactory = ConstructFactory> & { + readonly eventVersion: ConversationTurnEventVersion; +}; + +// @public (undocumented) +type ConversationHandlerFunctionLoggingOptions = { + retention?: ConversationHandlerFunctionLogRetention; + level?: ConversationHandlerFunctionLogLevel; +}; + +// @public (undocumented) +type ConversationHandlerFunctionLogLevel = Extract; + +// @public (undocumented) +type ConversationHandlerFunctionLogRetention = LogRetention; + +// @public (undocumented) +type ConversationTurnEvent = runtime.ConversationTurnEvent; + +// @public (undocumented) +const createExecutableTool: >(name: string, description: string, inputSchema: runtime.ToolInputSchema, handler: (input: TToolInput) => Promise) => ExecutableTool; + +// @public +const defineConversationHandlerFunction: (props: DefineConversationHandlerFunctionProps) => ConversationHandlerFunctionFactory; + +// @public (undocumented) +type DefineConversationHandlerFunctionProps = { + name: string; + entry?: string; + models: Array<{ + modelId: string | AiModel; + region?: string; + }>; + memoryMB?: number; + timeoutSeconds?: number; + logging?: ConversationHandlerFunctionLoggingOptions; +}; + +// @public (undocumented) +type ExecutableTool> = runtime.ToolDefinition & { + execute: (input: TToolInput) => Promise; +}; + +// @public (undocumented) +const handleConversationTurnEvent: (event: ConversationTurnEvent, props?: { + tools?: Array>; +}) => Promise; + +// @public (undocumented) +type ToolResultContentBlock = runtime.ToolResultContentBlock; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/backend-ai/src/conversation/factory.ts b/packages/backend-ai/src/conversation/factory.ts index fb488a8ac51..04d3a5dc179 100644 --- a/packages/backend-ai/src/conversation/factory.ts +++ b/packages/backend-ai/src/conversation/factory.ts @@ -159,7 +159,10 @@ class DefaultConversationHandlerFunctionFactory }; } -export type ConversationHandlerFunctionLogLevel = LogLevel; +export type ConversationHandlerFunctionLogLevel = Extract< + LogLevel, + 'info' | 'debug' | 'warn' | 'error' | 'fatal' | 'trace' +>; export type ConversationHandlerFunctionLogRetention = LogRetention; diff --git a/packages/backend-data/API.md b/packages/backend-data/API.md index 56e54db5079..36beacbf8a8 100644 --- a/packages/backend-data/API.md +++ b/packages/backend-data/API.md @@ -9,6 +9,8 @@ import { AmplifyFunction } from '@aws-amplify/plugin-types'; import { ConstructFactory } from '@aws-amplify/plugin-types'; import { DerivedCombinedSchema } from '@aws-amplify/data-schema-types'; import { DerivedModelSchema } from '@aws-amplify/data-schema-types'; +import { LogLevel } from '@aws-amplify/plugin-types'; +import { LogRetention } from '@aws-amplify/plugin-types'; // @public export type ApiKeyAuthorizationModeProps = { @@ -24,12 +26,26 @@ export type AuthorizationModes = { oidcAuthorizationMode?: OIDCAuthorizationModeProps; }; +// @public +export type DataLogConfig = { + retention?: LogRetention; + excludeVerboseContent?: boolean; + fieldLogLevel?: DataLogLevel; +}; + +// @public +export type DataLoggingOptions = true | DataLogConfig; + +// @public (undocumented) +export type DataLogLevel = Extract; + // @public export type DataProps = { schema: DataSchemaInput; name?: string; authorizationModes?: AuthorizationModes; functions?: Record>; + logging?: DataLoggingOptions; }; // @public diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index e13edcf0be6..c2162f37108 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -30,7 +30,7 @@ "dependencies": { "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/data-construct": "^1.10.1", + "@aws-amplify/data-construct": "^1.14.5", "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/graphql-generator": "^0.5.1", "@aws-amplify/plugin-types": "^1.6.0" diff --git a/packages/backend-data/src/factory.test.ts b/packages/backend-data/src/factory.test.ts index 91e1fdeff6c..55031b1ed88 100644 --- a/packages/backend-data/src/factory.test.ts +++ b/packages/backend-data/src/factory.test.ts @@ -38,7 +38,10 @@ import { import { AmplifyDataResources } from '@aws-amplify/data-construct'; import { AmplifyUserError } from '@aws-amplify/platform-core'; import { a } from '@aws-amplify/data-schema'; -import { AmplifyDataError } from './types.js'; +import { AmplifyDataError, DataLoggingOptions } from './types.js'; +import { CDKLoggingOptions } from './logging_options_parser.js'; +import { CfnGraphQLApi, FieldLogLevel } from 'aws-cdk-lib/aws-appsync'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; const CUSTOM_DDB_CFN_TYPE = 'Custom::AmplifyDynamoDBTable'; @@ -802,6 +805,144 @@ void describe('Destructive Schema Updates & Replace tables upon GSI updates', () }); }); +void describe('Logging Options', () => { + let stack: Stack; + let constructContainer: ConstructContainer; + let outputStorageStrategy: BackendOutputStorageStrategy; + let importPathVerifier: ImportPathVerifierStub; + let resourceNameValidator: ResourceNameValidatorStub; + let getInstanceProps: ConstructFactoryGetInstanceProps; + + const DEFAULT_LOGGING_OPTIONS: CDKLoggingOptions = { + excludeVerboseContent: true, + fieldLogLevel: FieldLogLevel.NONE, + retention: RetentionDays.ONE_WEEK, + }; + + const testCases: { + description: string; + input: DataLoggingOptions | undefined; + expectedOutput: CDKLoggingOptions | undefined; + }[] = [ + { + description: 'no logging options provided', + input: undefined, + expectedOutput: undefined, + }, + { + description: 'default - logging: true', + input: true, + expectedOutput: DEFAULT_LOGGING_OPTIONS, + }, + { + description: 'default - logging: {}', + input: {}, + expectedOutput: DEFAULT_LOGGING_OPTIONS, + }, + { + description: 'custom - excludeVerboseContent: false', + input: { excludeVerboseContent: false }, + expectedOutput: { + ...DEFAULT_LOGGING_OPTIONS, + excludeVerboseContent: false, + }, + }, + { + description: 'custom - fieldLogLevel: error', + input: { fieldLogLevel: 'error' }, + expectedOutput: { + ...DEFAULT_LOGGING_OPTIONS, + fieldLogLevel: FieldLogLevel.ERROR, + }, + }, + { + description: 'custom - fieldLogLevel: info, retention: 1 month', + input: { fieldLogLevel: 'info', retention: '1 month' }, + expectedOutput: { + ...DEFAULT_LOGGING_OPTIONS, + fieldLogLevel: FieldLogLevel.INFO, + retention: RetentionDays.ONE_MONTH, + }, + }, + { + description: + 'custom - excludeVerboseContent: false, level: debug, retention: 13 months', + input: { + excludeVerboseContent: false, + fieldLogLevel: 'debug', + retention: '13 months', + }, + expectedOutput: { + excludeVerboseContent: false, + fieldLogLevel: FieldLogLevel.DEBUG, + retention: RetentionDays.THIRTEEN_MONTHS, + }, + }, + ]; + + beforeEach(() => { + resetFactoryCount(); + stack = createStackAndSetContext({ isSandboxMode: true }); + + constructContainer = + createConstructContainerWithUserPoolAuthRegistered(stack); + outputStorageStrategy = new StackMetadataBackendOutputStorageStrategy( + stack + ); + importPathVerifier = new ImportPathVerifierStub(); + resourceNameValidator = new ResourceNameValidatorStub(); + + getInstanceProps = { + constructContainer, + outputStorageStrategy, + importPathVerifier, + resourceNameValidator, + }; + }); + + testCases.forEach((testCase) => { + void it(`${testCase.description}`, () => { + const dataFactory = defineData({ + schema: testSchema, + name: 'testLoggingOptions', + logging: testCase.input, + }); + const dataConstruct = dataFactory.getInstance(getInstanceProps); + const template = Template.fromStack( + Stack.of(dataConstruct.resources.graphqlApi) + ); + + if (testCase.expectedOutput) { + const createdLogConfig = dataConstruct.resources.cfnResources + .cfnGraphqlApi.logConfig as CfnGraphQLApi.LogConfigProperty; + assert.ok(createdLogConfig, 'logConfig should be defined'); + assert.strictEqual( + createdLogConfig.fieldLogLevel, + testCase.expectedOutput.fieldLogLevel + ); + assert.strictEqual( + createdLogConfig.excludeVerboseContent, + testCase.expectedOutput.excludeVerboseContent + ); + + template.hasResourceProperties('Custom::LogRetention', { + RetentionInDays: testCase.expectedOutput.retention, + }); + } else { + const createdLogConfig = dataConstruct.resources.cfnResources + .cfnGraphqlApi.logConfig as CfnGraphQLApi.LogConfigProperty; + assert.strictEqual(createdLogConfig, undefined); + + template.resourcePropertiesCountIs( + 'Custom::LogRetention', + 'LogRetention', + 0 + ); + } + }); + }); +}); + const resetFactoryCount = () => { DataFactory.factoryCount = 0; }; diff --git a/packages/backend-data/src/factory.ts b/packages/backend-data/src/factory.ts index af2cfd97891..ba8efa6d233 100644 --- a/packages/backend-data/src/factory.ts +++ b/packages/backend-data/src/factory.ts @@ -50,7 +50,7 @@ import { } from '@aws-amplify/data-schema-types'; import { Bucket } from 'aws-cdk-lib/aws-s3'; import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment'; - +import { convertLoggingOptionsToCDK } from './logging_options_parser.js'; const modelIntrospectionSchemaKey = 'modelIntrospectionSchema.json'; const defaultName = 'amplifyData'; @@ -244,6 +244,10 @@ class DataGenerator implements ConstructContainerEntryGenerator { const isSandboxDeployment = scope.node.tryGetContext(CDKContextKey.DEPLOYMENT_TYPE) === 'sandbox'; + const cdkLoggingOptions = convertLoggingOptionsToCDK( + this.props.logging ?? undefined + ); + try { const combinedSchema = combineCDKSchemas(amplifyGraphqlDefinitions); modelIntrospectionSchema = generateModelsSync({ @@ -266,6 +270,7 @@ class DataGenerator implements ConstructContainerEntryGenerator { allowDestructiveGraphqlSchemaUpdates: true, _provisionHotswapFriendlyResources: isSandboxDeployment, }, + logging: cdkLoggingOptions, }); } catch (error) { throw new AmplifyUserError( diff --git a/packages/backend-data/src/index.ts b/packages/backend-data/src/index.ts index b882c0c7126..7929c6a3da6 100644 --- a/packages/backend-data/src/index.ts +++ b/packages/backend-data/src/index.ts @@ -7,4 +7,7 @@ export { AuthorizationModes, DataSchemaInput, DataProps, + DataLogConfig, + DataLoggingOptions, + DataLogLevel, } from './types.js'; diff --git a/packages/backend-data/src/logging_options_parser.test.ts b/packages/backend-data/src/logging_options_parser.test.ts new file mode 100644 index 00000000000..11290965257 --- /dev/null +++ b/packages/backend-data/src/logging_options_parser.test.ts @@ -0,0 +1,87 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert'; +import { DataLoggingOptions } from './types.js'; +import { + CDKLoggingOptions, + convertLoggingOptionsToCDK, +} from './logging_options_parser.js'; +import { FieldLogLevel } from 'aws-cdk-lib/aws-appsync'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; + +type TestCase = { + description: string; + input: DataLoggingOptions | undefined; + expectedOutput: CDKLoggingOptions | undefined; +}; + +const DEFAULT_LOGGING_OPTIONS = { + excludeVerboseContent: true, + fieldLogLevel: FieldLogLevel.NONE, + retention: RetentionDays.ONE_WEEK, +}; + +const testCases: Array = [ + { + description: 'no logging options provided', + input: undefined, + expectedOutput: undefined, + }, + { + description: 'default - logging: true', + input: true, + expectedOutput: DEFAULT_LOGGING_OPTIONS, + }, + { + description: 'default - logging: {}', + input: {}, + expectedOutput: DEFAULT_LOGGING_OPTIONS, + }, + { + description: 'custom - excludeVerboseContent: false', + input: { excludeVerboseContent: false }, + expectedOutput: { + ...DEFAULT_LOGGING_OPTIONS, + excludeVerboseContent: false, + }, + }, + { + description: 'custom - fieldLogLevel: error', + input: { fieldLogLevel: 'error' }, + expectedOutput: { + ...DEFAULT_LOGGING_OPTIONS, + fieldLogLevel: FieldLogLevel.ERROR, + }, + }, + { + description: 'custom - fieldLogLevel: info, retention: 1 month', + input: { fieldLogLevel: 'info', retention: '1 month' }, + expectedOutput: { + ...DEFAULT_LOGGING_OPTIONS, + fieldLogLevel: FieldLogLevel.INFO, + retention: RetentionDays.ONE_MONTH, + }, + }, + { + description: + 'custom - excludeVerboseContent: false, level: debug, retention: 13 months', + input: { + excludeVerboseContent: false, + fieldLogLevel: 'debug', + retention: '13 months', + }, + expectedOutput: { + excludeVerboseContent: false, + fieldLogLevel: FieldLogLevel.DEBUG, + retention: RetentionDays.THIRTEEN_MONTHS, + }, + }, +]; + +void describe('LoggingOptions converter', () => { + testCases.forEach((testCase) => { + void it(`${testCase.description}`, () => { + const convertedOptions = convertLoggingOptionsToCDK(testCase.input); + assert.deepStrictEqual(convertedOptions, testCase.expectedOutput); + }); + }); +}); diff --git a/packages/backend-data/src/logging_options_parser.ts b/packages/backend-data/src/logging_options_parser.ts new file mode 100644 index 00000000000..ce0cd995d69 --- /dev/null +++ b/packages/backend-data/src/logging_options_parser.ts @@ -0,0 +1,65 @@ +import { DataLogLevel, DataLoggingOptions } from './types.js'; +import { + LogLevelConverter, + LogRetentionConverter, +} from '@aws-amplify/platform-core/cdk'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; +import { FieldLogLevel } from 'aws-cdk-lib/aws-appsync'; +import { LogRetention } from '@aws-amplify/plugin-types'; + +export type CDKLoggingOptions = { + excludeVerboseContent: boolean; + fieldLogLevel: FieldLogLevel; + retention: RetentionDays; +}; + +const DEFAULT_EXCLUDE_VERBOSE_CONTENT: boolean = true; +const DEFAULT_LEVEL: DataLogLevel = 'none'; +const DEFAULT_RETENTION: LogRetention = '1 week'; + +/** + * Converts logging options to CDK. + */ +export const convertLoggingOptionsToCDK = ( + loggingOptions: DataLoggingOptions | undefined +): CDKLoggingOptions | undefined => { + if (!loggingOptions) { + return undefined; + } + + // Determine if we should apply default configuration + const shouldApplyDefaultLogging = + loggingOptions === true || + (typeof loggingOptions === 'object' && + Object.keys(loggingOptions).length === 0); + + // Extract fields from the user's loggingOptions (if it's an object) + const config: DataLoggingOptions = + typeof loggingOptions === 'object' ? loggingOptions : {}; + + const excludeVerboseContent = shouldApplyDefaultLogging + ? DEFAULT_EXCLUDE_VERBOSE_CONTENT + : config.excludeVerboseContent ?? DEFAULT_EXCLUDE_VERBOSE_CONTENT; + + // For level and retention, we rely on converters. If config is empty or logging is true, use defaults. + const dataLogLevel = shouldApplyDefaultLogging + ? DEFAULT_LEVEL + : config.fieldLogLevel ?? DEFAULT_LEVEL; + + const logRetention = shouldApplyDefaultLogging + ? DEFAULT_RETENTION + : config.retention ?? DEFAULT_RETENTION; + + const fieldLogLevel = new LogLevelConverter().toCDKAppsyncFieldLogLevel( + dataLogLevel + )!; + const retention = new LogRetentionConverter().toCDKRetentionDays( + logRetention + )!; + + return { + excludeVerboseContent, + fieldLogLevel, + retention, + }; +}; diff --git a/packages/backend-data/src/types.ts b/packages/backend-data/src/types.ts index d3ecdf884cb..794a125a437 100644 --- a/packages/backend-data/src/types.ts +++ b/packages/backend-data/src/types.ts @@ -2,7 +2,12 @@ import { DerivedCombinedSchema, DerivedModelSchema, } from '@aws-amplify/data-schema-types'; -import { AmplifyFunction, ConstructFactory } from '@aws-amplify/plugin-types'; +import { + AmplifyFunction, + ConstructFactory, + LogLevel, + LogRetention, +} from '@aws-amplify/plugin-types'; /** * Authorization modes used in by client side Amplify represented in camelCase. @@ -139,6 +144,11 @@ export type DataProps = { * Functions invokable by the API. The specific input type of the function is subject to change or removal. */ functions?: Record>; + + /** + * Logging configuration for the API. + */ + logging?: DataLoggingOptions; }; export type AmplifyDataError = @@ -147,3 +157,83 @@ export type AmplifyDataError = | 'InvalidSchemaError' | 'MultipleSingletonResourcesError' | 'UnresolvedEntryPathError'; + +/** + * The logging configuration when writing GraphQL operations and tracing to Amazon CloudWatch for an AWS AppSync GraphQL API. + * Values can be `true` or a `DataLogConfig` object. + * + * ### Defaults + * Default settings will be applied when logging is set to `true` or an empty object, or for unspecified fields: + * - `excludeVerboseContent`: `true` + * - `fieldLogLevel`: `none` + * - `retention`: `1 week` + * + * **WARNING**: Verbose logging will log the full incoming query including user parameters. + * Sensitive information may be exposed in CloudWatch logs. Ensure that your IAM policies only grant access to authorized users. + * + * For information on AppSync's LogConfig, refer to https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appsync-graphqlapi-logconfig.html. + */ +export type DataLoggingOptions = true | DataLogConfig; + +/** + * Customizable logging configuration when writing GraphQL operations and tracing to Amazon CloudWatch for an AWS AppSync GraphQL API. + * + * **WARNING**: Verbose logging will log the full incoming query including user parameters. + * Sensitive information may be exposed in CloudWatch logs. Ensure that your IAM policies only grant access to authorized users. + * + * For information on AppSync's LogConfig, refer to https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appsync-graphqlapi-logconfig.html. + * For information on RetentionDays, refer to https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_logs.RetentionDays.html. + * @default excludeVerboseContent: true, fieldLogLevel: 'none', retention: '1 week' + */ +export type DataLogConfig = { + /** + * The number of days log events are kept in CloudWatch Logs. + * @default RetentionDays.ONE_WEEK + * @see https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_logs.RetentionDays.html + */ + retention?: LogRetention; + + /** + * When set to `true`, excludes verbose information from the logs, such as: + * - GraphQL Query + * - Request Headers + * - Response Headers + * - Context + * - Evaluated Mapping Templates + * + * This setting applies regardless of the specified logging level. + * + * **WARNING**: Verbose logging will log the full incoming query including user parameters. + * Sensitive information may be exposed in CloudWatch logs. Ensure that your IAM policies only grant access to authorized users. + * @default true + */ + excludeVerboseContent?: boolean; + + /** + * The field logging level. Values can be `'none'`, `'error'`, `'info'`, `'debug'`, or `'all'`. + * + * - **'none'**: No field-level logs are captured. + * - **'error'**: Logs the following information only for the fields that are in the error category: + * - The error section in the server response. + * - Field-level errors. + * - The generated request/response functions that got resolved for error fields. + * - **'info'**: Logs the following information only for the fields that are in the info and error categories: + * - Info-level messages. + * - The user messages sent through `$util.log.info` and `console.log`. + * - Field-level tracing and mapping logs are not shown. + * - **'debug'**: Logs the following information only for the fields that are in the debug, info, and error categories: + * - Debug-level messages. + * - The user messages sent through `$util.log.info`, `$util.log.debug`, `console.log`, and `console.debug`. + * - Field-level tracing and mapping logs are not shown. + * - **'all'**: The following information is logged for all fields in the query: + * - Field-level tracing information. + * - The generated request/response functions that were resolved for each field. + * @default 'none' + */ + fieldLogLevel?: DataLogLevel; +}; + +export type DataLogLevel = Extract< + LogLevel, + 'none' | 'all' | 'info' | 'debug' | 'error' +>; diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index 1a424e3ad84..522b29166bd 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -83,7 +83,7 @@ export type FunctionLoggingOptions = ({ }; // @public (undocumented) -export type FunctionLogLevel = LogLevel; +export type FunctionLogLevel = Extract; // @public (undocumented) export type FunctionLogRetention = LogRetention; diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index f6df34cd8c1..66ea558704b 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -68,7 +68,10 @@ export type TimeInterval = | `every year`; export type FunctionSchedule = TimeInterval | CronSchedule; -export type FunctionLogLevel = LogLevel; +export type FunctionLogLevel = Extract< + LogLevel, + 'info' | 'debug' | 'warn' | 'error' | 'fatal' | 'trace' +>; export type FunctionLogRetention = LogRetention; /** diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index 50d40bce8e1..06891c5af75 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -8,6 +8,7 @@ import { AppId } from '@aws-amplify/plugin-types'; import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; import { DeepPartialAmplifyGeneratedConfigs } from '@aws-amplify/plugin-types'; +import { FieldLogLevel } from 'aws-cdk-lib/aws-appsync'; import { LogLevel } from '@aws-amplify/plugin-types'; import { LogRetention } from '@aws-amplify/plugin-types'; import { RetentionDays } from 'aws-cdk-lib/aws-logs'; @@ -135,6 +136,8 @@ export type LocalConfigurationFileName = 'usage_data_preferences.json'; // @public class LogLevelConverter { + // (undocumented) + toCDKAppsyncFieldLogLevel: (logLevel: LogLevel | undefined) => FieldLogLevel | undefined; // (undocumented) toCDKLambdaApplicationLogLevel: (logLevel: LogLevel | undefined) => ApplicationLogLevel | undefined; } diff --git a/packages/platform-core/src/cdk/enum_converters.test.ts b/packages/platform-core/src/cdk/enum_converters.test.ts index 19edc0d1441..bc260d28da8 100644 --- a/packages/platform-core/src/cdk/enum_converters.test.ts +++ b/packages/platform-core/src/cdk/enum_converters.test.ts @@ -4,6 +4,7 @@ import { LogLevel, LogRetention } from '@aws-amplify/plugin-types'; import { RetentionDays } from 'aws-cdk-lib/aws-logs'; import { LogLevelConverter, LogRetentionConverter } from './enum_converters'; import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda'; +import { FieldLogLevel } from 'aws-cdk-lib/aws-appsync'; type TestCase = { input: TSource | undefined; @@ -120,7 +121,7 @@ void describe('LogRetentionConverter', () => { }); }); -void describe('LogLevelConverter', () => { +void describe('Lambda ApplicationLogLevelConverter', () => { const testCases: Array> = [ { input: undefined, @@ -160,3 +161,41 @@ void describe('LogLevelConverter', () => { }); }); }); + +void describe('Appsync FieldLogLevelConverter', () => { + const testCases: Array> = [ + { + input: undefined, + expectedOutput: undefined, + }, + { + input: 'none', + expectedOutput: FieldLogLevel.NONE, + }, + { + input: 'error', + expectedOutput: FieldLogLevel.ERROR, + }, + { + input: 'info', + expectedOutput: FieldLogLevel.INFO, + }, + { + input: 'debug', + expectedOutput: FieldLogLevel.DEBUG, + }, + { + input: 'all', + expectedOutput: FieldLogLevel.ALL, + }, + ]; + + testCases.forEach((testCase, index) => { + void it(`converts data log level[${index}]`, () => { + const convertedValue = new LogLevelConverter().toCDKAppsyncFieldLogLevel( + testCase.input + ); + assert.strictEqual(convertedValue, testCase.expectedOutput); + }); + }); +}); diff --git a/packages/platform-core/src/cdk/enum_converters.ts b/packages/platform-core/src/cdk/enum_converters.ts index 3f467785a0c..96f9652f5b7 100644 --- a/packages/platform-core/src/cdk/enum_converters.ts +++ b/packages/platform-core/src/cdk/enum_converters.ts @@ -1,6 +1,7 @@ import { LogLevel, LogRetention } from '@aws-amplify/plugin-types'; import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda'; import { RetentionDays } from 'aws-cdk-lib/aws-logs'; +import { FieldLogLevel } from 'aws-cdk-lib/aws-appsync'; /** * Converts LogRetention to CDK types. @@ -92,6 +93,29 @@ export class LogLevelConverter { case 'trace': { return ApplicationLogLevel.TRACE; } + default: + throw new Error(`Invalid Lambda application log level: ${logLevel}`); + } + }; + + toCDKAppsyncFieldLogLevel = ( + logLevel: LogLevel | undefined + ): FieldLogLevel | undefined => { + switch (logLevel) { + case undefined: + return undefined; + case 'none': + return FieldLogLevel.NONE; + case 'error': + return FieldLogLevel.ERROR; + case 'info': + return FieldLogLevel.INFO; + case 'debug': + return FieldLogLevel.DEBUG; + case 'all': + return FieldLogLevel.ALL; + default: + throw new Error(`Invalid Appsync field log level: ${logLevel}`); } }; } diff --git a/packages/plugin-types/API.md b/packages/plugin-types/API.md index ab33f98e346..181acf42420 100644 --- a/packages/plugin-types/API.md +++ b/packages/plugin-types/API.md @@ -197,7 +197,7 @@ export type ImportPathVerifier = { }; // @public (undocumented) -export type LogLevel = 'info' | 'debug' | 'warn' | 'error' | 'fatal' | 'trace'; +export type LogLevel = 'all' | 'debug' | 'error' | 'fatal' | 'info' | 'none' | 'trace' | 'warn'; // @public (undocumented) export type LogRetention = '1 day' | '3 days' | '5 days' | '1 week' | '2 weeks' | '1 month' | '2 months' | '3 months' | '4 months' | '5 months' | '6 months' | '1 year' | '13 months' | '18 months' | '2 years' | '3 years' | '5 years' | '6 years' | '7 years' | '8 years' | '9 years' | '10 years' | 'infinite'; diff --git a/packages/plugin-types/src/log_level.ts b/packages/plugin-types/src/log_level.ts index 5f4d23c0087..398bfb9be95 100644 --- a/packages/plugin-types/src/log_level.ts +++ b/packages/plugin-types/src/log_level.ts @@ -1 +1,9 @@ -export type LogLevel = 'info' | 'debug' | 'warn' | 'error' | 'fatal' | 'trace'; +export type LogLevel = + | 'all' + | 'debug' + | 'error' + | 'fatal' + | 'info' + | 'none' + | 'trace' + | 'warn'; From f3626c6079dec3fb704752aa196245224bd2100e Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Thu, 2 Jan 2025 15:04:43 -0800 Subject: [PATCH 186/199] fix dependabot version update workflow (#2394) * fix dependabot version update workflow * ignore spellcheck for sha --- .github/workflows/health_checks.yml | 5 +++-- .../dependabot_version_update_handler.test.ts | 12 ++++++++---- .../components/dependabot_version_update_handler.ts | 6 +++--- scripts/dependabot_handle_version_update.ts | 10 +++------- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/health_checks.yml b/.github/workflows/health_checks.yml index bf2d63289bf..c461f282b7f 100644 --- a/.github/workflows/health_checks.yml +++ b/.github/workflows/health_checks.yml @@ -277,6 +277,8 @@ jobs: - resolve_inputs steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 + with: + fetch-depth: 0 - uses: ./.github/actions/setup_node with: node-version: 18 @@ -285,10 +287,9 @@ jobs: node-version: 18 cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }} - name: Handle Dependabot version update pull request - run: npx tsx scripts/dependabot_handle_version_update.ts "$BASE_SHA" "$HEAD_SHA" + run: npx tsx scripts/dependabot_handle_version_update.ts "$BASE_SHA" env: BASE_SHA: ${{ github.event.pull_request.base.sha }} - HEAD_SHA: ${{ github.event.pull_request.head.sha }} # The dependabot_handler_version_update script needs to add the 'run-e2e' pull request label GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} do_include_e2e: diff --git a/scripts/components/dependabot_version_update_handler.test.ts b/scripts/components/dependabot_version_update_handler.test.ts index 8d9b612f1e2..929d00d95b1 100644 --- a/scripts/components/dependabot_version_update_handler.test.ts +++ b/scripts/components/dependabot_version_update_handler.test.ts @@ -117,6 +117,11 @@ void describe('dependabot version update handler', async () => { pull_request: { number: 1, body: pullRequestBody, + head: { + ref: 'dependabot/test_version_update_branch', + // eslint-disable-next-line spellcheck/spell-checker + sha: 'abcd1234', // used for naming the changeset file + }, }, }, issue: { @@ -131,15 +136,13 @@ void describe('dependabot version update handler', async () => { }; // Update package.json files for both packages and commit to match what Dependabot will do for a version update PR - await gitClient.switchToBranch('dependabot/test_update'); + await gitClient.switchToBranch('dependabot/test_version_update_branch'); await setPackageDependencies(cantaloupePackagePath, { testDep: '^1.1.0' }); await setPackageDependencies(platypusPackagePath, { testDep: '^1.1.0' }); await gitClient.commitAllChanges('Bump dependencies'); - const headRef = await gitClient.getHashForCurrentCommit(); const dependabotVersionUpdateHandler = new DependabotVersionUpdateHandler( baseRef, - headRef, gitClient, githubClient, testWorkingDir, @@ -150,7 +153,8 @@ void describe('dependabot version update handler', async () => { const changesetFilePath = path.join( testWorkingDir, - `.changeset/dependabot-${headRef}.md` + // eslint-disable-next-line spellcheck/spell-checker + '.changeset/dependabot-abcd1234.md' ); await assertChangesetFile( diff --git a/scripts/components/dependabot_version_update_handler.ts b/scripts/components/dependabot_version_update_handler.ts index 6e8739c8054..08dd3477246 100644 --- a/scripts/components/dependabot_version_update_handler.ts +++ b/scripts/components/dependabot_version_update_handler.ts @@ -16,7 +16,6 @@ export class DependabotVersionUpdateHandler { */ constructor( private readonly baseRef: string, - private readonly headRef: string, private readonly gitClient: GitClient, private readonly ghClient: GithubClient, private readonly _rootDir: string = process.cwd(), @@ -40,7 +39,8 @@ export class DependabotVersionUpdateHandler { return; } - const branch = await this.gitClient.getCurrentBranch(); + const branch = this._ghContext.payload.pull_request.head.ref; + await this.gitClient.switchToBranch(branch); if (!branch.startsWith('dependabot/')) { // if branch is not a dependabot branch, return early return; @@ -78,7 +78,7 @@ export class DependabotVersionUpdateHandler { // Create and commit the changeset file, then add the 'run-e2e' label and force push to the PR const fileName = path.join( this._rootDir, - `.changeset/dependabot-${this.headRef}.md` + `.changeset/dependabot-${this._ghContext.payload.pull_request.head.sha}.md` ); const versionUpdates = await this.getVersionUpdates(); await this.createChangesetFile(fileName, versionUpdates, packageNames); diff --git a/scripts/dependabot_handle_version_update.ts b/scripts/dependabot_handle_version_update.ts index da9c92de1b9..c24012d51af 100644 --- a/scripts/dependabot_handle_version_update.ts +++ b/scripts/dependabot_handle_version_update.ts @@ -4,17 +4,13 @@ import { DependabotVersionUpdateHandler } from './components/dependabot_version_ const baseRef = process.argv[2]; if (baseRef === undefined) { - throw new Error('No base ref specified for generate changeset check'); -} - -const headRef = process.argv[3]; -if (headRef === undefined) { - throw new Error('No head ref specified for generate changeset check'); + throw new Error( + 'No base ref specified for handle dependabot version update check' + ); } const dependabotVersionUpdateHandler = new DependabotVersionUpdateHandler( baseRef, - headRef, new GitClient(), new GithubClient() ); From 8322e7278c375d3521f0b4bf6ebe38850628fb70 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 16:01:13 +0000 Subject: [PATCH 187/199] Version Packages (#2367) Co-authored-by: github-actions[bot] --- .changeset/blue-meals-kneel.md | 2 -- .changeset/chilly-pillows-live.md | 8 -------- .changeset/hungry-dogs-juggle.md | 2 -- .changeset/new-maps-lay.md | 5 ----- .changeset/quick-dingos-whisper.md | 10 ---------- .changeset/short-schools-act.md | 5 ----- .changeset/stale-pianos-marry.md | 2 -- .changeset/tall-parrots-sort.md | 6 ------ packages/backend-ai/CHANGELOG.md | 13 +++++++++++++ packages/backend-ai/package.json | 6 +++--- packages/backend-data/CHANGELOG.md | 11 +++++++++++ packages/backend-data/package.json | 6 +++--- packages/backend-deployer/CHANGELOG.md | 10 ++++++++++ packages/backend-deployer/package.json | 6 +++--- packages/backend-function/CHANGELOG.md | 12 ++++++++++++ packages/backend-function/package.json | 6 +++--- packages/backend/CHANGELOG.md | 19 +++++++++++++++++++ packages/backend/package.json | 12 ++++++------ packages/cli/CHANGELOG.md | 16 ++++++++++++++++ packages/cli/package.json | 14 +++++++------- packages/client-config/CHANGELOG.md | 12 ++++++++++++ packages/client-config/package.json | 8 ++++---- packages/model-generator/CHANGELOG.md | 10 ++++++++++ packages/model-generator/package.json | 6 +++--- packages/platform-core/CHANGELOG.md | 12 ++++++++++++ packages/platform-core/package.json | 4 ++-- packages/plugin-types/CHANGELOG.md | 6 ++++++ packages/plugin-types/package.json | 2 +- packages/sandbox/CHANGELOG.md | 14 ++++++++++++++ packages/sandbox/package.json | 10 +++++----- 30 files changed, 175 insertions(+), 80 deletions(-) delete mode 100644 .changeset/blue-meals-kneel.md delete mode 100644 .changeset/chilly-pillows-live.md delete mode 100644 .changeset/hungry-dogs-juggle.md delete mode 100644 .changeset/new-maps-lay.md delete mode 100644 .changeset/quick-dingos-whisper.md delete mode 100644 .changeset/short-schools-act.md delete mode 100644 .changeset/stale-pianos-marry.md delete mode 100644 .changeset/tall-parrots-sort.md diff --git a/.changeset/blue-meals-kneel.md b/.changeset/blue-meals-kneel.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/blue-meals-kneel.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/chilly-pillows-live.md b/.changeset/chilly-pillows-live.md deleted file mode 100644 index da53db4b3b7..00000000000 --- a/.changeset/chilly-pillows-live.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@aws-amplify/model-generator': patch -'@aws-amplify/client-config': patch -'@aws-amplify/sandbox': patch -'@aws-amplify/backend-cli': patch ---- - -wraps no outputs found error from backend output client diff --git a/.changeset/hungry-dogs-juggle.md b/.changeset/hungry-dogs-juggle.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/hungry-dogs-juggle.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/new-maps-lay.md b/.changeset/new-maps-lay.md deleted file mode 100644 index e39a85c9457..00000000000 --- a/.changeset/new-maps-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -catch and wrap deployment in progress when deleting the backend diff --git a/.changeset/quick-dingos-whisper.md b/.changeset/quick-dingos-whisper.md deleted file mode 100644 index 3e4c117eda2..00000000000 --- a/.changeset/quick-dingos-whisper.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@aws-amplify/backend-function': minor -'@aws-amplify/platform-core': minor -'@aws-amplify/backend-data': minor -'@aws-amplify/plugin-types': minor -'@aws-amplify/backend-ai': minor -'@aws-amplify/backend': minor ---- - -added data logging api to defineData diff --git a/.changeset/short-schools-act.md b/.changeset/short-schools-act.md deleted file mode 100644 index da6857ad9e2..00000000000 --- a/.changeset/short-schools-act.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/platform-core': patch ---- - -add InsufficientMemorySpaceError wrapping diff --git a/.changeset/stale-pianos-marry.md b/.changeset/stale-pianos-marry.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/stale-pianos-marry.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/tall-parrots-sort.md b/.changeset/tall-parrots-sort.md deleted file mode 100644 index c14c30fd69c..00000000000 --- a/.changeset/tall-parrots-sort.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/backend-function': minor -'@aws-amplify/backend': minor ---- - -adds support for architecture property on defineFunction diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index a46001a7e52..b9f5e19f530 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/backend-ai +## 1.3.0 + +### Minor Changes + +- 23f1240: added data logging api to defineData + +### Patch Changes + +- Updated dependencies [23f1240] +- Updated dependencies [abff5a0] + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 8e00334af05..281755a8112 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "1.2.0", + "version": "1.3.0", "type": "module", "publishConfig": { "access": "public" @@ -26,8 +26,8 @@ "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/data-schema-types": "^1.2.0", - "@aws-amplify/platform-core": "^1.3.0", - "@aws-amplify/plugin-types": "^1.6.0" + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", diff --git a/packages/backend-data/CHANGELOG.md b/packages/backend-data/CHANGELOG.md index 6acf615618d..3cdafdd4109 100644 --- a/packages/backend-data/CHANGELOG.md +++ b/packages/backend-data/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend-data +## 1.4.0 + +### Minor Changes + +- 23f1240: added data logging api to defineData + +### Patch Changes + +- Updated dependencies [23f1240] + - @aws-amplify/plugin-types@1.7.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index c2162f37108..1e83462ba51 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-data", - "version": "1.3.0", + "version": "1.4.0", "type": "module", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "devDependencies": { "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/backend-platform-test-stubs": "^0.3.7", - "@aws-amplify/platform-core": "^1.4.0" + "@aws-amplify/platform-core": "^1.5.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", @@ -33,6 +33,6 @@ "@aws-amplify/data-construct": "^1.14.5", "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/graphql-generator": "^0.5.1", - "@aws-amplify/plugin-types": "^1.6.0" + "@aws-amplify/plugin-types": "^1.7.0" } } diff --git a/packages/backend-deployer/CHANGELOG.md b/packages/backend-deployer/CHANGELOG.md index 75ad01f825d..8cf0c3d4d3f 100644 --- a/packages/backend-deployer/CHANGELOG.md +++ b/packages/backend-deployer/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/backend-deployer +## 1.1.13 + +### Patch Changes + +- 642b441: catch and wrap deployment in progress when deleting the backend +- Updated dependencies [23f1240] +- Updated dependencies [abff5a0] + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.1.12 ### Patch Changes diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index 93082d2ecc6..b3648344362 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-deployer", - "version": "1.1.12", + "version": "1.1.13", "type": "module", "publishConfig": { "access": "public" @@ -19,8 +19,8 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "execa": "^9.5.1", "tsx": "^4.6.1", "strip-ansi": "^6.0.1" diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index 31c9a145496..a557263de6d 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/backend-function +## 1.11.0 + +### Minor Changes + +- 23f1240: added data logging api to defineData +- b1c0f0d: adds support for architecture property on defineFunction + +### Patch Changes + +- Updated dependencies [23f1240] + - @aws-amplify/plugin-types@1.7.0 + ## 1.10.0 ### Minor Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index 6cb3d38b934..a8dcf94e8fc 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.10.0", + "version": "1.11.0", "type": "module", "publishConfig": { "access": "public" @@ -26,13 +26,13 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-s3": "^3.624.0", "execa": "^9.5.1" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.7", - "@aws-amplify/platform-core": "^1.4.0", + "@aws-amplify/platform-core": "^1.5.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index cf0f756dd50..bb97d98b901 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,24 @@ # @aws-amplify/backend +## 1.12.0 + +### Minor Changes + +- 23f1240: added data logging api to defineData +- b1c0f0d: adds support for architecture property on defineFunction + +### Patch Changes + +- Updated dependencies [aaeda9b] +- Updated dependencies [23f1240] +- Updated dependencies [abff5a0] +- Updated dependencies [b1c0f0d] + - @aws-amplify/client-config@1.5.5 + - @aws-amplify/backend-function@1.11.0 + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/backend-data@1.4.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.11.0 ### Minor Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 834966d38a1..5190db01efb 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.11.0", + "version": "1.12.0", "type": "module", "publishConfig": { "access": "public" @@ -32,15 +32,15 @@ "dependencies": { "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/backend-auth": "^1.4.2", - "@aws-amplify/backend-function": "^1.10.0", - "@aws-amplify/backend-data": "^1.3.0", + "@aws-amplify/backend-function": "^1.11.0", + "@aws-amplify/backend-data": "^1.4.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.4", - "@aws-amplify/client-config": "^1.5.4", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/client-config": "^1.5.5", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-amplify": "^3.624.0" }, "peerDependencies": { diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index f90b68b64d3..c35ae7ab33c 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,21 @@ # @aws-amplify/backend-cli +## 1.4.6 + +### Patch Changes + +- aaeda9b: wraps no outputs found error from backend output client +- Updated dependencies [aaeda9b] +- Updated dependencies [642b441] +- Updated dependencies [23f1240] +- Updated dependencies [abff5a0] + - @aws-amplify/model-generator@1.0.12 + - @aws-amplify/client-config@1.5.5 + - @aws-amplify/sandbox@1.2.9 + - @aws-amplify/backend-deployer@1.1.13 + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.4.5 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 72122edab6d..589cd58450d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-cli", - "version": "1.4.5", + "version": "1.4.6", "description": "Command line interface for various Amplify tools", "bin": { "ampx": "lib/ampx.js", @@ -31,17 +31,17 @@ }, "homepage": "https://github.com/aws-amplify/amplify-backend#readme", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.12", + "@aws-amplify/backend-deployer": "^1.1.13", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.1", - "@aws-amplify/client-config": "^1.5.4", + "@aws-amplify/client-config": "^1.5.5", "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/form-generator": "^1.0.3", - "@aws-amplify/model-generator": "^1.0.11", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", - "@aws-amplify/sandbox": "^1.2.8", + "@aws-amplify/model-generator": "^1.0.12", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", + "@aws-amplify/sandbox": "^1.2.9", "@aws-amplify/schema-generator": "^1.2.6", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", diff --git a/packages/client-config/CHANGELOG.md b/packages/client-config/CHANGELOG.md index e15faba6692..92a98d278b3 100644 --- a/packages/client-config/CHANGELOG.md +++ b/packages/client-config/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/client-config +## 1.5.5 + +### Patch Changes + +- aaeda9b: wraps no outputs found error from backend output client +- Updated dependencies [aaeda9b] +- Updated dependencies [23f1240] +- Updated dependencies [abff5a0] + - @aws-amplify/model-generator@1.0.12 + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.5.4 ### Patch Changes diff --git a/packages/client-config/package.json b/packages/client-config/package.json index 3672b0590c2..7566183f769 100644 --- a/packages/client-config/package.json +++ b/packages/client-config/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/client-config", - "version": "1.5.4", + "version": "1.5.5", "type": "module", "publishConfig": { "access": "public" @@ -26,9 +26,9 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/deployed-backend-client": "^1.5.0", - "@aws-amplify/model-generator": "^1.0.10", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/model-generator": "^1.0.12", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "zod": "^3.22.2" }, "devDependencies": { diff --git a/packages/model-generator/CHANGELOG.md b/packages/model-generator/CHANGELOG.md index e85bfc57a75..cc1e73ccad4 100644 --- a/packages/model-generator/CHANGELOG.md +++ b/packages/model-generator/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/model-generator +## 1.0.12 + +### Patch Changes + +- aaeda9b: wraps no outputs found error from backend output client +- Updated dependencies [23f1240] +- Updated dependencies [abff5a0] + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.0.11 ### Patch Changes diff --git a/packages/model-generator/package.json b/packages/model-generator/package.json index 8dd4ed67c33..f5bf48036b0 100644 --- a/packages/model-generator/package.json +++ b/packages/model-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/model-generator", - "version": "1.0.11", + "version": "1.0.12", "type": "module", "publishConfig": { "access": "public" @@ -23,8 +23,8 @@ "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/graphql-generator": "^0.5.1", "@aws-amplify/graphql-types-generator": "^3.6.0", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-appsync": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/credential-providers": "^3.624.0", diff --git a/packages/platform-core/CHANGELOG.md b/packages/platform-core/CHANGELOG.md index 96ffdd282b5..ab74ebabd87 100644 --- a/packages/platform-core/CHANGELOG.md +++ b/packages/platform-core/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/platform-core +## 1.5.0 + +### Minor Changes + +- 23f1240: added data logging api to defineData + +### Patch Changes + +- abff5a0: add InsufficientMemorySpaceError wrapping +- Updated dependencies [23f1240] + - @aws-amplify/plugin-types@1.7.0 + ## 1.4.0 ### Minor Changes diff --git a/packages/platform-core/package.json b/packages/platform-core/package.json index a637c342733..bd0e1cbae4f 100644 --- a/packages/platform-core/package.json +++ b/packages/platform-core/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/platform-core", - "version": "1.4.0", + "version": "1.5.0", "type": "commonjs", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "@types/uuid": "9.0.7" }, "dependencies": { - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-sts": "^3.624.0", "is-ci": "^3.0.1", "lodash.mergewith": "^4.6.2", diff --git a/packages/plugin-types/CHANGELOG.md b/packages/plugin-types/CHANGELOG.md index 6affed723d7..0923b8e46ca 100644 --- a/packages/plugin-types/CHANGELOG.md +++ b/packages/plugin-types/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/plugin-types +## 1.7.0 + +### Minor Changes + +- 23f1240: added data logging api to defineData + ## 1.6.0 ### Minor Changes diff --git a/packages/plugin-types/package.json b/packages/plugin-types/package.json index 05f2958a8f0..fd424073e42 100644 --- a/packages/plugin-types/package.json +++ b/packages/plugin-types/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/plugin-types", - "version": "1.6.0", + "version": "1.7.0", "types": "lib/index.d.ts", "type": "commonjs", "publishConfig": { diff --git a/packages/sandbox/CHANGELOG.md b/packages/sandbox/CHANGELOG.md index a6d59617bb0..4fa2de7d0dc 100644 --- a/packages/sandbox/CHANGELOG.md +++ b/packages/sandbox/CHANGELOG.md @@ -1,5 +1,19 @@ # @aws-amplify/sandbox +## 1.2.9 + +### Patch Changes + +- aaeda9b: wraps no outputs found error from backend output client +- Updated dependencies [aaeda9b] +- Updated dependencies [642b441] +- Updated dependencies [23f1240] +- Updated dependencies [abff5a0] + - @aws-amplify/client-config@1.5.5 + - @aws-amplify/backend-deployer@1.1.13 + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.2.8 ### Patch Changes diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index 4f81dda9ab7..b04e734283f 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/sandbox", - "version": "1.2.8", + "version": "1.2.9", "type": "module", "publishConfig": { "access": "public" @@ -19,13 +19,13 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.12", + "@aws-amplify/backend-deployer": "^1.1.13", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.1", - "@aws-amplify/client-config": "^1.5.4", + "@aws-amplify/client-config": "^1.5.5", "@aws-amplify/deployed-backend-client": "^1.5.0", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", From a7506f91dd9f21325815d0634e793b89fa88387e Mon Sep 17 00:00:00 2001 From: Kamil Sobol <5849952+sobolk@users.noreply.github.com> Date: Fri, 3 Jan 2025 09:28:10 -0800 Subject: [PATCH 188/199] Revert "Version Packages (#2367)" (#2398) This reverts commit 8322e7278c375d3521f0b4bf6ebe38850628fb70. --- .changeset/blue-meals-kneel.md | 2 ++ .changeset/chilly-pillows-live.md | 8 ++++++++ .changeset/hungry-dogs-juggle.md | 2 ++ .changeset/new-maps-lay.md | 5 +++++ .changeset/quick-dingos-whisper.md | 10 ++++++++++ .changeset/short-schools-act.md | 5 +++++ .changeset/stale-pianos-marry.md | 2 ++ .changeset/tall-parrots-sort.md | 6 ++++++ packages/backend-ai/CHANGELOG.md | 13 ------------- packages/backend-ai/package.json | 6 +++--- packages/backend-data/CHANGELOG.md | 11 ----------- packages/backend-data/package.json | 6 +++--- packages/backend-deployer/CHANGELOG.md | 10 ---------- packages/backend-deployer/package.json | 6 +++--- packages/backend-function/CHANGELOG.md | 12 ------------ packages/backend-function/package.json | 6 +++--- packages/backend/CHANGELOG.md | 19 ------------------- packages/backend/package.json | 12 ++++++------ packages/cli/CHANGELOG.md | 16 ---------------- packages/cli/package.json | 14 +++++++------- packages/client-config/CHANGELOG.md | 12 ------------ packages/client-config/package.json | 8 ++++---- packages/model-generator/CHANGELOG.md | 10 ---------- packages/model-generator/package.json | 6 +++--- packages/platform-core/CHANGELOG.md | 12 ------------ packages/platform-core/package.json | 4 ++-- packages/plugin-types/CHANGELOG.md | 6 ------ packages/plugin-types/package.json | 2 +- packages/sandbox/CHANGELOG.md | 14 -------------- packages/sandbox/package.json | 10 +++++----- 30 files changed, 80 insertions(+), 175 deletions(-) create mode 100644 .changeset/blue-meals-kneel.md create mode 100644 .changeset/chilly-pillows-live.md create mode 100644 .changeset/hungry-dogs-juggle.md create mode 100644 .changeset/new-maps-lay.md create mode 100644 .changeset/quick-dingos-whisper.md create mode 100644 .changeset/short-schools-act.md create mode 100644 .changeset/stale-pianos-marry.md create mode 100644 .changeset/tall-parrots-sort.md diff --git a/.changeset/blue-meals-kneel.md b/.changeset/blue-meals-kneel.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/blue-meals-kneel.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/chilly-pillows-live.md b/.changeset/chilly-pillows-live.md new file mode 100644 index 00000000000..da53db4b3b7 --- /dev/null +++ b/.changeset/chilly-pillows-live.md @@ -0,0 +1,8 @@ +--- +'@aws-amplify/model-generator': patch +'@aws-amplify/client-config': patch +'@aws-amplify/sandbox': patch +'@aws-amplify/backend-cli': patch +--- + +wraps no outputs found error from backend output client diff --git a/.changeset/hungry-dogs-juggle.md b/.changeset/hungry-dogs-juggle.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/hungry-dogs-juggle.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/new-maps-lay.md b/.changeset/new-maps-lay.md new file mode 100644 index 00000000000..e39a85c9457 --- /dev/null +++ b/.changeset/new-maps-lay.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +catch and wrap deployment in progress when deleting the backend diff --git a/.changeset/quick-dingos-whisper.md b/.changeset/quick-dingos-whisper.md new file mode 100644 index 00000000000..3e4c117eda2 --- /dev/null +++ b/.changeset/quick-dingos-whisper.md @@ -0,0 +1,10 @@ +--- +'@aws-amplify/backend-function': minor +'@aws-amplify/platform-core': minor +'@aws-amplify/backend-data': minor +'@aws-amplify/plugin-types': minor +'@aws-amplify/backend-ai': minor +'@aws-amplify/backend': minor +--- + +added data logging api to defineData diff --git a/.changeset/short-schools-act.md b/.changeset/short-schools-act.md new file mode 100644 index 00000000000..da6857ad9e2 --- /dev/null +++ b/.changeset/short-schools-act.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/platform-core': patch +--- + +add InsufficientMemorySpaceError wrapping diff --git a/.changeset/stale-pianos-marry.md b/.changeset/stale-pianos-marry.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/stale-pianos-marry.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/tall-parrots-sort.md b/.changeset/tall-parrots-sort.md new file mode 100644 index 00000000000..c14c30fd69c --- /dev/null +++ b/.changeset/tall-parrots-sort.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/backend-function': minor +'@aws-amplify/backend': minor +--- + +adds support for architecture property on defineFunction diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index b9f5e19f530..a46001a7e52 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,18 +1,5 @@ # @aws-amplify/backend-ai -## 1.3.0 - -### Minor Changes - -- 23f1240: added data logging api to defineData - -### Patch Changes - -- Updated dependencies [23f1240] -- Updated dependencies [abff5a0] - - @aws-amplify/platform-core@1.5.0 - - @aws-amplify/plugin-types@1.7.0 - ## 1.2.0 ### Minor Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 281755a8112..8e00334af05 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "1.3.0", + "version": "1.2.0", "type": "module", "publishConfig": { "access": "public" @@ -26,8 +26,8 @@ "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/data-schema-types": "^1.2.0", - "@aws-amplify/platform-core": "^1.5.0", - "@aws-amplify/plugin-types": "^1.7.0" + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", diff --git a/packages/backend-data/CHANGELOG.md b/packages/backend-data/CHANGELOG.md index 3cdafdd4109..6acf615618d 100644 --- a/packages/backend-data/CHANGELOG.md +++ b/packages/backend-data/CHANGELOG.md @@ -1,16 +1,5 @@ # @aws-amplify/backend-data -## 1.4.0 - -### Minor Changes - -- 23f1240: added data logging api to defineData - -### Patch Changes - -- Updated dependencies [23f1240] - - @aws-amplify/plugin-types@1.7.0 - ## 1.3.0 ### Minor Changes diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index 1e83462ba51..c2162f37108 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-data", - "version": "1.4.0", + "version": "1.3.0", "type": "module", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "devDependencies": { "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/backend-platform-test-stubs": "^0.3.7", - "@aws-amplify/platform-core": "^1.5.0" + "@aws-amplify/platform-core": "^1.4.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", @@ -33,6 +33,6 @@ "@aws-amplify/data-construct": "^1.14.5", "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/graphql-generator": "^0.5.1", - "@aws-amplify/plugin-types": "^1.7.0" + "@aws-amplify/plugin-types": "^1.6.0" } } diff --git a/packages/backend-deployer/CHANGELOG.md b/packages/backend-deployer/CHANGELOG.md index 8cf0c3d4d3f..75ad01f825d 100644 --- a/packages/backend-deployer/CHANGELOG.md +++ b/packages/backend-deployer/CHANGELOG.md @@ -1,15 +1,5 @@ # @aws-amplify/backend-deployer -## 1.1.13 - -### Patch Changes - -- 642b441: catch and wrap deployment in progress when deleting the backend -- Updated dependencies [23f1240] -- Updated dependencies [abff5a0] - - @aws-amplify/platform-core@1.5.0 - - @aws-amplify/plugin-types@1.7.0 - ## 1.1.12 ### Patch Changes diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index b3648344362..93082d2ecc6 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-deployer", - "version": "1.1.13", + "version": "1.1.12", "type": "module", "publishConfig": { "access": "public" @@ -19,8 +19,8 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.5.0", - "@aws-amplify/plugin-types": "^1.7.0", + "@aws-amplify/platform-core": "^1.4.0", + "@aws-amplify/plugin-types": "^1.6.0", "execa": "^9.5.1", "tsx": "^4.6.1", "strip-ansi": "^6.0.1" diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index a557263de6d..31c9a145496 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,17 +1,5 @@ # @aws-amplify/backend-function -## 1.11.0 - -### Minor Changes - -- 23f1240: added data logging api to defineData -- b1c0f0d: adds support for architecture property on defineFunction - -### Patch Changes - -- Updated dependencies [23f1240] - - @aws-amplify/plugin-types@1.7.0 - ## 1.10.0 ### Minor Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index a8dcf94e8fc..6cb3d38b934 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.11.0", + "version": "1.10.0", "type": "module", "publishConfig": { "access": "public" @@ -26,13 +26,13 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", - "@aws-amplify/plugin-types": "^1.7.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-s3": "^3.624.0", "execa": "^9.5.1" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.7", - "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/platform-core": "^1.4.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index bb97d98b901..cf0f756dd50 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,24 +1,5 @@ # @aws-amplify/backend -## 1.12.0 - -### Minor Changes - -- 23f1240: added data logging api to defineData -- b1c0f0d: adds support for architecture property on defineFunction - -### Patch Changes - -- Updated dependencies [aaeda9b] -- Updated dependencies [23f1240] -- Updated dependencies [abff5a0] -- Updated dependencies [b1c0f0d] - - @aws-amplify/client-config@1.5.5 - - @aws-amplify/backend-function@1.11.0 - - @aws-amplify/platform-core@1.5.0 - - @aws-amplify/backend-data@1.4.0 - - @aws-amplify/plugin-types@1.7.0 - ## 1.11.0 ### Minor Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 5190db01efb..834966d38a1 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.12.0", + "version": "1.11.0", "type": "module", "publishConfig": { "access": "public" @@ -32,15 +32,15 @@ "dependencies": { "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/backend-auth": "^1.4.2", - "@aws-amplify/backend-function": "^1.11.0", - "@aws-amplify/backend-data": "^1.4.0", + "@aws-amplify/backend-function": "^1.10.0", + "@aws-amplify/backend-data": "^1.3.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.4", - "@aws-amplify/client-config": "^1.5.5", - "@aws-amplify/platform-core": "^1.5.0", - "@aws-amplify/plugin-types": "^1.7.0", + "@aws-amplify/client-config": "^1.5.4", + "@aws-amplify/platform-core": "^1.4.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-amplify": "^3.624.0" }, "peerDependencies": { diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index c35ae7ab33c..f90b68b64d3 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,21 +1,5 @@ # @aws-amplify/backend-cli -## 1.4.6 - -### Patch Changes - -- aaeda9b: wraps no outputs found error from backend output client -- Updated dependencies [aaeda9b] -- Updated dependencies [642b441] -- Updated dependencies [23f1240] -- Updated dependencies [abff5a0] - - @aws-amplify/model-generator@1.0.12 - - @aws-amplify/client-config@1.5.5 - - @aws-amplify/sandbox@1.2.9 - - @aws-amplify/backend-deployer@1.1.13 - - @aws-amplify/platform-core@1.5.0 - - @aws-amplify/plugin-types@1.7.0 - ## 1.4.5 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 589cd58450d..72122edab6d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-cli", - "version": "1.4.6", + "version": "1.4.5", "description": "Command line interface for various Amplify tools", "bin": { "ampx": "lib/ampx.js", @@ -31,17 +31,17 @@ }, "homepage": "https://github.com/aws-amplify/amplify-backend#readme", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.13", + "@aws-amplify/backend-deployer": "^1.1.12", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.1", - "@aws-amplify/client-config": "^1.5.5", + "@aws-amplify/client-config": "^1.5.4", "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/form-generator": "^1.0.3", - "@aws-amplify/model-generator": "^1.0.12", - "@aws-amplify/platform-core": "^1.5.0", - "@aws-amplify/plugin-types": "^1.7.0", - "@aws-amplify/sandbox": "^1.2.9", + "@aws-amplify/model-generator": "^1.0.11", + "@aws-amplify/platform-core": "^1.4.0", + "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/sandbox": "^1.2.8", "@aws-amplify/schema-generator": "^1.2.6", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", diff --git a/packages/client-config/CHANGELOG.md b/packages/client-config/CHANGELOG.md index 92a98d278b3..e15faba6692 100644 --- a/packages/client-config/CHANGELOG.md +++ b/packages/client-config/CHANGELOG.md @@ -1,17 +1,5 @@ # @aws-amplify/client-config -## 1.5.5 - -### Patch Changes - -- aaeda9b: wraps no outputs found error from backend output client -- Updated dependencies [aaeda9b] -- Updated dependencies [23f1240] -- Updated dependencies [abff5a0] - - @aws-amplify/model-generator@1.0.12 - - @aws-amplify/platform-core@1.5.0 - - @aws-amplify/plugin-types@1.7.0 - ## 1.5.4 ### Patch Changes diff --git a/packages/client-config/package.json b/packages/client-config/package.json index 7566183f769..3672b0590c2 100644 --- a/packages/client-config/package.json +++ b/packages/client-config/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/client-config", - "version": "1.5.5", + "version": "1.5.4", "type": "module", "publishConfig": { "access": "public" @@ -26,9 +26,9 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/deployed-backend-client": "^1.5.0", - "@aws-amplify/model-generator": "^1.0.12", - "@aws-amplify/platform-core": "^1.5.0", - "@aws-amplify/plugin-types": "^1.7.0", + "@aws-amplify/model-generator": "^1.0.10", + "@aws-amplify/platform-core": "^1.4.0", + "@aws-amplify/plugin-types": "^1.6.0", "zod": "^3.22.2" }, "devDependencies": { diff --git a/packages/model-generator/CHANGELOG.md b/packages/model-generator/CHANGELOG.md index cc1e73ccad4..e85bfc57a75 100644 --- a/packages/model-generator/CHANGELOG.md +++ b/packages/model-generator/CHANGELOG.md @@ -1,15 +1,5 @@ # @aws-amplify/model-generator -## 1.0.12 - -### Patch Changes - -- aaeda9b: wraps no outputs found error from backend output client -- Updated dependencies [23f1240] -- Updated dependencies [abff5a0] - - @aws-amplify/platform-core@1.5.0 - - @aws-amplify/plugin-types@1.7.0 - ## 1.0.11 ### Patch Changes diff --git a/packages/model-generator/package.json b/packages/model-generator/package.json index f5bf48036b0..8dd4ed67c33 100644 --- a/packages/model-generator/package.json +++ b/packages/model-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/model-generator", - "version": "1.0.12", + "version": "1.0.11", "type": "module", "publishConfig": { "access": "public" @@ -23,8 +23,8 @@ "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/graphql-generator": "^0.5.1", "@aws-amplify/graphql-types-generator": "^3.6.0", - "@aws-amplify/platform-core": "^1.5.0", - "@aws-amplify/plugin-types": "^1.7.0", + "@aws-amplify/platform-core": "^1.4.0", + "@aws-amplify/plugin-types": "^1.4.0", "@aws-sdk/client-appsync": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/credential-providers": "^3.624.0", diff --git a/packages/platform-core/CHANGELOG.md b/packages/platform-core/CHANGELOG.md index ab74ebabd87..96ffdd282b5 100644 --- a/packages/platform-core/CHANGELOG.md +++ b/packages/platform-core/CHANGELOG.md @@ -1,17 +1,5 @@ # @aws-amplify/platform-core -## 1.5.0 - -### Minor Changes - -- 23f1240: added data logging api to defineData - -### Patch Changes - -- abff5a0: add InsufficientMemorySpaceError wrapping -- Updated dependencies [23f1240] - - @aws-amplify/plugin-types@1.7.0 - ## 1.4.0 ### Minor Changes diff --git a/packages/platform-core/package.json b/packages/platform-core/package.json index bd0e1cbae4f..a637c342733 100644 --- a/packages/platform-core/package.json +++ b/packages/platform-core/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/platform-core", - "version": "1.5.0", + "version": "1.4.0", "type": "commonjs", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "@types/uuid": "9.0.7" }, "dependencies": { - "@aws-amplify/plugin-types": "^1.7.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-sts": "^3.624.0", "is-ci": "^3.0.1", "lodash.mergewith": "^4.6.2", diff --git a/packages/plugin-types/CHANGELOG.md b/packages/plugin-types/CHANGELOG.md index 0923b8e46ca..6affed723d7 100644 --- a/packages/plugin-types/CHANGELOG.md +++ b/packages/plugin-types/CHANGELOG.md @@ -1,11 +1,5 @@ # @aws-amplify/plugin-types -## 1.7.0 - -### Minor Changes - -- 23f1240: added data logging api to defineData - ## 1.6.0 ### Minor Changes diff --git a/packages/plugin-types/package.json b/packages/plugin-types/package.json index fd424073e42..05f2958a8f0 100644 --- a/packages/plugin-types/package.json +++ b/packages/plugin-types/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/plugin-types", - "version": "1.7.0", + "version": "1.6.0", "types": "lib/index.d.ts", "type": "commonjs", "publishConfig": { diff --git a/packages/sandbox/CHANGELOG.md b/packages/sandbox/CHANGELOG.md index 4fa2de7d0dc..a6d59617bb0 100644 --- a/packages/sandbox/CHANGELOG.md +++ b/packages/sandbox/CHANGELOG.md @@ -1,19 +1,5 @@ # @aws-amplify/sandbox -## 1.2.9 - -### Patch Changes - -- aaeda9b: wraps no outputs found error from backend output client -- Updated dependencies [aaeda9b] -- Updated dependencies [642b441] -- Updated dependencies [23f1240] -- Updated dependencies [abff5a0] - - @aws-amplify/client-config@1.5.5 - - @aws-amplify/backend-deployer@1.1.13 - - @aws-amplify/platform-core@1.5.0 - - @aws-amplify/plugin-types@1.7.0 - ## 1.2.8 ### Patch Changes diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index b04e734283f..4f81dda9ab7 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/sandbox", - "version": "1.2.9", + "version": "1.2.8", "type": "module", "publishConfig": { "access": "public" @@ -19,13 +19,13 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.13", + "@aws-amplify/backend-deployer": "^1.1.12", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.1", - "@aws-amplify/client-config": "^1.5.5", + "@aws-amplify/client-config": "^1.5.4", "@aws-amplify/deployed-backend-client": "^1.5.0", - "@aws-amplify/platform-core": "^1.5.0", - "@aws-amplify/plugin-types": "^1.7.0", + "@aws-amplify/platform-core": "^1.4.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", From 28e9642761caf1cb21e961c60f825672f9cb5a54 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 3 Jan 2025 09:32:13 -0800 Subject: [PATCH 189/199] remove handle_dependabot_version_update step from needs (#2399) --- .github/workflows/health_checks.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/health_checks.yml b/.github/workflows/health_checks.yml index c461f282b7f..626c320b736 100644 --- a/.github/workflows/health_checks.yml +++ b/.github/workflows/health_checks.yml @@ -296,7 +296,6 @@ jobs: needs: - install - resolve_inputs - - handle_dependabot_version_update runs-on: ubuntu-latest permissions: # This is required so that the step can read the labels on the pull request From 8183548af1855f880f6e355f81ed49f843652a86 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 09:53:47 -0800 Subject: [PATCH 190/199] Version Packages (#2401) Co-authored-by: github-actions[bot] --- .changeset/blue-meals-kneel.md | 2 -- .changeset/chilly-pillows-live.md | 8 -------- .changeset/hungry-dogs-juggle.md | 2 -- .changeset/new-maps-lay.md | 5 ----- .changeset/quick-dingos-whisper.md | 10 ---------- .changeset/short-schools-act.md | 5 ----- .changeset/stale-pianos-marry.md | 2 -- .changeset/tall-parrots-sort.md | 6 ------ packages/backend-ai/CHANGELOG.md | 13 +++++++++++++ packages/backend-ai/package.json | 6 +++--- packages/backend-data/CHANGELOG.md | 11 +++++++++++ packages/backend-data/package.json | 6 +++--- packages/backend-deployer/CHANGELOG.md | 10 ++++++++++ packages/backend-deployer/package.json | 6 +++--- packages/backend-function/CHANGELOG.md | 12 ++++++++++++ packages/backend-function/package.json | 6 +++--- packages/backend/CHANGELOG.md | 19 +++++++++++++++++++ packages/backend/package.json | 12 ++++++------ packages/cli/CHANGELOG.md | 16 ++++++++++++++++ packages/cli/package.json | 14 +++++++------- packages/client-config/CHANGELOG.md | 12 ++++++++++++ packages/client-config/package.json | 8 ++++---- packages/model-generator/CHANGELOG.md | 10 ++++++++++ packages/model-generator/package.json | 6 +++--- packages/platform-core/CHANGELOG.md | 12 ++++++++++++ packages/platform-core/package.json | 4 ++-- packages/plugin-types/CHANGELOG.md | 6 ++++++ packages/plugin-types/package.json | 2 +- packages/sandbox/CHANGELOG.md | 14 ++++++++++++++ packages/sandbox/package.json | 10 +++++----- 30 files changed, 175 insertions(+), 80 deletions(-) delete mode 100644 .changeset/blue-meals-kneel.md delete mode 100644 .changeset/chilly-pillows-live.md delete mode 100644 .changeset/hungry-dogs-juggle.md delete mode 100644 .changeset/new-maps-lay.md delete mode 100644 .changeset/quick-dingos-whisper.md delete mode 100644 .changeset/short-schools-act.md delete mode 100644 .changeset/stale-pianos-marry.md delete mode 100644 .changeset/tall-parrots-sort.md diff --git a/.changeset/blue-meals-kneel.md b/.changeset/blue-meals-kneel.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/blue-meals-kneel.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/chilly-pillows-live.md b/.changeset/chilly-pillows-live.md deleted file mode 100644 index da53db4b3b7..00000000000 --- a/.changeset/chilly-pillows-live.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@aws-amplify/model-generator': patch -'@aws-amplify/client-config': patch -'@aws-amplify/sandbox': patch -'@aws-amplify/backend-cli': patch ---- - -wraps no outputs found error from backend output client diff --git a/.changeset/hungry-dogs-juggle.md b/.changeset/hungry-dogs-juggle.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/hungry-dogs-juggle.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/new-maps-lay.md b/.changeset/new-maps-lay.md deleted file mode 100644 index e39a85c9457..00000000000 --- a/.changeset/new-maps-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/backend-deployer': patch ---- - -catch and wrap deployment in progress when deleting the backend diff --git a/.changeset/quick-dingos-whisper.md b/.changeset/quick-dingos-whisper.md deleted file mode 100644 index 3e4c117eda2..00000000000 --- a/.changeset/quick-dingos-whisper.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@aws-amplify/backend-function': minor -'@aws-amplify/platform-core': minor -'@aws-amplify/backend-data': minor -'@aws-amplify/plugin-types': minor -'@aws-amplify/backend-ai': minor -'@aws-amplify/backend': minor ---- - -added data logging api to defineData diff --git a/.changeset/short-schools-act.md b/.changeset/short-schools-act.md deleted file mode 100644 index da6857ad9e2..00000000000 --- a/.changeset/short-schools-act.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/platform-core': patch ---- - -add InsufficientMemorySpaceError wrapping diff --git a/.changeset/stale-pianos-marry.md b/.changeset/stale-pianos-marry.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/stale-pianos-marry.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/tall-parrots-sort.md b/.changeset/tall-parrots-sort.md deleted file mode 100644 index c14c30fd69c..00000000000 --- a/.changeset/tall-parrots-sort.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/backend-function': minor -'@aws-amplify/backend': minor ---- - -adds support for architecture property on defineFunction diff --git a/packages/backend-ai/CHANGELOG.md b/packages/backend-ai/CHANGELOG.md index a46001a7e52..80955f5eb1a 100644 --- a/packages/backend-ai/CHANGELOG.md +++ b/packages/backend-ai/CHANGELOG.md @@ -1,5 +1,18 @@ # @aws-amplify/backend-ai +## 1.3.0 + +### Minor Changes + +- a7506f9: added data logging api to defineData + +### Patch Changes + +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/backend-ai/package.json b/packages/backend-ai/package.json index 8e00334af05..281755a8112 100644 --- a/packages/backend-ai/package.json +++ b/packages/backend-ai/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-ai", - "version": "1.2.0", + "version": "1.3.0", "type": "module", "publishConfig": { "access": "public" @@ -26,8 +26,8 @@ "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/data-schema-types": "^1.2.0", - "@aws-amplify/platform-core": "^1.3.0", - "@aws-amplify/plugin-types": "^1.6.0" + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", diff --git a/packages/backend-data/CHANGELOG.md b/packages/backend-data/CHANGELOG.md index 6acf615618d..2201316d3ca 100644 --- a/packages/backend-data/CHANGELOG.md +++ b/packages/backend-data/CHANGELOG.md @@ -1,5 +1,16 @@ # @aws-amplify/backend-data +## 1.4.0 + +### Minor Changes + +- a7506f9: added data logging api to defineData + +### Patch Changes + +- Updated dependencies [a7506f9] + - @aws-amplify/plugin-types@1.7.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/backend-data/package.json b/packages/backend-data/package.json index c2162f37108..1e83462ba51 100644 --- a/packages/backend-data/package.json +++ b/packages/backend-data/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-data", - "version": "1.3.0", + "version": "1.4.0", "type": "module", "publishConfig": { "access": "public" @@ -21,7 +21,7 @@ "devDependencies": { "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/backend-platform-test-stubs": "^0.3.7", - "@aws-amplify/platform-core": "^1.4.0" + "@aws-amplify/platform-core": "^1.5.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", @@ -33,6 +33,6 @@ "@aws-amplify/data-construct": "^1.14.5", "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/graphql-generator": "^0.5.1", - "@aws-amplify/plugin-types": "^1.6.0" + "@aws-amplify/plugin-types": "^1.7.0" } } diff --git a/packages/backend-deployer/CHANGELOG.md b/packages/backend-deployer/CHANGELOG.md index 75ad01f825d..c904704ade9 100644 --- a/packages/backend-deployer/CHANGELOG.md +++ b/packages/backend-deployer/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/backend-deployer +## 1.1.13 + +### Patch Changes + +- a7506f9: catch and wrap deployment in progress when deleting the backend +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.1.12 ### Patch Changes diff --git a/packages/backend-deployer/package.json b/packages/backend-deployer/package.json index 93082d2ecc6..b3648344362 100644 --- a/packages/backend-deployer/package.json +++ b/packages/backend-deployer/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-deployer", - "version": "1.1.12", + "version": "1.1.13", "type": "module", "publishConfig": { "access": "public" @@ -19,8 +19,8 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "execa": "^9.5.1", "tsx": "^4.6.1", "strip-ansi": "^6.0.1" diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index 31c9a145496..8db2202c77e 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/backend-function +## 1.11.0 + +### Minor Changes + +- a7506f9: added data logging api to defineData +- a7506f9: adds support for architecture property on defineFunction + +### Patch Changes + +- Updated dependencies [a7506f9] + - @aws-amplify/plugin-types@1.7.0 + ## 1.10.0 ### Minor Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index 6cb3d38b934..a8dcf94e8fc 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.10.0", + "version": "1.11.0", "type": "module", "publishConfig": { "access": "public" @@ -26,13 +26,13 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-s3": "^3.624.0", "execa": "^9.5.1" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.7", - "@aws-amplify/platform-core": "^1.4.0", + "@aws-amplify/platform-core": "^1.5.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index cf0f756dd50..81696e3bbb3 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,24 @@ # @aws-amplify/backend +## 1.12.0 + +### Minor Changes + +- a7506f9: added data logging api to defineData +- a7506f9: adds support for architecture property on defineFunction + +### Patch Changes + +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] + - @aws-amplify/client-config@1.5.5 + - @aws-amplify/backend-function@1.11.0 + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/backend-data@1.4.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.11.0 ### Minor Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 834966d38a1..5190db01efb 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.11.0", + "version": "1.12.0", "type": "module", "publishConfig": { "access": "public" @@ -32,15 +32,15 @@ "dependencies": { "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/backend-auth": "^1.4.2", - "@aws-amplify/backend-function": "^1.10.0", - "@aws-amplify/backend-data": "^1.3.0", + "@aws-amplify/backend-function": "^1.11.0", + "@aws-amplify/backend-data": "^1.4.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.4", - "@aws-amplify/client-config": "^1.5.4", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/client-config": "^1.5.5", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-amplify": "^3.624.0" }, "peerDependencies": { diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index f90b68b64d3..f74b6b96d1f 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,21 @@ # @aws-amplify/backend-cli +## 1.4.6 + +### Patch Changes + +- a7506f9: wraps no outputs found error from backend output client +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] + - @aws-amplify/model-generator@1.0.12 + - @aws-amplify/client-config@1.5.5 + - @aws-amplify/sandbox@1.2.9 + - @aws-amplify/backend-deployer@1.1.13 + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.4.5 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 72122edab6d..589cd58450d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-cli", - "version": "1.4.5", + "version": "1.4.6", "description": "Command line interface for various Amplify tools", "bin": { "ampx": "lib/ampx.js", @@ -31,17 +31,17 @@ }, "homepage": "https://github.com/aws-amplify/amplify-backend#readme", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.12", + "@aws-amplify/backend-deployer": "^1.1.13", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.1", - "@aws-amplify/client-config": "^1.5.4", + "@aws-amplify/client-config": "^1.5.5", "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/form-generator": "^1.0.3", - "@aws-amplify/model-generator": "^1.0.11", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", - "@aws-amplify/sandbox": "^1.2.8", + "@aws-amplify/model-generator": "^1.0.12", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", + "@aws-amplify/sandbox": "^1.2.9", "@aws-amplify/schema-generator": "^1.2.6", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", diff --git a/packages/client-config/CHANGELOG.md b/packages/client-config/CHANGELOG.md index e15faba6692..d88e8c839c6 100644 --- a/packages/client-config/CHANGELOG.md +++ b/packages/client-config/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/client-config +## 1.5.5 + +### Patch Changes + +- a7506f9: wraps no outputs found error from backend output client +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] + - @aws-amplify/model-generator@1.0.12 + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.5.4 ### Patch Changes diff --git a/packages/client-config/package.json b/packages/client-config/package.json index 3672b0590c2..7566183f769 100644 --- a/packages/client-config/package.json +++ b/packages/client-config/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/client-config", - "version": "1.5.4", + "version": "1.5.5", "type": "module", "publishConfig": { "access": "public" @@ -26,9 +26,9 @@ "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/deployed-backend-client": "^1.5.0", - "@aws-amplify/model-generator": "^1.0.10", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/model-generator": "^1.0.12", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "zod": "^3.22.2" }, "devDependencies": { diff --git a/packages/model-generator/CHANGELOG.md b/packages/model-generator/CHANGELOG.md index e85bfc57a75..fbd1823cf7f 100644 --- a/packages/model-generator/CHANGELOG.md +++ b/packages/model-generator/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/model-generator +## 1.0.12 + +### Patch Changes + +- a7506f9: wraps no outputs found error from backend output client +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.0.11 ### Patch Changes diff --git a/packages/model-generator/package.json b/packages/model-generator/package.json index 8dd4ed67c33..f5bf48036b0 100644 --- a/packages/model-generator/package.json +++ b/packages/model-generator/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/model-generator", - "version": "1.0.11", + "version": "1.0.12", "type": "module", "publishConfig": { "access": "public" @@ -23,8 +23,8 @@ "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/graphql-generator": "^0.5.1", "@aws-amplify/graphql-types-generator": "^3.6.0", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-appsync": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/credential-providers": "^3.624.0", diff --git a/packages/platform-core/CHANGELOG.md b/packages/platform-core/CHANGELOG.md index 96ffdd282b5..d824c13fcb1 100644 --- a/packages/platform-core/CHANGELOG.md +++ b/packages/platform-core/CHANGELOG.md @@ -1,5 +1,17 @@ # @aws-amplify/platform-core +## 1.5.0 + +### Minor Changes + +- a7506f9: added data logging api to defineData + +### Patch Changes + +- a7506f9: add InsufficientMemorySpaceError wrapping +- Updated dependencies [a7506f9] + - @aws-amplify/plugin-types@1.7.0 + ## 1.4.0 ### Minor Changes diff --git a/packages/platform-core/package.json b/packages/platform-core/package.json index a637c342733..bd0e1cbae4f 100644 --- a/packages/platform-core/package.json +++ b/packages/platform-core/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/platform-core", - "version": "1.4.0", + "version": "1.5.0", "type": "commonjs", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "@types/uuid": "9.0.7" }, "dependencies": { - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-sts": "^3.624.0", "is-ci": "^3.0.1", "lodash.mergewith": "^4.6.2", diff --git a/packages/plugin-types/CHANGELOG.md b/packages/plugin-types/CHANGELOG.md index 6affed723d7..2ff01de154c 100644 --- a/packages/plugin-types/CHANGELOG.md +++ b/packages/plugin-types/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/plugin-types +## 1.7.0 + +### Minor Changes + +- a7506f9: added data logging api to defineData + ## 1.6.0 ### Minor Changes diff --git a/packages/plugin-types/package.json b/packages/plugin-types/package.json index 05f2958a8f0..fd424073e42 100644 --- a/packages/plugin-types/package.json +++ b/packages/plugin-types/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/plugin-types", - "version": "1.6.0", + "version": "1.7.0", "types": "lib/index.d.ts", "type": "commonjs", "publishConfig": { diff --git a/packages/sandbox/CHANGELOG.md b/packages/sandbox/CHANGELOG.md index a6d59617bb0..90271901a69 100644 --- a/packages/sandbox/CHANGELOG.md +++ b/packages/sandbox/CHANGELOG.md @@ -1,5 +1,19 @@ # @aws-amplify/sandbox +## 1.2.9 + +### Patch Changes + +- a7506f9: wraps no outputs found error from backend output client +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] +- Updated dependencies [a7506f9] + - @aws-amplify/client-config@1.5.5 + - @aws-amplify/backend-deployer@1.1.13 + - @aws-amplify/platform-core@1.5.0 + - @aws-amplify/plugin-types@1.7.0 + ## 1.2.8 ### Patch Changes diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index 4f81dda9ab7..b04e734283f 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/sandbox", - "version": "1.2.8", + "version": "1.2.9", "type": "module", "publishConfig": { "access": "public" @@ -19,13 +19,13 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.12", + "@aws-amplify/backend-deployer": "^1.1.13", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.1", - "@aws-amplify/client-config": "^1.5.4", + "@aws-amplify/client-config": "^1.5.5", "@aws-amplify/deployed-backend-client": "^1.5.0", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", From b5869daa47a980d180967390cfd789043a737bdf Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 3 Jan 2025 10:39:13 -0800 Subject: [PATCH 191/199] add permission to write to pull request for handle version update job (#2402) --- .github/workflows/health_checks.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/health_checks.yml b/.github/workflows/health_checks.yml index 626c320b736..ae7ffd3286b 100644 --- a/.github/workflows/health_checks.yml +++ b/.github/workflows/health_checks.yml @@ -275,6 +275,9 @@ jobs: needs: - install - resolve_inputs + permissions: + # This is required so that this job can add the 'run-e2e' label on the pull request + pull-requests: write steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 with: From f20e8a3c09bfde6799cfaa4c25e8773c372bd403 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 3 Jan 2025 13:00:17 -0800 Subject: [PATCH 192/199] add permission to write contents for handle version update job (#2403) --- .github/workflows/health_checks.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/health_checks.yml b/.github/workflows/health_checks.yml index ae7ffd3286b..9438226e94f 100644 --- a/.github/workflows/health_checks.yml +++ b/.github/workflows/health_checks.yml @@ -276,8 +276,9 @@ jobs: - install - resolve_inputs permissions: - # This is required so that this job can add the 'run-e2e' label on the pull request + # This is required so that this job can add the 'run-e2e' label and push to the pull request pull-requests: write + contents: write steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4 with: From a712983d807ade09d983520d8f7a27180028a267 Mon Sep 17 00:00:00 2001 From: Kamil Sobol <5849952+sobolk@users.noreply.github.com> Date: Mon, 6 Jan 2025 09:07:39 -0800 Subject: [PATCH 193/199] Base64 encode amplify errors in serialized form (#2404) --- .changeset/metal-taxis-sing.md | 5 + .../src/errors/amplify_error.test.ts | 120 +++++++++++----- .../platform-core/src/errors/amplify_error.ts | 130 ++++++++++++------ 3 files changed, 177 insertions(+), 78 deletions(-) create mode 100644 .changeset/metal-taxis-sing.md diff --git a/.changeset/metal-taxis-sing.md b/.changeset/metal-taxis-sing.md new file mode 100644 index 00000000000..20b19e1886b --- /dev/null +++ b/.changeset/metal-taxis-sing.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/platform-core': patch +--- + +Base64 encode serialized Amplify Errors diff --git a/packages/platform-core/src/errors/amplify_error.test.ts b/packages/platform-core/src/errors/amplify_error.test.ts index bfb9feec56e..295da2c5186 100644 --- a/packages/platform-core/src/errors/amplify_error.test.ts +++ b/packages/platform-core/src/errors/amplify_error.test.ts @@ -85,67 +85,113 @@ and some after the error message assert.deepStrictEqual(actual?.cause?.message, testError.cause?.message); }); - void it('deserialize when string is encoded with single quote and has double quotes in it', () => { - const sampleStderr = `some random stderr + void describe('V1 deserialization', () => { + void it('deserialize when string is encoded with single quote and has double quotes in it', () => { + const sampleStderr = `some random stderr ${util.inspect({ serializedError: '{"name":"SyntaxError","classification":"ERROR","options":{"message":"test error message","resolution":"test resolution"}}', })} and some after the error message `; - const actual = AmplifyError.fromStderr(sampleStderr); - assert.deepStrictEqual(actual?.name, 'SyntaxError'); - assert.deepStrictEqual(actual?.classification, 'ERROR'); - assert.deepStrictEqual(actual?.message, 'test error message'); - assert.deepStrictEqual(actual?.resolution, 'test resolution'); - }); + const actual = AmplifyError.fromStderr(sampleStderr); + assert.deepStrictEqual(actual?.name, 'SyntaxError'); + assert.deepStrictEqual(actual?.classification, 'ERROR'); + assert.deepStrictEqual(actual?.message, 'test error message'); + assert.deepStrictEqual(actual?.resolution, 'test resolution'); + }); - void it('deserialize when string is encoded with single quote and has double quotes escaped in between', () => { - const sampleStderr = `some random stderr + void it('deserialize when string is encoded with single quote and has double quotes escaped in between', () => { + const sampleStderr = `some random stderr ${util.inspect({ serializedError: '{"name":"SyntaxError","classification":"ERROR","options":{"message":"paths must start with \\"/\\" and end with \\"/*","resolution":"test resolution"}}', })} and some after the error message `; - const actual = AmplifyError.fromStderr(sampleStderr); - assert.deepStrictEqual(actual?.name, 'SyntaxError'); - assert.deepStrictEqual(actual?.classification, 'ERROR'); - assert.deepStrictEqual( - actual?.message, - 'paths must start with "/" and end with "/*' - ); - assert.deepStrictEqual(actual?.resolution, 'test resolution'); - }); + const actual = AmplifyError.fromStderr(sampleStderr); + assert.deepStrictEqual(actual?.name, 'SyntaxError'); + assert.deepStrictEqual(actual?.classification, 'ERROR'); + assert.deepStrictEqual( + actual?.message, + 'paths must start with "/" and end with "/*' + ); + assert.deepStrictEqual(actual?.resolution, 'test resolution'); + }); - void it('deserialize when string is encoded with double quote and has double quotes string in it', () => { - const sampleStderr = `some random stderr + void it('deserialize when string is encoded with double quote and has double quotes string in it', () => { + const sampleStderr = `some random stderr serializedError: "{\\"name\\":\\"SyntaxError\\",\\"classification\\":\\"ERROR\\",\\"options\\":{\\"message\\":\\"test error message\\",\\"resolution\\":\\"test resolution\\"}}" and some after the error message `; - const actual = AmplifyError.fromStderr(sampleStderr); - assert.deepStrictEqual(actual?.name, 'SyntaxError'); - assert.deepStrictEqual(actual?.classification, 'ERROR'); - assert.deepStrictEqual(actual?.message, 'test error message'); - assert.deepStrictEqual(actual?.resolution, 'test resolution'); - }); + const actual = AmplifyError.fromStderr(sampleStderr); + assert.deepStrictEqual(actual?.name, 'SyntaxError'); + assert.deepStrictEqual(actual?.classification, 'ERROR'); + assert.deepStrictEqual(actual?.message, 'test error message'); + assert.deepStrictEqual(actual?.resolution, 'test resolution'); + }); - void it('deserialize when string has single quotes in between', () => { - const sampleStderr = `some random stderr + void it('deserialize when string has single quotes in between', () => { + const sampleStderr = `some random stderr ${util.inspect({ serializedError: '{"name":"SyntaxError","classification":"ERROR","options":{"message":"Cannot read properties of undefined (reading \'data\')","resolution":"test resolution"}}', })} and some after the error message `; - const actual = AmplifyError.fromStderr(sampleStderr); - assert.deepStrictEqual(actual?.name, 'SyntaxError'); - assert.deepStrictEqual(actual?.classification, 'ERROR'); - assert.deepStrictEqual( - actual?.message, - `Cannot read properties of undefined (reading 'data')` - ); - assert.deepStrictEqual(actual?.resolution, 'test resolution'); + const actual = AmplifyError.fromStderr(sampleStderr); + assert.deepStrictEqual(actual?.name, 'SyntaxError'); + assert.deepStrictEqual(actual?.classification, 'ERROR'); + assert.deepStrictEqual( + actual?.message, + `Cannot read properties of undefined (reading 'data')` + ); + assert.deepStrictEqual(actual?.resolution, 'test resolution'); + }); + }); + + void describe('V2 deserialization', () => { + void it('deserialize when string is encoded with single quote', () => { + const sampleStderr = `some random stderr + serializedError: '${Buffer.from( + '{"name":"SyntaxError","classification":"ERROR","options":{"message":"test error message","resolution":"test resolution"}}' + ).toString('base64')}', +and some after the error message + `; + const actual = AmplifyError.fromStderr(sampleStderr); + assert.deepStrictEqual(actual?.name, 'SyntaxError'); + assert.deepStrictEqual(actual?.classification, 'ERROR'); + assert.deepStrictEqual(actual?.message, 'test error message'); + assert.deepStrictEqual(actual?.resolution, 'test resolution'); + }); + + void it('deserialize when string is encoded with double quote', () => { + const sampleStderr = `some random stderr + serializedError: "${Buffer.from( + '{"name":"SyntaxError","classification":"ERROR","options":{"message":"test error message","resolution":"test resolution"}}' + ).toString('base64')}", +and some after the error message + `; + const actual = AmplifyError.fromStderr(sampleStderr); + assert.deepStrictEqual(actual?.name, 'SyntaxError'); + assert.deepStrictEqual(actual?.classification, 'ERROR'); + assert.deepStrictEqual(actual?.message, 'test error message'); + assert.deepStrictEqual(actual?.resolution, 'test resolution'); + }); + + void it('deserialize when string is encoded with back ticks', () => { + const sampleStderr = `some random stderr + serializedError: \`${Buffer.from( + '{"name":"SyntaxError","classification":"ERROR","options":{"message":"test error message","resolution":"test resolution"}}' + ).toString('base64')}\`, +and some after the error message + `; + const actual = AmplifyError.fromStderr(sampleStderr); + assert.deepStrictEqual(actual?.name, 'SyntaxError'); + assert.deepStrictEqual(actual?.classification, 'ERROR'); + assert.deepStrictEqual(actual?.message, 'test error message'); + assert.deepStrictEqual(actual?.resolution, 'test resolution'); + }); }); }); diff --git a/packages/platform-core/src/errors/amplify_error.ts b/packages/platform-core/src/errors/amplify_error.ts index e2a2c1c1a46..981d18a1ccf 100644 --- a/packages/platform-core/src/errors/amplify_error.ts +++ b/packages/platform-core/src/errors/amplify_error.ts @@ -47,55 +47,41 @@ export abstract class AmplifyError extends Error { if (cause && AmplifyError.isAmplifyError(cause)) { cause.serializedError = undefined; } - this.serializedError = JSON.stringify( - { - name, - classification, - options, - cause, - }, - errorSerializer - ); + this.serializedError = Buffer.from( + JSON.stringify( + { + name, + classification, + options, + cause, + }, + errorSerializer + ) + ).toString('base64'); } static fromStderr = (_stderr: string): AmplifyError | undefined => { - /** - * `["']?serializedError["']?:[ ]?` captures the start of the serialized error. The quotes depend on which OS is being used - * `(?:`(.+?)`|'(.+?)'|"((?:\\"|[^"])*?)")` captures the rest of the serialized string enclosed in either single quote, - * double quotes or back-ticks. - */ - const extractionRegex = - /["']?serializedError["']?:[ ]?(?:`(.+?)`|'(.+?)'|"((?:\\"|[^"])*?)")/; - const serialized = _stderr.match(extractionRegex); - if (serialized && serialized.length === 4) { - // 4 because 1 match and 3 capturing groups - try { - const serializedString = serialized - .slice(1) - .find((item) => item && item.length > 0) - ?.replaceAll('\\"', '"') - .replaceAll("\\'", "'"); + try { + const serializedString = tryFindSerializedErrorJSONString(_stderr); - if (!serializedString) { - return undefined; - } + if (!serializedString) { + return undefined; + } - const { name, classification, options, cause } = - JSON.parse(serializedString); + const { name, classification, options, cause } = + JSON.parse(serializedString); - let serializedCause = cause; - if (cause && ErrorSerializerDeserializer.isSerializedErrorType(cause)) { - serializedCause = ErrorSerializerDeserializer.deserialize(cause); - } - return classification === 'ERROR' - ? new AmplifyUserError(name, options, serializedCause) - : new AmplifyFault(name, options, serializedCause); - } catch (error) { - // cannot deserialize - return undefined; + let serializedCause = cause; + if (cause && ErrorSerializerDeserializer.isSerializedErrorType(cause)) { + serializedCause = ErrorSerializerDeserializer.deserialize(cause); } + return classification === 'ERROR' + ? new AmplifyUserError(name, options, serializedCause) + : new AmplifyFault(name, options, serializedCause); + } catch (error) { + // cannot deserialize + return undefined; } - return undefined; }; /** @@ -207,6 +193,68 @@ export abstract class AmplifyError extends Error { }; } +const tryFindSerializedErrorJSONString = ( + _stderr: string +): string | undefined => { + let errorJSONString = tryFindSerializedErrorJSONStringV2(_stderr); + if (!errorJSONString) { + errorJSONString = tryFindSerializedErrorJSONStringV1(_stderr); + } + return errorJSONString; +}; + +/** + * Tries to find serialized string assuming that it is in a form of serialized JSON encoded with base64. + */ +const tryFindSerializedErrorJSONStringV2 = ( + _stderr: string +): string | undefined => { + /** + * `["']?serializedError["']?:[ ]?` captures the start of the serialized error. The quotes depend on which OS is being used + * `(?:`([a-zA-Z0-9+/=]+?)`|'([a-zA-Z0-9+/=]+?)'|"([a-zA-Z0-9+/=]+?)")` captures the rest of the serialized string enclosed in either single quote, + * double quotes or back-ticks. + */ + const extractionRegex = + /["']?serializedError["']?:[ ]?(?:`([a-zA-Z0-9+/=]+?)`|'([a-zA-Z0-9+/=]+?)'|"([a-zA-Z0-9+/=]+?)")/; + const serialized = _stderr.match(extractionRegex); + if (serialized && serialized.length === 4) { + // 4 because 1 match and 3 capturing groups + const base64SerializedString = serialized + .slice(1) + .find((item) => item && item.length > 0); + if (base64SerializedString) { + return Buffer.from(base64SerializedString, 'base64').toString('utf-8'); + } + } + return undefined; +}; + +/** + * Tries to find serialized string assuming that it is in a form of serialized JSON. + * @deprecated This is old format left for backwards compatibility in case that synth-time components are using older version of platform-core. + */ +const tryFindSerializedErrorJSONStringV1 = ( + _stderr: string +): string | undefined => { + /** + * `["']?serializedError["']?:[ ]?` captures the start of the serialized error. The quotes depend on which OS is being used + * `(?:`(.+?)`|'(.+?)'|"((?:\\"|[^"])*?)")` captures the rest of the serialized string enclosed in either single quote, + * double quotes or back-ticks. + */ + const extractionRegex = + /["']?serializedError["']?:[ ]?(?:`(.+?)`|'(.+?)'|"((?:\\"|[^"])*?)")/; + const serialized = _stderr.match(extractionRegex); + if (serialized && serialized.length === 4) { + // 4 because 1 match and 3 capturing groups + return serialized + .slice(1) + .find((item) => item && item.length > 0) + ?.replaceAll('\\"', '"') + .replaceAll("\\'", "'"); + } + return undefined; +}; + const isCredentialsError = (err?: Error): boolean => { return ( !!err && From a04dbb84ce309e43f9dfe35cb7362970e2076e6f Mon Sep 17 00:00:00 2001 From: Kamil Sobol <5849952+sobolk@users.noreply.github.com> Date: Mon, 6 Jan 2025 09:54:57 -0800 Subject: [PATCH 194/199] Improve retry predicate for create amplify (#2407) --- .changeset/large-rocks-sparkle.md | 2 ++ packages/integration-tests/src/retry.ts | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 .changeset/large-rocks-sparkle.md diff --git a/.changeset/large-rocks-sparkle.md b/.changeset/large-rocks-sparkle.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/large-rocks-sparkle.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/integration-tests/src/retry.ts b/packages/integration-tests/src/retry.ts index 713d3e37a33..ba527862910 100644 --- a/packages/integration-tests/src/retry.ts +++ b/packages/integration-tests/src/retry.ts @@ -51,8 +51,8 @@ export class RetryPredicates { const didProcessExitWithError = message.includes('exit code 1'); const isKnownProcess = (message.includes('yarn add') && message.includes('aws-amplify')) || - message.includes('npm create amplify') || - message.includes('pnpm create amplify'); + /npm create ['"]?amplify/.test(message) || + /pnpm create ['"]?amplify/.test(message); return didProcessExitWithError && isKnownProcess; }; } From 3f521c32ba5312c71c1568a6997a236c79747f21 Mon Sep 17 00:00:00 2001 From: Burak Karahan Date: Wed, 8 Jan 2025 00:06:33 +0300 Subject: [PATCH 195/199] Feature: custom runtimes to define function (#1602) * feature(function): add custom provided function support to defineFunction * chore: add changeset * chore(function): fix e2ee path and changeset * chore(function): add new API.md * fix(function): fix e2ee tests for provided function Co-authored-by: Kamil Sobol * chore: change changeset Co-authored-by: Kamil Sobol * chore(function): add without docker error message Co-authored-by: Kamil Sobol * chore(function): add provided function error message Co-authored-by: Kamil Sobol * chore(function): add missing imports * chore(function): fix lint error * Update packages/backend-function/src/provided_function_factory.ts Co-authored-by: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> * Update packages/backend-function/src/provided_function_factory.ts Co-authored-by: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> * chore(function): fix lint issue --------- Co-authored-by: Kamil Sobol Co-authored-by: Amplifiyer <51211245+Amplifiyer@users.noreply.github.com> --- .changeset/long-berries-greet.md | 6 + packages/backend-function/API.md | 14 +- packages/backend-function/src/factory.test.ts | 32 ++++ packages/backend-function/src/factory.ts | 110 ++++++-------- .../src/function_construct_base.ts | 58 +++++++ packages/backend-function/src/index.ts | 2 + .../src/provided_function_factory.ts | 142 ++++++++++++++++++ .../src/resource_access_acceptor.ts | 43 ++++++ .../amplify/func-src/handler_provider.ts | 7 + .../amplify/function.ts | 16 ++ .../amplify/test_factories.ts | 2 + 11 files changed, 365 insertions(+), 67 deletions(-) create mode 100644 .changeset/long-berries-greet.md create mode 100644 packages/backend-function/src/function_construct_base.ts create mode 100644 packages/backend-function/src/provided_function_factory.ts create mode 100644 packages/backend-function/src/resource_access_acceptor.ts create mode 100644 packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_provider.ts diff --git a/.changeset/long-berries-greet.md b/.changeset/long-berries-greet.md new file mode 100644 index 00000000000..c0ad1b3e797 --- /dev/null +++ b/.changeset/long-berries-greet.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/backend-function': minor +'@aws-amplify/backend': minor +--- + +add custom provided function support to define function diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index 522b29166bd..671906009b8 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -6,8 +6,10 @@ import { AmplifyResourceGroupName } from '@aws-amplify/plugin-types'; import { BackendSecret } from '@aws-amplify/plugin-types'; +import { Construct } from 'constructs'; import { ConstructFactory } from '@aws-amplify/plugin-types'; import { FunctionResources } from '@aws-amplify/plugin-types'; +import { IFunction } from 'aws-cdk-lib/aws-lambda'; import { LogLevel } from '@aws-amplify/plugin-types'; import { LogRetention } from '@aws-amplify/plugin-types'; import { ResourceAccessAcceptorFactory } from '@aws-amplify/plugin-types'; @@ -61,8 +63,11 @@ type DataClientError = { // @public (undocumented) type DataClientReturn = T extends DataClientEnv ? DataClientConfig : DataClientError; -// @public -export const defineFunction: (props?: FunctionProps) => ConstructFactory & ResourceAccessAcceptorFactory & AddEnvironmentFactory & StackProvider>; +// @public (undocumented) +export function defineFunction(props?: FunctionProps): ConstructFactory & ResourceAccessAcceptorFactory & AddEnvironmentFactory & StackProvider>; + +// @public (undocumented) +export function defineFunction(provider: (scope: Construct) => IFunction, providerProps?: ProvidedFunctionProps): ConstructFactory & ResourceAccessAcceptorFactory & StackProvider>; // @public (undocumented) export type FunctionArchitecture = 'x86_64' | 'arm64'; @@ -135,6 +140,11 @@ type LibraryOptions = { // @public (undocumented) export type NodeVersion = 16 | 18 | 20 | 22; +// @public (undocumented) +export type ProvidedFunctionProps = { + resourceGroupName?: AmplifyResourceGroupName; +}; + // @public (undocumented) type ResourceConfig = { API: { diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index 568d77d2c37..2bf4610f941 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -24,6 +24,7 @@ import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; import fsp from 'fs/promises'; import path from 'node:path'; import { AmplifyUserError } from '@aws-amplify/platform-core'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; const createStackAndSetContext = (): Stack => { const app = new App(); @@ -782,4 +783,35 @@ void describe('AmplifyFunctionFactory', () => { ); }); }); + + void describe('provided function runtime property', () => { + void it('sets valid runtime', () => { + const lambda = defineFunction((scope) => { + return new NodejsFunction(scope, 'nodejs-provided', { + entry: + './packages/backend-function/src/test-assets/default-lambda/handler.ts', + runtime: Runtime.NODEJS_22_X, + }); + }).getInstance(getInstanceProps); + const template = Template.fromStack(lambda.stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + Runtime: Runtime.NODEJS_22_X.name, + }); + }); + + void it('provided function defaults to oldest runtime', () => { + const lambda = defineFunction((scope) => { + return new NodejsFunction(scope, 'nodejs-provided', { + entry: + './packages/backend-function/src/test-assets/default-lambda/handler.ts', + }); + }).getInstance(getInstanceProps); + const template = Template.fromStack(lambda.stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + Runtime: Runtime.NODEJS_16_X.name, + }); + }); + }); }); diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index 66ea558704b..4c1be471e29 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -1,8 +1,4 @@ -import { - FunctionOutput, - functionOutputKey, -} from '@aws-amplify/backend-output-schemas'; -import { AttributionMetadataStorage } from '@aws-amplify/backend-output-storage'; +import { FunctionOutput } from '@aws-amplify/backend-output-schemas'; import { AmplifyUserError, CallerDirectoryExtractor, @@ -20,19 +16,19 @@ import { GenerateContainerEntryProps, LogLevel, LogRetention, + ResourceAccessAcceptor, ResourceAccessAcceptorFactory, ResourceNameValidator, ResourceProvider, - SsmEnvironmentEntry, StackProvider, } from '@aws-amplify/plugin-types'; import { Duration, Size, Stack, Tags } from 'aws-cdk-lib'; import { Rule } from 'aws-cdk-lib/aws-events'; import * as targets from 'aws-cdk-lib/aws-events-targets'; -import { Policy } from 'aws-cdk-lib/aws-iam'; import { Architecture, CfnFunction, + IFunction, ILayerVersion, LayerVersion, Runtime, @@ -41,7 +37,6 @@ import { NodejsFunction, OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs'; import { Construct } from 'constructs'; import { readFileSync } from 'fs'; import { createRequire } from 'module'; -import { fileURLToPath } from 'node:url'; import { EOL } from 'os'; import * as path from 'path'; import { FunctionEnvironmentTranslator } from './function_env_translator.js'; @@ -49,8 +44,12 @@ import { FunctionEnvironmentTypeGenerator } from './function_env_type_generator. import { FunctionLayerArnParser } from './layer_parser.js'; import { convertLoggingOptionsToCDK } from './logging_options_parser.js'; import { convertFunctionSchedulesToRuleSchedules } from './schedule_parser.js'; - -const functionStackType = 'function-Lambda'; +import { + ProvidedFunctionFactory, + ProvidedFunctionProps, +} from './provided_function_factory.js'; +import { AmplifyFunctionBase } from './function_construct_base.js'; +import { FunctionResourceAccessAcceptor } from './resource_access_acceptor.js'; export type AddEnvironmentFactory = { addEnvironment: (key: string, value: string | BackendSecret) => void; @@ -74,17 +73,38 @@ export type FunctionLogLevel = Extract< >; export type FunctionLogRetention = LogRetention; -/** - * Entry point for defining a function in the Amplify ecosystem - */ -export const defineFunction = ( - props: FunctionProps = {} +export function defineFunction( + props?: FunctionProps ): ConstructFactory< ResourceProvider & ResourceAccessAcceptorFactory & AddEnvironmentFactory & StackProvider -> => new FunctionFactory(props, new Error().stack); +>; +export function defineFunction( + provider: (scope: Construct) => IFunction, + providerProps?: ProvidedFunctionProps +): ConstructFactory< + ResourceProvider & + ResourceAccessAcceptorFactory & + StackProvider +>; +/** + * Entry point for defining a function in the Amplify ecosystem + */ +// This is the "implementation overload", it's not visible in public api. +// We have to use function notation instead of arrow notation. +// Arrow notation does not support overloads. +// eslint-disable-next-line no-restricted-syntax +export function defineFunction( + propsOrProvider: FunctionProps | ((scope: Construct) => IFunction) = {}, + providerProps?: ProvidedFunctionProps +): unknown { + if (propsOrProvider && typeof propsOrProvider === 'function') { + return new ProvidedFunctionFactory(propsOrProvider, providerProps); + } + return new FunctionFactory(propsOrProvider, new Error().stack); +} export type FunctionProps = { /** @@ -507,14 +527,10 @@ class FunctionGenerator implements ConstructContainerEntryGenerator { } class AmplifyFunction - extends Construct - implements - ResourceProvider, - ResourceAccessAcceptorFactory, - AddEnvironmentFactory + extends AmplifyFunctionBase + implements AddEnvironmentFactory { readonly resources: FunctionResources; - readonly stack: Stack; private readonly functionEnvironmentTranslator: FunctionEnvironmentTranslator; constructor( scope: Construct, @@ -523,9 +539,7 @@ class AmplifyFunction backendSecretResolver: BackendSecretResolver, outputStorageStrategy: BackendOutputStorageStrategy ) { - super(scope, id); - - this.stack = Stack.of(scope); + super(scope, id, outputStorageStrategy); const runtime = nodeVersionMap[props.runtime]; @@ -647,52 +661,18 @@ class AmplifyFunction }, }; - this.storeOutput(outputStorageStrategy); - - new AttributionMetadataStorage().storeAttributionMetadata( - Stack.of(this), - functionStackType, - fileURLToPath(new URL('../package.json', import.meta.url)) - ); + this.storeOutput(); } addEnvironment = (key: string, value: string | BackendSecret) => { this.functionEnvironmentTranslator.addEnvironmentEntry(key, value); }; - getResourceAccessAcceptor = () => ({ - identifier: `${this.node.id}LambdaResourceAccessAcceptor`, - acceptResourceAccess: ( - policy: Policy, - ssmEnvironmentEntries: SsmEnvironmentEntry[] - ) => { - const role = this.resources.lambda.role; - if (!role) { - // This should never happen since we are using the Function L2 construct - throw new Error( - 'No execution role found to attach lambda permissions to' - ); - } - policy.attachToRole(role); - ssmEnvironmentEntries.forEach(({ name, path }) => { - this.functionEnvironmentTranslator.addSsmEnvironmentEntry(name, path); - }); - }, - }); - - /** - * Store storage outputs using provided strategy - */ - private storeOutput = ( - outputStorageStrategy: BackendOutputStorageStrategy - ): void => { - outputStorageStrategy.appendToBackendOutputList(functionOutputKey, { - version: '1', - payload: { - definedFunctions: this.resources.lambda.functionName, - }, - }); - }; + getResourceAccessAcceptor = (): ResourceAccessAcceptor => + new FunctionResourceAccessAcceptor( + this, + this.functionEnvironmentTranslator + ); } const isWholeNumberBetweenInclusive = ( diff --git a/packages/backend-function/src/function_construct_base.ts b/packages/backend-function/src/function_construct_base.ts new file mode 100644 index 00000000000..73de12e5dba --- /dev/null +++ b/packages/backend-function/src/function_construct_base.ts @@ -0,0 +1,58 @@ +import { Construct } from 'constructs'; +import { + BackendOutputStorageStrategy, + FunctionResources, + ResourceAccessAcceptor, + ResourceAccessAcceptorFactory, + ResourceProvider, +} from '@aws-amplify/plugin-types'; +import { Stack } from 'aws-cdk-lib'; +import { + FunctionOutput, + functionOutputKey, +} from '@aws-amplify/backend-output-schemas'; +import { AttributionMetadataStorage } from '@aws-amplify/backend-output-storage'; +import { fileURLToPath } from 'node:url'; + +const functionStackType = 'function-Lambda'; + +/** + * A base class for function constructs. + */ +export abstract class AmplifyFunctionBase + extends Construct + implements ResourceProvider, ResourceAccessAcceptorFactory +{ + readonly stack: Stack; + abstract resources: FunctionResources; + + abstract getResourceAccessAcceptor: () => ResourceAccessAcceptor; + + /** + * Creates base function construct. + */ + protected constructor( + scope: Construct, + id: string, + private readonly outputStorageStrategy: BackendOutputStorageStrategy + ) { + super(scope, id); + + this.stack = Stack.of(scope); + + new AttributionMetadataStorage().storeAttributionMetadata( + Stack.of(this), + functionStackType, + fileURLToPath(new URL('../package.json', import.meta.url)) + ); + } + + protected storeOutput = (): void => { + this.outputStorageStrategy.appendToBackendOutputList(functionOutputKey, { + version: '1', + payload: { + definedFunctions: this.resources.lambda.functionName, + }, + }); + }; +} diff --git a/packages/backend-function/src/index.ts b/packages/backend-function/src/index.ts index cb95b1ef8d8..8d9b853ad39 100644 --- a/packages/backend-function/src/index.ts +++ b/packages/backend-function/src/index.ts @@ -1 +1,3 @@ export * from './factory.js'; +import { ProvidedFunctionProps } from './provided_function_factory.js'; +export { ProvidedFunctionProps }; diff --git a/packages/backend-function/src/provided_function_factory.ts b/packages/backend-function/src/provided_function_factory.ts new file mode 100644 index 00000000000..2d6becb0117 --- /dev/null +++ b/packages/backend-function/src/provided_function_factory.ts @@ -0,0 +1,142 @@ +import { + AmplifyFunction, + AmplifyResourceGroupName, + BackendOutputStorageStrategy, + ConstructContainerEntryGenerator, + ConstructFactory, + ConstructFactoryGetInstanceProps, + FunctionResources, + GenerateContainerEntryProps, + ResourceAccessAcceptor, +} from '@aws-amplify/plugin-types'; +import { Construct } from 'constructs'; +import { CfnFunction, IFunction } from 'aws-cdk-lib/aws-lambda'; +import { FunctionOutput } from '@aws-amplify/backend-output-schemas'; +import { Tags } from 'aws-cdk-lib'; +import { AmplifyUserError, TagName } from '@aws-amplify/platform-core'; +import { AmplifyFunctionBase } from './function_construct_base.js'; +import { FunctionResourceAccessAcceptor } from './resource_access_acceptor.js'; + +export type ProvidedFunctionProps = { + /** + * Group the function with existing Amplify resources or separate the function into its own group. + * @default 'function' // grouping with other Amplify functions + * @example + * resourceGroupName: 'auth' // to group an auth trigger with an auth resource + */ + resourceGroupName?: AmplifyResourceGroupName; +}; + +/** + * Adapts provided CDK function as Amplify function. + */ +export class ProvidedFunctionFactory + implements ConstructFactory +{ + private generator: ConstructContainerEntryGenerator; + + /** + * Creates provided function factory. + */ + constructor( + private readonly functionProvider: (scope: Construct) => IFunction, + private readonly props?: ProvidedFunctionProps + ) {} + + /** + * Creates a function instance. + */ + getInstance(props: ConstructFactoryGetInstanceProps): AmplifyFunction { + if (!this.generator) { + this.generator = new ProvidedFunctionGenerator( + this.functionProvider, + props.outputStorageStrategy, + this.props + ); + } + return props.constructContainer.getOrCompute( + this.generator + ) as AmplifyFunction; + } +} + +class ProvidedFunctionGenerator implements ConstructContainerEntryGenerator { + readonly resourceGroupName: AmplifyResourceGroupName; + + constructor( + private readonly functionProvider: (scope: Construct) => IFunction, + private readonly outputStorageStrategy: BackendOutputStorageStrategy, + props?: ProvidedFunctionProps + ) { + this.resourceGroupName = props?.resourceGroupName ?? 'function'; + } + + generateContainerEntry = ({ scope }: GenerateContainerEntryProps) => { + let providedFunction: IFunction; + try { + providedFunction = this.functionProvider(scope); + } catch (e) { + if ( + e instanceof Error && + (e.message.includes('docker exited with status 1') || + e.message.includes('docker ENOENT')) + ) { + throw new AmplifyUserError( + 'CustomFunctionProviderDockerError', + { + message: 'Failed to instantiate custom function provider', + resolution: + 'See https://docs.amplify.aws/react/build-a-backend/functions/custom-functions for more details about current limitations and troubleshooting steps.', + }, + e + ); + } else { + throw new AmplifyUserError( + 'CustomFunctionProviderError', + { + message: 'Failed to instantiate custom function provider', + resolution: + 'Check the definition of your custom function provided in `defineFunction` and refer to the logs for more information. See https://docs.amplify.aws/react/build-a-backend/functions/custom-functions for more details.', + }, + e instanceof Error ? e : undefined + ); + } + } + return new ProvidedAmplifyFunction( + scope, + `${providedFunction.node.id}-provided`, + this.outputStorageStrategy, + providedFunction + ); + }; +} + +class ProvidedAmplifyFunction extends AmplifyFunctionBase { + readonly resources: FunctionResources; + constructor( + scope: Construct, + id: string, + outputStorageStrategy: BackendOutputStorageStrategy, + providedFunction: IFunction + ) { + super(scope, id, outputStorageStrategy); + + const cfnFunction = providedFunction.node.findChild( + 'Resource' + ) as CfnFunction; + + Tags.of(cfnFunction).add(TagName.FRIENDLY_NAME, providedFunction.node.id); + + this.resources = { + lambda: providedFunction, + cfnResources: { + cfnFunction, + }, + }; + + this.storeOutput(); + } + + getResourceAccessAcceptor = (): ResourceAccessAcceptor => + new FunctionResourceAccessAcceptor(this); +} diff --git a/packages/backend-function/src/resource_access_acceptor.ts b/packages/backend-function/src/resource_access_acceptor.ts new file mode 100644 index 00000000000..e3ba57aaf66 --- /dev/null +++ b/packages/backend-function/src/resource_access_acceptor.ts @@ -0,0 +1,43 @@ +import { + ResourceAccessAcceptor, + SsmEnvironmentEntry, +} from '@aws-amplify/plugin-types'; +import { FunctionEnvironmentTranslator } from './function_env_translator.js'; +import { AmplifyFunctionBase } from './function_construct_base.js'; +import { Policy } from 'aws-cdk-lib/aws-iam'; + +/** + * A function resource acceptor. + */ +export class FunctionResourceAccessAcceptor implements ResourceAccessAcceptor { + readonly identifier: string; + + /** + * Creates function resource acceptor. + */ + constructor( + private readonly func: AmplifyFunctionBase, + private readonly functionEnvironmentTranslator?: FunctionEnvironmentTranslator + ) { + this.identifier = `${func.node.id}LambdaResourceAccessAcceptor`; + } + + acceptResourceAccess = ( + policy: Policy, + ssmEnvironmentEntries: SsmEnvironmentEntry[] + ) => { + const role = this.func.resources.lambda.role; + if (!role) { + // This should never happen since we are using the Function L2 construct + throw new Error( + 'No execution role found to attach lambda permissions to' + ); + } + policy.attachToRole(role); + if (this.functionEnvironmentTranslator) { + for (const { name, path } of ssmEnvironmentEntries) { + this.functionEnvironmentTranslator.addSsmEnvironmentEntry(name, path); + } + } + }; +} diff --git a/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_provider.ts b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_provider.ts new file mode 100644 index 00000000000..f7c71c983ca --- /dev/null +++ b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/func-src/handler_provider.ts @@ -0,0 +1,7 @@ +/** + * This function is a simple hello world function. + * It's for not direct defineFunction, it's provided function + */ +export const handler = async () => { + return 'Hello from NodeJS Function!'; +}; diff --git a/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/function.ts b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/function.ts index a4f30ef66f1..8784959dffc 100644 --- a/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/function.ts +++ b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/function.ts @@ -1,4 +1,8 @@ import { defineFunction } from '@aws-amplify/backend'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { Runtime } from 'aws-cdk-lib/aws-lambda'; +import * as path from 'path'; +import { fileURLToPath } from 'url'; export const funcWithSsm = defineFunction({ name: 'funcWithSsm', @@ -24,6 +28,18 @@ export const funcNoMinify = defineFunction({ }, }); +export const funcProvided = defineFunction((scope) => { + return new NodejsFunction(scope, 'funcProvided', { + entry: path.resolve( + fileURLToPath(import.meta.url), + '..', + 'func-src', + 'handler_provider.ts' + ), + runtime: Runtime.NODEJS_18_X, + }); +}); + export const funcCustomEmailSender = defineFunction({ name: 'funcCustomEmailSender', entry: './func-src/handler_custom_email_sender.ts', diff --git a/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/test_factories.ts b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/test_factories.ts index 4e72f34424b..4841663dc03 100644 --- a/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/test_factories.ts +++ b/packages/integration-tests/src/test-projects/advanced-auth-and-functions/amplify/test_factories.ts @@ -4,6 +4,7 @@ import { funcWithAwsSdk, funcWithSchedule, funcWithSsm, + funcProvided, } from './function.js'; import { auth } from './auth/resource.js'; @@ -14,4 +15,5 @@ export const authAndFunctions = { funcWithSchedule, funcNoMinify, funcCustomEmailSender, + funcProvided, }; From d46024ef911a367b0d5f4f42e746d20480b79dc3 Mon Sep 17 00:00:00 2001 From: Kamil Sobol <5849952+sobolk@users.noreply.github.com> Date: Tue, 7 Jan 2025 14:13:04 -0800 Subject: [PATCH 196/199] Log conversation handler streaming progress (#2412) * Log conversation handler streaming progress * pr feedback --- .changeset/three-adults-breathe.md | 5 + .../runtime/bedrock_converse_adapter.test.ts | 65 ++++++++ .../runtime/bedrock_converse_adapter.ts | 151 ++++++++++-------- 3 files changed, 152 insertions(+), 69 deletions(-) create mode 100644 .changeset/three-adults-breathe.md diff --git a/.changeset/three-adults-breathe.md b/.changeset/three-adults-breathe.md new file mode 100644 index 00000000000..5744d86934e --- /dev/null +++ b/.changeset/three-adults-breathe.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/ai-constructs': patch +--- + +Log streaming progress diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts index b3a99ed1a0b..26d261b292c 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.test.ts @@ -905,6 +905,71 @@ void describe('Bedrock converse adapter', () => { assert.deepStrictEqual(toolExecuteMock.mock.calls[1].arguments[0], {}); }); + void it('logs streaming progress sparsely', async () => { + const event: ConversationTurnEvent = { + ...commonEvent, + }; + + const bedrockClient = new BedrockRuntimeClient(); + const bedrockResponseQueue: Array< + ConverseCommandOutput | ConverseStreamCommandOutput + > = []; + const numberOfBlocks = 512; + const content = Array.from({ length: numberOfBlocks }, (v, k) => { + return { + text: `block${k}`, + }; + }); + const bedrockResponse = mockBedrockResponse(content, true); + bedrockResponse.$metadata.requestId = 'testRequestId'; + bedrockResponseQueue.push(bedrockResponse); + + mock.method(bedrockClient, 'send', () => + Promise.resolve(bedrockResponseQueue.shift()) + ); + + const consoleInfoMock = mock.fn<(data: string) => void>(); + const consoleErrorMock = mock.fn<(data: string) => void>(); + const consoleLogMock = mock.fn<(data: string) => void>(); + const consoleDebugMock = mock.fn<(data: string) => void>(); + const consoleMock = { + info: consoleInfoMock, + error: consoleErrorMock, + log: consoleLogMock, + debug: consoleDebugMock, + } as unknown as Console; + const adapter = new BedrockConverseAdapter( + event, + [], + bedrockClient, + undefined, + messageHistoryRetriever, + undefined, + consoleMock + ); + + await askBedrockWithStreaming(adapter); + + const progressCalls = consoleInfoMock.mock.calls.filter((call) => + call.arguments[0].includes('chunks from Bedrock Converse Stream response') + ); + assert.strictEqual(progressCalls.length, 3); + assert.strictEqual( + progressCalls[0].arguments[0], + 'Processed 1000 chunks from Bedrock Converse Stream response, requestId=testRequestId' + ); + assert.strictEqual( + progressCalls[1].arguments[0], + 'Processed 2000 chunks from Bedrock Converse Stream response, requestId=testRequestId' + ); + // each block is decomposed into 4 chunks + start and stop of whole message. + const expectedNumberOfAllChunks = numberOfBlocks * 4 + 2; + assert.strictEqual( + progressCalls[2].arguments[0], + `Completed processing ${expectedNumberOfAllChunks.toString()} chunks from Bedrock Converse Stream response, requestId=testRequestId` + ); + }); + void it('throws if tool is duplicated', () => { assert.throws( () => diff --git a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts index 0b692a848af..ae9b01118eb 100644 --- a/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts +++ b/packages/ai-constructs/src/conversation/runtime/bedrock_converse_adapter.ts @@ -213,88 +213,101 @@ export class BedrockConverseAdapter { content: [], }; - for await (const chunk of bedrockResponse.stream) { - this.logger.debug('Bedrock Converse Stream response chunk:', chunk); - if (chunk.messageStart) { - accumulatedAssistantMessage.role = chunk.messageStart.role; - } else if (chunk.contentBlockStart) { - blockDeltaIndex = 0; - lastBlockDeltaIndex = 0; - if (chunk.contentBlockStart.start?.toolUse) { - toolUseBlock = { - toolUse: { - ...chunk.contentBlockStart.start?.toolUse, - input: undefined, - }, - }; - } - } else if (chunk.contentBlockDelta) { - if (chunk.contentBlockDelta.delta?.toolUse) { - if (!chunk.contentBlockDelta.delta.toolUse.input) { - toolUseInput = ''; - } else { - toolUseInput += chunk.contentBlockDelta.delta.toolUse.input; + let processedBedrockChunks = 0; + try { + for await (const chunk of bedrockResponse.stream) { + this.logger.debug('Bedrock Converse Stream response chunk:', chunk); + if (chunk.messageStart) { + accumulatedAssistantMessage.role = chunk.messageStart.role; + } else if (chunk.contentBlockStart) { + blockDeltaIndex = 0; + lastBlockDeltaIndex = 0; + if (chunk.contentBlockStart.start?.toolUse) { + toolUseBlock = { + toolUse: { + ...chunk.contentBlockStart.start?.toolUse, + input: undefined, + }, + }; } - } else if (chunk.contentBlockDelta.delta?.text) { - text += chunk.contentBlockDelta.delta.text; - yield { - accumulatedTurnContent: [...accumulatedTurnContent, { text }], - conversationId: this.event.conversationId, - associatedUserMessageId: this.event.currentMessageId, - contentBlockText: chunk.contentBlockDelta.delta.text, - contentBlockIndex: blockIndex, - contentBlockDeltaIndex: blockDeltaIndex, - }; - lastBlockDeltaIndex = blockDeltaIndex; - blockDeltaIndex++; - } - } else if (chunk.contentBlockStop) { - if (toolUseBlock) { - if (toolUseInput) { - toolUseBlock.toolUse.input = JSON.parse(toolUseInput); - } else { - // Bedrock API requires tool input to be non-null in message history. - // Therefore, falling back to empty object. - toolUseBlock.toolUse.input = {}; + } else if (chunk.contentBlockDelta) { + if (chunk.contentBlockDelta.delta?.toolUse) { + if (!chunk.contentBlockDelta.delta.toolUse.input) { + toolUseInput = ''; + } else { + toolUseInput += chunk.contentBlockDelta.delta.toolUse.input; + } + } else if (chunk.contentBlockDelta.delta?.text) { + text += chunk.contentBlockDelta.delta.text; + yield { + accumulatedTurnContent: [...accumulatedTurnContent, { text }], + conversationId: this.event.conversationId, + associatedUserMessageId: this.event.currentMessageId, + contentBlockText: chunk.contentBlockDelta.delta.text, + contentBlockIndex: blockIndex, + contentBlockDeltaIndex: blockDeltaIndex, + }; + lastBlockDeltaIndex = blockDeltaIndex; + blockDeltaIndex++; } - accumulatedAssistantMessage.content?.push(toolUseBlock); - if ( - toolUseBlock.toolUse.name && - this.clientToolByName.has(toolUseBlock.toolUse.name) - ) { - clientToolsRequested = true; - accumulatedTurnContent.push(toolUseBlock); + } else if (chunk.contentBlockStop) { + if (toolUseBlock) { + if (toolUseInput) { + toolUseBlock.toolUse.input = JSON.parse(toolUseInput); + } else { + // Bedrock API requires tool input to be non-null in message history. + // Therefore, falling back to empty object. + toolUseBlock.toolUse.input = {}; + } + accumulatedAssistantMessage.content?.push(toolUseBlock); + if ( + toolUseBlock.toolUse.name && + this.clientToolByName.has(toolUseBlock.toolUse.name) + ) { + clientToolsRequested = true; + accumulatedTurnContent.push(toolUseBlock); + yield { + accumulatedTurnContent: [...accumulatedTurnContent], + conversationId: this.event.conversationId, + associatedUserMessageId: this.event.currentMessageId, + contentBlockIndex: blockIndex, + contentBlockToolUse: JSON.stringify(toolUseBlock), + }; + lastBlockIndex = blockIndex; + blockIndex++; + } + toolUseBlock = undefined; + toolUseInput = ''; + } else { + accumulatedAssistantMessage.content?.push({ + text, + }); + accumulatedTurnContent.push({ text }); yield { accumulatedTurnContent: [...accumulatedTurnContent], conversationId: this.event.conversationId, associatedUserMessageId: this.event.currentMessageId, contentBlockIndex: blockIndex, - contentBlockToolUse: JSON.stringify(toolUseBlock), + contentBlockDoneAtIndex: lastBlockDeltaIndex, }; + text = ''; lastBlockIndex = blockIndex; blockIndex++; } - toolUseBlock = undefined; - toolUseInput = ''; - } else { - accumulatedAssistantMessage.content?.push({ - text, - }); - accumulatedTurnContent.push({ text }); - yield { - accumulatedTurnContent: [...accumulatedTurnContent], - conversationId: this.event.conversationId, - associatedUserMessageId: this.event.currentMessageId, - contentBlockIndex: blockIndex, - contentBlockDoneAtIndex: lastBlockDeltaIndex, - }; - text = ''; - lastBlockIndex = blockIndex; - blockIndex++; + } else if (chunk.messageStop) { + stopReason = chunk.messageStop.stopReason ?? ''; + } + processedBedrockChunks++; + if (processedBedrockChunks % 1000 === 0) { + this.logger.info( + `Processed ${processedBedrockChunks} chunks from Bedrock Converse Stream response, requestId=${bedrockResponse.$metadata.requestId}` + ); } - } else if (chunk.messageStop) { - stopReason = chunk.messageStop.stopReason ?? ''; } + } finally { + this.logger.info( + `Completed processing ${processedBedrockChunks} chunks from Bedrock Converse Stream response, requestId=${bedrockResponse.$metadata.requestId}` + ); } this.logger.debug( 'Accumulated Bedrock Converse Stream response:', From c5d54c2bd5ea5fa34661725f1f9a6818b9faa2e2 Mon Sep 17 00:00:00 2001 From: "Aaron S." <94858815+stocaaro@users.noreply.github.com> Date: Tue, 7 Jan 2025 16:13:44 -0600 Subject: [PATCH 197/199] fix: Update getAmplifyDataClient to have strict env type and remove narrowing logic (#2409) --- .changeset/forty-tips-turn.md | 6 ++ package-lock.json | 80 +++++++++---------- packages/backend-function/API.md | 19 +---- .../get_amplify_clients_configuration.test.ts | 46 +---------- .../get_amplify_clients_configuration.ts | 43 +--------- .../backend-function/src/runtime/index.ts | 3 - 6 files changed, 52 insertions(+), 145 deletions(-) create mode 100644 .changeset/forty-tips-turn.md diff --git a/.changeset/forty-tips-turn.md b/.changeset/forty-tips-turn.md new file mode 100644 index 00000000000..2096230470c --- /dev/null +++ b/.changeset/forty-tips-turn.md @@ -0,0 +1,6 @@ +--- +'@aws-amplify/backend': patch +'@aws-amplify/backend-function': patch +--- + +Update getAmplifyDataClient to have strict env type and remove narrowing logic diff --git a/package-lock.json b/package-lock.json index 889039955a9..3f7db738980 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33635,20 +33635,20 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.11.0", + "version": "1.12.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-auth": "^1.4.2", - "@aws-amplify/backend-data": "^1.3.0", - "@aws-amplify/backend-function": "^1.10.0", + "@aws-amplify/backend-data": "^1.4.0", + "@aws-amplify/backend-function": "^1.11.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.4", - "@aws-amplify/client-config": "^1.5.4", + "@aws-amplify/client-config": "^1.5.5", "@aws-amplify/data-schema": "^1.13.4", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-amplify": "^3.624.0" }, "devDependencies": { @@ -33663,15 +33663,15 @@ }, "packages/backend-ai": { "name": "@aws-amplify/backend-ai", - "version": "1.2.0", + "version": "1.3.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/ai-constructs": "^1.2.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/data-schema-types": "^1.2.0", - "@aws-amplify/platform-core": "^1.3.0", - "@aws-amplify/plugin-types": "^1.6.0" + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", @@ -33703,7 +33703,7 @@ }, "packages/backend-data": { "name": "@aws-amplify/backend-data", - "version": "1.3.0", + "version": "1.4.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", @@ -33711,12 +33711,12 @@ "@aws-amplify/data-construct": "^1.14.5", "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/graphql-generator": "^0.5.1", - "@aws-amplify/plugin-types": "^1.6.0" + "@aws-amplify/plugin-types": "^1.7.0" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.7", "@aws-amplify/data-schema": "^1.13.4", - "@aws-amplify/platform-core": "^1.4.0" + "@aws-amplify/platform-core": "^1.5.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", @@ -33725,11 +33725,11 @@ }, "packages/backend-deployer": { "name": "@aws-amplify/backend-deployer", - "version": "1.1.12", + "version": "1.1.13", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "execa": "^9.5.1", "strip-ansi": "^6.0.1", "tsx": "^4.6.1" @@ -33859,18 +33859,18 @@ }, "packages/backend-function": { "name": "@aws-amplify/backend-function", - "version": "1.10.0", + "version": "1.11.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-s3": "^3.624.0", "execa": "^9.5.1" }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.7", - "@aws-amplify/platform-core": "^1.4.0", + "@aws-amplify/platform-core": "^1.5.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", @@ -34080,20 +34080,20 @@ }, "packages/cli": { "name": "@aws-amplify/backend-cli", - "version": "1.4.5", + "version": "1.4.6", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.12", + "@aws-amplify/backend-deployer": "^1.1.13", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.1", - "@aws-amplify/client-config": "^1.5.4", + "@aws-amplify/client-config": "^1.5.5", "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/form-generator": "^1.0.3", - "@aws-amplify/model-generator": "^1.0.11", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", - "@aws-amplify/sandbox": "^1.2.8", + "@aws-amplify/model-generator": "^1.0.12", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", + "@aws-amplify/sandbox": "^1.2.9", "@aws-amplify/schema-generator": "^1.2.6", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", @@ -34470,14 +34470,14 @@ }, "packages/client-config": { "name": "@aws-amplify/client-config", - "version": "1.5.4", + "version": "1.5.5", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/deployed-backend-client": "^1.5.0", - "@aws-amplify/model-generator": "^1.0.10", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/model-generator": "^1.0.12", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "zod": "^3.22.2" }, "devDependencies": { @@ -34961,15 +34961,15 @@ }, "packages/model-generator": { "name": "@aws-amplify/model-generator", - "version": "1.0.11", + "version": "1.0.12", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.1.0", "@aws-amplify/deployed-backend-client": "^1.5.0", "@aws-amplify/graphql-generator": "^0.5.1", "@aws-amplify/graphql-types-generator": "^3.6.0", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-appsync": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/credential-providers": "^3.624.0", @@ -34983,10 +34983,10 @@ }, "packages/platform-core": { "name": "@aws-amplify/platform-core", - "version": "1.4.0", + "version": "1.5.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-sts": "^3.624.0", "is-ci": "^3.0.1", "lodash.mergewith": "^4.6.2", @@ -35020,7 +35020,7 @@ }, "packages/plugin-types": { "name": "@aws-amplify/plugin-types", - "version": "1.6.0", + "version": "1.7.0", "license": "Apache-2.0", "peerDependencies": { "@aws-sdk/types": "^3.609.0", @@ -35030,16 +35030,16 @@ }, "packages/sandbox": { "name": "@aws-amplify/sandbox", - "version": "1.2.8", + "version": "1.2.9", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.12", + "@aws-amplify/backend-deployer": "^1.1.13", "@aws-amplify/backend-secret": "^1.1.2", "@aws-amplify/cli-core": "^1.2.1", - "@aws-amplify/client-config": "^1.5.4", + "@aws-amplify/client-config": "^1.5.5", "@aws-amplify/deployed-backend-client": "^1.5.0", - "@aws-amplify/platform-core": "^1.4.0", - "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index 671906009b8..e2a6f120607 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -22,9 +22,6 @@ declare namespace __export__runtime { getAmplifyDataClientConfig, DataClientConfig, DataClientEnv, - DataClientError, - DataClientReturn, - InvalidConfig, LibraryOptions, ResourceConfig } @@ -54,15 +51,6 @@ type DataClientEnv = { AMPLIFY_DATA_DEFAULT_NAME: string; } & Record; -// @public (undocumented) -type DataClientError = { - resourceConfig: InvalidConfig; - libraryOptions: InvalidConfig; -}; - -// @public (undocumented) -type DataClientReturn = T extends DataClientEnv ? DataClientConfig : DataClientError; - // @public (undocumented) export function defineFunction(props?: FunctionProps): ConstructFactory & ResourceAccessAcceptorFactory & AddEnvironmentFactory & StackProvider>; @@ -114,12 +102,7 @@ export type FunctionProps = { export type FunctionSchedule = TimeInterval | CronSchedule; // @public -const getAmplifyDataClientConfig: (env: T, s3Client?: S3Client) => Promise>; - -// @public (undocumented) -type InvalidConfig = unknown & { - invalidType: 'Some of the AWS environment variables needed to configure Amplify are missing.'; -}; +const getAmplifyDataClientConfig: (env: DataClientEnv, s3Client?: S3Client) => Promise; // @public (undocumented) type LibraryOptions = { diff --git a/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts b/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts index 1287b11922d..c39d45106d0 100644 --- a/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts +++ b/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts @@ -41,58 +41,14 @@ void describe('getAmplifyDataClientConfig', () => { [ { name: 'no set name', - dataBackendName: 'AMPLIFY_DATA', validEnv: validDefaultEnv, }, { name: 'an explicit name', - dataBackendName: 'AMPLIFY_DATA_TEST_NAME', validEnv: validNamedEnv, }, - ].forEach(({ name, dataBackendName, validEnv }) => { + ].forEach(({ name, validEnv }) => { void describe(`env variable with ${name} for the data backend`, () => { - Object.keys(validEnv) - .filter((k) => k !== 'AMPLIFY_DATA_DEFAULT_NAME') - .forEach((envFieldToExclude) => { - if (envFieldToExclude.includes(dataBackendName)) { - void it(`throws error when ${envFieldToExclude} is not included`, async () => { - const env = { ...validEnv } as Record; - delete env[envFieldToExclude]; - await assert.rejects( - async () => await getAmplifyDataClientConfig(env), - /The data environment variables are malformed/ - ); - }); - - void it(`throws error when ${envFieldToExclude} is not a string`, async () => { - const env = { ...validEnv } as Record; - env[envFieldToExclude] = 123; - await assert.rejects( - async () => await getAmplifyDataClientConfig(env), - /The data environment variables are malformed/ - ); - }); - } else { - void it(`returns empty config objects when ${envFieldToExclude} is not included`, async () => { - const env = { ...validEnv } as Record; - delete env[envFieldToExclude]; - assert.deepEqual(await getAmplifyDataClientConfig(env), { - resourceConfig: {}, - libraryOptions: {}, - }); - }); - - void it(`returns empty config objects when ${envFieldToExclude} is not a string`, async () => { - const env = { ...validEnv } as Record; - env[envFieldToExclude] = 123; - assert.deepEqual(await getAmplifyDataClientConfig(env), { - resourceConfig: {}, - libraryOptions: {}, - }); - }); - } - }); - void it('raises a custom error message when the model introspection schema is missing from the s3 bucket', async () => { const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { throw new NoSuchKey({ message: 'TEST_ERROR', $metadata: {} }); diff --git a/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts index 8c179fdf57c..51e1132ea7a 100644 --- a/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts +++ b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts @@ -28,21 +28,6 @@ type DataEnvExtension = { type ExtendedAmplifyClientEnv = DataClientEnv & DataEnvExtension; -const isAmplifyClientEnv = (env: object): env is DataClientEnv => { - return ( - 'AWS_ACCESS_KEY_ID' in env && - typeof env.AWS_ACCESS_KEY_ID === 'string' && - 'AWS_SECRET_ACCESS_KEY' in env && - typeof env.AWS_SECRET_ACCESS_KEY === 'string' && - 'AWS_SESSION_TOKEN' in env && - typeof env.AWS_SESSION_TOKEN === 'string' && - 'AWS_REGION' in env && - typeof env.AWS_REGION === 'string' && - 'AMPLIFY_DATA_DEFAULT_NAME' in env && - typeof env.AMPLIFY_DATA_DEFAULT_NAME === 'string' - ); -}; - /* eslint-disable @typescript-eslint/naming-convention */ export type ResourceConfig = { API: { @@ -110,24 +95,11 @@ const getLibraryOptions = (env: DataClientEnv): LibraryOptions => { }; }; -export type InvalidConfig = unknown & { - invalidType: 'Some of the AWS environment variables needed to configure Amplify are missing.'; -}; - -export type DataClientError = { - resourceConfig: InvalidConfig; - libraryOptions: InvalidConfig; -}; - export type DataClientConfig = { resourceConfig: ResourceConfig; libraryOptions: LibraryOptions; }; -export type DataClientReturn = T extends DataClientEnv - ? DataClientConfig - : DataClientError; - const extendEnv = ( env: DataClientEnv & Record, dataName: string @@ -171,20 +143,13 @@ const extendEnv = ( * @param env - The environment variables for the data client * @returns An object containing the `resourceConfig` and `libraryOptions` */ -export const getAmplifyDataClientConfig = async ( - env: T, +export const getAmplifyDataClientConfig = async ( + env: DataClientEnv, s3Client?: S3Client -): Promise> => { +): Promise => { if (!s3Client) { s3Client = new S3Client(); } - if (env === null || typeof env !== 'object') { - throw new Error(`Invalid environment variables: ${JSON.stringify(env)}`); - } - - if (!isAmplifyClientEnv(env)) { - return { resourceConfig: {}, libraryOptions: {} } as DataClientReturn; - } const dataName = new NamingConverter().toScreamingSnakeCase( env.AMPLIFY_DATA_DEFAULT_NAME @@ -224,5 +189,5 @@ export const getAmplifyDataClientConfig = async ( modelIntrospectionSchema ); - return { resourceConfig, libraryOptions } as DataClientReturn; + return { resourceConfig, libraryOptions }; }; diff --git a/packages/backend-function/src/runtime/index.ts b/packages/backend-function/src/runtime/index.ts index 07edc4c883b..675c3bee51d 100644 --- a/packages/backend-function/src/runtime/index.ts +++ b/packages/backend-function/src/runtime/index.ts @@ -2,9 +2,6 @@ export { getAmplifyDataClientConfig, DataClientConfig, DataClientEnv, - DataClientError, - DataClientReturn, - InvalidConfig, LibraryOptions, ResourceConfig, } from './get_amplify_clients_configuration.js'; From 22a286384e27fae69e3249c49fd46e7961080e7a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 16:16:55 +0100 Subject: [PATCH 198/199] Version Packages (#2408) Co-authored-by: github-actions[bot] --- .changeset/forty-tips-turn.md | 6 ------ .changeset/large-rocks-sparkle.md | 2 -- .changeset/long-berries-greet.md | 6 ------ .changeset/metal-taxis-sing.md | 5 ----- .changeset/three-adults-breathe.md | 5 ----- packages/ai-constructs/CHANGELOG.md | 8 ++++++++ packages/ai-constructs/package.json | 4 ++-- packages/backend-function/CHANGELOG.md | 10 ++++++++++ packages/backend-function/package.json | 4 ++-- packages/backend/CHANGELOG.md | 15 +++++++++++++++ packages/backend/package.json | 6 +++--- packages/platform-core/CHANGELOG.md | 6 ++++++ packages/platform-core/package.json | 2 +- 13 files changed, 47 insertions(+), 32 deletions(-) delete mode 100644 .changeset/forty-tips-turn.md delete mode 100644 .changeset/large-rocks-sparkle.md delete mode 100644 .changeset/long-berries-greet.md delete mode 100644 .changeset/metal-taxis-sing.md delete mode 100644 .changeset/three-adults-breathe.md diff --git a/.changeset/forty-tips-turn.md b/.changeset/forty-tips-turn.md deleted file mode 100644 index 2096230470c..00000000000 --- a/.changeset/forty-tips-turn.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/backend': patch -'@aws-amplify/backend-function': patch ---- - -Update getAmplifyDataClient to have strict env type and remove narrowing logic diff --git a/.changeset/large-rocks-sparkle.md b/.changeset/large-rocks-sparkle.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/large-rocks-sparkle.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/long-berries-greet.md b/.changeset/long-berries-greet.md deleted file mode 100644 index c0ad1b3e797..00000000000 --- a/.changeset/long-berries-greet.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@aws-amplify/backend-function': minor -'@aws-amplify/backend': minor ---- - -add custom provided function support to define function diff --git a/.changeset/metal-taxis-sing.md b/.changeset/metal-taxis-sing.md deleted file mode 100644 index 20b19e1886b..00000000000 --- a/.changeset/metal-taxis-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/platform-core': patch ---- - -Base64 encode serialized Amplify Errors diff --git a/.changeset/three-adults-breathe.md b/.changeset/three-adults-breathe.md deleted file mode 100644 index 5744d86934e..00000000000 --- a/.changeset/three-adults-breathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@aws-amplify/ai-constructs': patch ---- - -Log streaming progress diff --git a/packages/ai-constructs/CHANGELOG.md b/packages/ai-constructs/CHANGELOG.md index e5726bb44cd..3f125eb1905 100644 --- a/packages/ai-constructs/CHANGELOG.md +++ b/packages/ai-constructs/CHANGELOG.md @@ -1,5 +1,13 @@ # @aws-amplify/ai-constructs +## 1.2.1 + +### Patch Changes + +- d46024e: Log streaming progress +- Updated dependencies [a712983] + - @aws-amplify/platform-core@1.5.1 + ## 1.2.0 ### Minor Changes diff --git a/packages/ai-constructs/package.json b/packages/ai-constructs/package.json index d8692ff3bac..55944d7ced9 100644 --- a/packages/ai-constructs/package.json +++ b/packages/ai-constructs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/ai-constructs", - "version": "1.2.0", + "version": "1.2.1", "type": "commonjs", "publishConfig": { "access": "public" @@ -27,7 +27,7 @@ "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/platform-core": "^1.5.1", "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-bedrock-runtime": "^3.622.0", "@smithy/types": "^3.3.0", diff --git a/packages/backend-function/CHANGELOG.md b/packages/backend-function/CHANGELOG.md index 8db2202c77e..2305b3c4879 100644 --- a/packages/backend-function/CHANGELOG.md +++ b/packages/backend-function/CHANGELOG.md @@ -1,5 +1,15 @@ # @aws-amplify/backend-function +## 1.12.0 + +### Minor Changes + +- 3f521c3: add custom provided function support to define function + +### Patch Changes + +- c5d54c2: Update getAmplifyDataClient to have strict env type and remove narrowing logic + ## 1.11.0 ### Minor Changes diff --git a/packages/backend-function/package.json b/packages/backend-function/package.json index a8dcf94e8fc..71a545b1331 100644 --- a/packages/backend-function/package.json +++ b/packages/backend-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend-function", - "version": "1.11.0", + "version": "1.12.0", "type": "module", "publishConfig": { "access": "public" @@ -32,7 +32,7 @@ }, "devDependencies": { "@aws-amplify/backend-platform-test-stubs": "^0.3.7", - "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/platform-core": "^1.5.1", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 81696e3bbb3..08939e209b4 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @aws-amplify/backend +## 1.13.0 + +### Minor Changes + +- 3f521c3: add custom provided function support to define function + +### Patch Changes + +- c5d54c2: Update getAmplifyDataClient to have strict env type and remove narrowing logic +- Updated dependencies [c5d54c2] +- Updated dependencies [3f521c3] +- Updated dependencies [a712983] + - @aws-amplify/backend-function@1.12.0 + - @aws-amplify/platform-core@1.5.1 + ## 1.12.0 ### Minor Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 5190db01efb..c021c33d880 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/backend", - "version": "1.12.0", + "version": "1.13.0", "type": "module", "publishConfig": { "access": "public" @@ -32,14 +32,14 @@ "dependencies": { "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/backend-auth": "^1.4.2", - "@aws-amplify/backend-function": "^1.11.0", + "@aws-amplify/backend-function": "^1.12.0", "@aws-amplify/backend-data": "^1.4.0", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/backend-secret": "^1.1.4", "@aws-amplify/backend-storage": "^1.2.4", "@aws-amplify/client-config": "^1.5.5", - "@aws-amplify/platform-core": "^1.5.0", + "@aws-amplify/platform-core": "^1.5.1", "@aws-amplify/plugin-types": "^1.7.0", "@aws-sdk/client-amplify": "^3.624.0" }, diff --git a/packages/platform-core/CHANGELOG.md b/packages/platform-core/CHANGELOG.md index d824c13fcb1..95f5ed46126 100644 --- a/packages/platform-core/CHANGELOG.md +++ b/packages/platform-core/CHANGELOG.md @@ -1,5 +1,11 @@ # @aws-amplify/platform-core +## 1.5.1 + +### Patch Changes + +- a712983: Base64 encode serialized Amplify Errors + ## 1.5.0 ### Minor Changes diff --git a/packages/platform-core/package.json b/packages/platform-core/package.json index bd0e1cbae4f..47b2b28d90d 100644 --- a/packages/platform-core/package.json +++ b/packages/platform-core/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/platform-core", - "version": "1.5.0", + "version": "1.5.1", "type": "commonjs", "publishConfig": { "access": "public" From 2dab201cb9a222c3b8c396a46c17d661411839ab Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 10 Jan 2025 10:10:07 -0800 Subject: [PATCH 199/199] report cdk versions (#2414) * report cdk versions * update how and when we read lock file * update lock file reader factory * try this * update eol for windows * refactor lock file readers * PR feedback * fix lint --- .changeset/silver-tables-do.md | 10 ++ .eslint_dictionary.json | 3 + .../backend-deployer/src/cdk_deployer.test.ts | 1 + packages/cli-core/package.json | 3 +- .../npm_lock_file_reader.test.ts | 75 +++++++++ .../lock-file-reader/npm_lock_file_reader.ts | 66 ++++++++ .../pnpm_lock_file_reader.test.ts | 145 ++++++++++++++++++ .../lock-file-reader/pnpm_lock_file_reader.ts | 59 +++++++ .../lock-file-reader/types.ts | 9 ++ .../yarn_classic_lock_file_reader.test.ts | 82 ++++++++++ .../yarn_classic_lock_file_reader.ts | 52 +++++++ .../yarn_modern_lock_file_reader.test.ts | 97 ++++++++++++ .../yarn_modern_lock_file_reader.ts | 57 +++++++ .../npm_package_manager_controller.test.ts | 61 ++++++++ .../npm_package_manager_controller.ts | 5 +- .../package_manager_controller_base.ts | 13 ++ .../pnpm_package_manager_controller.test.ts | 61 ++++++++ .../pnpm_package_manager_controller.ts | 5 +- ...classic_package_manager_controller.test.ts | 62 ++++++++ ...yarn_classic_package_manager_controller.ts | 5 +- ..._modern_package_manager_controller.test.ts | 63 ++++++++ .../yarn_modern_package_manager_controller.ts | 5 +- packages/cli/src/ampx.ts | 9 +- .../sandbox/sandbox_command_factory.ts | 16 +- .../src/amplify_project_creator.test.ts | 1 + .../initial_project_file_generator.test.ts | 1 + packages/platform-core/API.md | 3 +- .../src/usage-data/usage_data.ts | 2 +- .../src/usage-data/usage_data_emitter.test.ts | 32 ++++ .../src/usage-data/usage_data_emitter.ts | 12 +- .../usage_data_emitter_factory.test.ts | 9 +- .../usage-data/usage_data_emitter_factory.ts | 8 +- packages/plugin-types/API.md | 7 + .../src/package_manager_controller.ts | 3 + 34 files changed, 1025 insertions(+), 17 deletions(-) create mode 100644 .changeset/silver-tables-do.md create mode 100644 packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.test.ts create mode 100644 packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.ts create mode 100644 packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.test.ts create mode 100644 packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.ts create mode 100644 packages/cli-core/src/package-manager-controller/lock-file-reader/types.ts create mode 100644 packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.test.ts create mode 100644 packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.ts create mode 100644 packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.test.ts create mode 100644 packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.ts diff --git a/.changeset/silver-tables-do.md b/.changeset/silver-tables-do.md new file mode 100644 index 00000000000..8029abf5cf9 --- /dev/null +++ b/.changeset/silver-tables-do.md @@ -0,0 +1,10 @@ +--- +'@aws-amplify/backend-deployer': patch +'create-amplify': patch +'@aws-amplify/backend-cli': patch +'@aws-amplify/cli-core': patch +'@aws-amplify/platform-core': minor +'@aws-amplify/plugin-types': minor +--- + +Report cdk versions diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index 21c64ec6a86..bac7d033068 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -24,6 +24,7 @@ "changelog", "changeset", "changesets", + "checksum", "chown", "claude", "cloudformation", @@ -99,6 +100,7 @@ "lang", "linux", "localhost", + "lockfile", "lsof", "lstat", "macos", @@ -167,6 +169,7 @@ "subpath", "syncable", "synthing", + "testapp", "testname", "testnamebucket", "testuser", diff --git a/packages/backend-deployer/src/cdk_deployer.test.ts b/packages/backend-deployer/src/cdk_deployer.test.ts index 24981682a44..254cb0ff3d5 100644 --- a/packages/backend-deployer/src/cdk_deployer.test.ts +++ b/packages/backend-deployer/src/cdk_deployer.test.ts @@ -46,6 +46,7 @@ void describe('invokeCDKCommand', () => { runWithPackageManager: mock.fn(() => Promise.resolve() as never), getCommand: (args: string[]) => `'npx ${args.join(' ')}'`, allowsSignalPropagation: () => true, + tryGetDependencies: mock.fn(() => Promise.resolve([])), }; const invoker = new CDKDeployer( diff --git a/packages/cli-core/package.json b/packages/cli-core/package.json index a735285a12f..266bbd5932a 100644 --- a/packages/cli-core/package.json +++ b/packages/cli-core/package.json @@ -22,6 +22,7 @@ "@aws-amplify/platform-core": "^1.3.0", "@inquirer/prompts": "^3.0.0", "execa": "^9.5.1", - "kleur": "^4.1.5" + "kleur": "^4.1.5", + "zod": "^3.22.2" } } diff --git a/packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.test.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.test.ts new file mode 100644 index 00000000000..fff5b1c46b6 --- /dev/null +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.test.ts @@ -0,0 +1,75 @@ +import assert from 'assert'; +import fsp from 'fs/promises'; +import { afterEach, describe, it, mock } from 'node:test'; +import path from 'path'; +import { NpmLockFileReader } from './npm_lock_file_reader.js'; + +void describe('NpmLockFileReader', () => { + const fspReadFileMock = mock.method(fsp, 'readFile', () => + JSON.stringify({ + name: 'test_project', + version: '1.0.0', + packages: { + '': { + name: 'test_project', + version: '1.0.0', + }, + 'node_modules/test_dep': { + version: '1.2.3', + }, + 'node_modules/some_other_dep': { + version: '12.13.14', + }, + }, + }) + ); + const npmLockFileReader = new NpmLockFileReader(); + + afterEach(() => { + fspReadFileMock.mock.resetCalls(); + }); + + void it('can get lock file contents from cwd', async () => { + const lockFileContents = + await npmLockFileReader.getLockFileContentsFromCwd(); + const expectedLockFileContents = { + dependencies: [ + { + name: 'test_dep', // "node_modules/" prefix is removed + version: '1.2.3', + }, + { + name: 'some_other_dep', // "node_modules/" prefix is removed + version: '12.13.14', + }, + ], + }; + assert.deepEqual(lockFileContents, expectedLockFileContents); + assert.strictEqual( + fspReadFileMock.mock.calls[0].arguments[0], + path.resolve(process.cwd(), 'package-lock.json') + ); + assert.strictEqual(fspReadFileMock.mock.callCount(), 1); + }); + + void it('returns undefined when package-lock.json is not present or parse-able', async () => { + fspReadFileMock.mock.mockImplementationOnce(() => + Promise.reject(new Error()) + ); + const lockFileContents = + await npmLockFileReader.getLockFileContentsFromCwd(); + assert.deepEqual(lockFileContents, undefined); + }); + + void it('returns empty dependency array when package-lock.json does not have dependencies', async () => { + fspReadFileMock.mock.mockImplementationOnce(() => + JSON.stringify({ + name: 'test_project', + version: '1.0.0', + }) + ); + const lockFileContents = + await npmLockFileReader.getLockFileContentsFromCwd(); + assert.deepEqual(lockFileContents, { dependencies: [] }); + }); +}); diff --git a/packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.ts new file mode 100644 index 00000000000..6f412085b56 --- /dev/null +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.ts @@ -0,0 +1,66 @@ +import { Dependency } from '@aws-amplify/plugin-types'; +import fsp from 'fs/promises'; +import path from 'path'; +import z from 'zod'; +import { LockFileContents, LockFileReader } from './types.js'; +import { printer } from '../../printer.js'; +import { LogLevel } from '../../printer/printer.js'; + +/** + * NpmLockFileReader is an abstraction around the logic used to read and parse lock file contents + */ +export class NpmLockFileReader implements LockFileReader { + getLockFileContentsFromCwd = async (): Promise< + LockFileContents | undefined + > => { + const dependencies: Array = []; + const packageLockJsonPath = path.resolve( + process.cwd(), + 'package-lock.json' + ); + let packageLockJson; + try { + const jsonLockContents = await fsp.readFile(packageLockJsonPath, 'utf-8'); + const jsonLockParsedValue = JSON.parse(jsonLockContents); + // This will strip fields that are not part of the package lock schema + packageLockJson = packageLockJsonSchema.parse(jsonLockParsedValue); + } catch (error) { + printer.log( + `Failed to get lock file contents because ${packageLockJsonPath} does not exist or is not parse-able`, + LogLevel.DEBUG + ); + return; + } + + for (const key in packageLockJson.packages) { + if (key === '') { + // Skip root project in packages + continue; + } + const dependencyVersion = packageLockJson.packages[key].version; + + // Version may not exist if package is a symbolic link + if (dependencyVersion) { + // Remove "node_modules/" prefix + const dependencyName = key.replace(/^node_modules\//, ''); + dependencies.push({ + name: dependencyName, + version: dependencyVersion, + }); + } + } + + return { dependencies }; + }; +} + +const packageLockJsonSchema = z.object({ + packages: z + .record( + z.string(), + z.object({ + version: z.string().optional(), + }) + ) + .optional(), +}); diff --git a/packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.test.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.test.ts new file mode 100644 index 00000000000..443a5f6e5cf --- /dev/null +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.test.ts @@ -0,0 +1,145 @@ +import assert from 'assert'; +import fsp from 'fs/promises'; +import { afterEach, describe, it, mock } from 'node:test'; +import path from 'path'; +import { PnpmLockFileReader } from './pnpm_lock_file_reader.js'; + +void describe('PnpmLockFileReader', () => { + const fspReadFileMock = mock.method( + fsp, + 'readFile', + () => `lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + aws-amplify: + specifier: ^6.12.0 + version: 6.12.0 + devDependencies: + '@aws-amplify/backend': + specifier: ^1.11.0 + version: 1.12.0(@aws-sdk/client-cloudformation@3.723.0)(@aws-sdk/client-s3@3.723.0)(@aws-sdk/client-sso-oidc@3.621.0(@aws-sdk/client-sts@3.621.0))(@aws-sdk/types@3.723.0)(aws-cdk-lib@2.174.1(constructs@10.4.2))(constructs@10.4.2)(zod@3.24.1) + '@aws-amplify/backend-cli': + specifier: ^1.4.5 + version: 1.4.6(@aws-sdk/client-sso-oidc@3.621.0(@aws-sdk/client-sts@3.621.0))(@aws-sdk/client-sts@3.621.0)(@aws-sdk/types@3.723.0)(aws-cdk-lib@2.174.1(constructs@10.4.2))(aws-cdk@2.174.1)(constructs@10.4.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2) + aws-cdk: + specifier: ^2.173.4 + version: 2.174.1 + aws-cdk-lib: + specifier: ^2.173.4 + version: 2.174.1(constructs@10.4.2) + constructs: + specifier: ^10.4.2 + version: 10.4.2 + esbuild: + specifier: ^0.24.2 + version: 0.24.2 + tsx: + specifier: ^4.19.2 + version: 4.19.2 + typescript: + specifier: ^5.7.2 + version: 5.7.2 + +packages: + + '@test_dep@1.2.3': + resolution: {integrity: some-sha} + engines: {node: '>=6.0.0'} + + 'some_other_dep@12.13.14': + resolution: {integrity: some-other-sha} + engines: {node: '>=8'}` + ); + const pnpmLockFileReader = new PnpmLockFileReader(); + + afterEach(() => { + fspReadFileMock.mock.resetCalls(); + }); + + void it('can get lock file contents from cwd', async () => { + const lockFileContents = + await pnpmLockFileReader.getLockFileContentsFromCwd(); + const expectedLockFileContents = { + dependencies: [ + { + name: '@test_dep', + version: '1.2.3', + }, + { + name: 'some_other_dep', + version: '12.13.14', + }, + ], + }; + assert.deepEqual(lockFileContents, expectedLockFileContents); + assert.strictEqual( + fspReadFileMock.mock.calls[0].arguments[0], + path.resolve(process.cwd(), 'pnpm-lock.yaml') + ); + assert.strictEqual(fspReadFileMock.mock.callCount(), 1); + }); + + void it('returns empty lock file contents when pnpm-lock.yaml is not present or parse-able', async () => { + fspReadFileMock.mock.mockImplementationOnce(() => + Promise.reject(new Error()) + ); + const lockFileContents = + await pnpmLockFileReader.getLockFileContentsFromCwd(); + assert.deepEqual(lockFileContents, undefined); + }); + + void it('returns empty dependency array when pnpm-lock.yaml does not have dependencies', async () => { + mock.method( + fsp, + 'readFile', + () => `lockfileVersion: '9.0' + + settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + + importers: + + .: + dependencies: + aws-amplify: + specifier: ^6.12.0 + version: 6.12.0 + devDependencies: + '@aws-amplify/backend': + specifier: ^1.11.0 + version: 1.12.0(@aws-sdk/client-cloudformation@3.723.0)(@aws-sdk/client-s3@3.723.0)(@aws-sdk/client-sso-oidc@3.621.0(@aws-sdk/client-sts@3.621.0))(@aws-sdk/types@3.723.0)(aws-cdk-lib@2.174.1(constructs@10.4.2))(constructs@10.4.2)(zod@3.24.1) + '@aws-amplify/backend-cli': + specifier: ^1.4.5 + version: 1.4.6(@aws-sdk/client-sso-oidc@3.621.0(@aws-sdk/client-sts@3.621.0))(@aws-sdk/client-sts@3.621.0)(@aws-sdk/types@3.723.0)(aws-cdk-lib@2.174.1(constructs@10.4.2))(aws-cdk@2.174.1)(constructs@10.4.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2) + aws-cdk: + specifier: ^2.173.4 + version: 2.174.1 + aws-cdk-lib: + specifier: ^2.173.4 + version: 2.174.1(constructs@10.4.2) + constructs: + specifier: ^10.4.2 + version: 10.4.2 + esbuild: + specifier: ^0.24.2 + version: 0.24.2 + tsx: + specifier: ^4.19.2 + version: 4.19.2 + typescript: + specifier: ^5.7.2 + version: 5.7.2` + ); + const lockFileContents = + await pnpmLockFileReader.getLockFileContentsFromCwd(); + assert.deepEqual(lockFileContents, { dependencies: [] }); + }); +}); diff --git a/packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.ts new file mode 100644 index 00000000000..caed7ecaf64 --- /dev/null +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.ts @@ -0,0 +1,59 @@ +import { Dependency } from '@aws-amplify/plugin-types'; +import fsp from 'fs/promises'; +import path from 'path'; +import { LockFileContents, LockFileReader } from './types.js'; +import { printer } from '../../printer.js'; +import { LogLevel } from '../../printer/printer.js'; + +/** + * PnpmLockFileReader is an abstraction around the logic used to read and parse lock file contents + */ +export class PnpmLockFileReader implements LockFileReader { + getLockFileContentsFromCwd = async (): Promise< + LockFileContents | undefined + > => { + const eolRegex = '[\r\n]'; + const dependencies: Array = []; + const pnpmLockPath = path.resolve(process.cwd(), 'pnpm-lock.yaml'); + + try { + const pnpmLockContents = await fsp.readFile(pnpmLockPath, 'utf-8'); + const pnpmLockContentsArray = pnpmLockContents.split( + new RegExp(`${eolRegex}${eolRegex}`) + ); + + const startOfPackagesIndex = pnpmLockContentsArray.indexOf('packages:'); + if (startOfPackagesIndex === -1) { + return { dependencies }; + } + const pnpmLockPackages = pnpmLockContentsArray.slice( + startOfPackagesIndex + 1 + ); + + for (const pnpmDependencyBlock of pnpmLockPackages) { + // Get line that contains dependency name and version and remove quotes and colon + const pnpmDependencyLine = pnpmDependencyBlock + .trim() + .split(new RegExp(eolRegex))[0] + .replaceAll(/[':]/g, ''); + const dependencyName = pnpmDependencyLine.slice( + 0, + pnpmDependencyLine.lastIndexOf('@') + ); + const dependencyVersion = pnpmDependencyLine.slice( + pnpmDependencyLine.lastIndexOf('@') + 1 + ); + + dependencies.push({ name: dependencyName, version: dependencyVersion }); + } + } catch (error) { + printer.log( + `Failed to get lock file contents because ${pnpmLockPath} does not exist or is not parse-able`, + LogLevel.DEBUG + ); + return; + } + + return { dependencies }; + }; +} diff --git a/packages/cli-core/src/package-manager-controller/lock-file-reader/types.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/types.ts new file mode 100644 index 00000000000..6762c4a30de --- /dev/null +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/types.ts @@ -0,0 +1,9 @@ +import { Dependency } from '@aws-amplify/plugin-types'; + +export type LockFileContents = { + dependencies: Array; +}; + +export type LockFileReader = { + getLockFileContentsFromCwd: () => Promise; +}; diff --git a/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.test.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.test.ts new file mode 100644 index 00000000000..38afb7860b1 --- /dev/null +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.test.ts @@ -0,0 +1,82 @@ +import assert from 'assert'; +import fsp from 'fs/promises'; +import { afterEach, describe, it, mock } from 'node:test'; +import path from 'path'; +import { YarnClassicLockFileReader } from './yarn_classic_lock_file_reader.js'; + +void describe('YarnClassicLockFileReader', () => { + const fspReadFileMock = mock.method( + fsp, + 'readFile', + () => `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@test_dep@^1.0.0": + version "1.2.3" + resolved "https://registry.yarnpkg.com/some_dep" + integrity some-sha + dependencies: + "sub_dep_1" "^0.3.5" + "sub_dep_2" "^0.3.24" + +some_other_dep@12.13.14: + version "12.13.14" + resolved "https://registry.yarnpkg.com/some_other_dep" + integrity some-other-sha + dependencies: + sub_dep_3 "~2.0.1"` + ); + const yarnClassicLockFileReader = new YarnClassicLockFileReader(); + + afterEach(() => { + fspReadFileMock.mock.resetCalls(); + }); + + void it('can get lock file contents from cwd', async () => { + const lockFileContents = + await yarnClassicLockFileReader.getLockFileContentsFromCwd(); + const expectedLockFileContents = { + dependencies: [ + { + name: '@test_dep', + version: '1.2.3', + }, + { + name: 'some_other_dep', + version: '12.13.14', + }, + ], + }; + assert.deepEqual(lockFileContents, expectedLockFileContents); + assert.strictEqual( + fspReadFileMock.mock.calls[0].arguments[0], + path.resolve(process.cwd(), 'yarn.lock') + ); + assert.strictEqual(fspReadFileMock.mock.callCount(), 1); + }); + + void it('returns undefined when yarn.lock is not present or parse-able', async () => { + fspReadFileMock.mock.mockImplementationOnce(() => + Promise.reject(new Error()) + ); + const lockFileContents = + await yarnClassicLockFileReader.getLockFileContentsFromCwd(); + assert.deepEqual(lockFileContents, undefined); + }); + + void it('returns empty dependency array when yarn.lock does not have dependencies', async () => { + mock.method( + fsp, + 'readFile', + () => `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +` + ); + const lockFileContents = + await yarnClassicLockFileReader.getLockFileContentsFromCwd(); + assert.deepEqual(lockFileContents, { dependencies: [] }); + }); +}); diff --git a/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.ts new file mode 100644 index 00000000000..cd12059485c --- /dev/null +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.ts @@ -0,0 +1,52 @@ +import { Dependency } from '@aws-amplify/plugin-types'; +import fsp from 'fs/promises'; +import path from 'path'; +import { LockFileContents, LockFileReader } from './types.js'; +import { printer } from '../../printer.js'; +import { LogLevel } from '../../printer/printer.js'; + +/** + * YarnClassicLockFileReader is an abstraction around the logic used to read and parse lock file contents + */ +export class YarnClassicLockFileReader implements LockFileReader { + getLockFileContentsFromCwd = async (): Promise< + LockFileContents | undefined + > => { + const eolRegex = '[\r\n]'; + const dependencies: Array = []; + const yarnLockPath = path.resolve(process.cwd(), 'yarn.lock'); + + try { + const yarnLockContents = await fsp.readFile(yarnLockPath, 'utf-8'); + const yarnLockContentsArray = yarnLockContents + .trim() + .split(new RegExp(`${eolRegex}${eolRegex}`)); + + // Slice to remove comment block at the start of the lock file + for (const yarnDependencyBlock of yarnLockContentsArray.slice(1)) { + const yarnDependencyLines = yarnDependencyBlock + .trim() + .split(new RegExp(eolRegex)); + const yarnDependencyName = yarnDependencyLines[0]; + const yarnDependencyVersion = yarnDependencyLines[1]; + + // Get dependency name before versioning info + const dependencyName = yarnDependencyName + .slice(0, yarnDependencyName.lastIndexOf('@')) + .replaceAll(/"/g, ''); + const versionMatch = yarnDependencyVersion.match(/"(.*)"/); + const dependencyVersion = versionMatch ? versionMatch[1] : ''; + + dependencies.push({ name: dependencyName, version: dependencyVersion }); + } + } catch (error) { + printer.log( + `Failed to get lock file contents because ${yarnLockPath} does not exist or is not parse-able`, + LogLevel.DEBUG + ); + return; + } + + return { dependencies }; + }; +} diff --git a/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.test.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.test.ts new file mode 100644 index 00000000000..461b88f66a9 --- /dev/null +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.test.ts @@ -0,0 +1,97 @@ +import assert from 'assert'; +import fsp from 'fs/promises'; +import { afterEach, describe, it, mock } from 'node:test'; +import path from 'path'; +import { YarnModernLockFileReader } from './yarn_modern_lock_file_reader.js'; + +void describe('YarnModernLockFileReader', () => { + const fspReadFileMock = mock.method( + fsp, + 'readFile', + () => `# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10c0 + +"@test_dep@npm:^1.0.0": + version: 1.2.3 + resolution: "@test_dep@npm:1.2.3" + dependencies: + "@sub_dep_1": "npm:^0.3.5" + "sub_dep_2": "npm:^0.3.24" + checksum: some-checksum + languageName: node + linkType: hard + +"some_other_dep@npm:12.13.14": + version: 12.13.14 + resolution: "some_other_dep@npm:12.13.14" + dependencies: + sub-dep_3: "npm:~2.0.1" + checksum: some-other-checksum + languageName: node + linkType: hard` + ); + const yarnModernLockFileReader = new YarnModernLockFileReader(); + + afterEach(() => { + fspReadFileMock.mock.resetCalls(); + }); + + void it('can get lock file contents from cwd', async () => { + const lockFileContents = + await yarnModernLockFileReader.getLockFileContentsFromCwd(); + const expectedLockFileContents = { + dependencies: [ + { + name: '@test_dep', + version: '1.2.3', + }, + { + name: 'some_other_dep', + version: '12.13.14', + }, + ], + }; + assert.deepEqual(lockFileContents, expectedLockFileContents); + assert.strictEqual( + fspReadFileMock.mock.calls[0].arguments[0], + path.resolve(process.cwd(), 'yarn.lock') + ); + assert.strictEqual(fspReadFileMock.mock.callCount(), 1); + }); + + void it('returns undefined when yarn.lock is not present or parse-able', async () => { + fspReadFileMock.mock.mockImplementationOnce(() => + Promise.reject(new Error()) + ); + const lockFileContents = + await yarnModernLockFileReader.getLockFileContentsFromCwd(); + assert.deepEqual(lockFileContents, undefined); + }); + + void it('returns empty dependency array when yarn.lock does not have dependencies', async () => { + mock.method( + fsp, + 'readFile', + () => `# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10c0 + +"testapp@workspace:.": + version: 0.0.0-use.local + resolution: "testapp@workspace:." + languageName: unknown + linkType: soft +` + ); + const lockFileContents = + await yarnModernLockFileReader.getLockFileContentsFromCwd(); + assert.deepEqual(lockFileContents, { dependencies: [] }); + }); +}); diff --git a/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.ts new file mode 100644 index 00000000000..868c6dc8d0e --- /dev/null +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.ts @@ -0,0 +1,57 @@ +import { Dependency } from '@aws-amplify/plugin-types'; +import fsp from 'fs/promises'; +import path from 'path'; +import { LockFileContents, LockFileReader } from './types.js'; +import { printer } from '../../printer.js'; +import { LogLevel } from '../../printer/printer.js'; + +/** + * YarnModernLockFileReader is an abstraction around the logic used to read and parse lock file contents + */ +export class YarnModernLockFileReader implements LockFileReader { + getLockFileContentsFromCwd = async (): Promise< + LockFileContents | undefined + > => { + const eolRegex = '[\r\n]'; + const dependencies: Array = []; + const yarnLockPath = path.resolve(process.cwd(), 'yarn.lock'); + + try { + const yarnLockContents = await fsp.readFile(yarnLockPath, 'utf-8'); + const yarnLockContentsArray = yarnLockContents.split( + new RegExp(`${eolRegex}${eolRegex}`) + ); + + if (yarnLockContentsArray.length === 3) { + // Contents are only comment block, metadata, and workspace info + return { dependencies }; + } + + // Slice to remove comment block and metadata at the start of the lock file + for (const yarnDependencyBlock of yarnLockContentsArray.slice(2)) { + const yarnDependencyLines = yarnDependencyBlock + .trim() + .split(new RegExp(eolRegex)); + const yarnDependencyName = yarnDependencyLines[0]; + const yarnDependencyVersion = yarnDependencyLines[1]; + + // Get dependency name before versioning info + const dependencyName = yarnDependencyName + .slice(0, yarnDependencyName.lastIndexOf('@')) + .replaceAll(/"/g, ''); + const versionMatch = yarnDependencyVersion.match(/[\d.]+/); + const dependencyVersion = versionMatch ? versionMatch[0] : ''; + + dependencies.push({ name: dependencyName, version: dependencyVersion }); + } + } catch (error) { + printer.log( + `Failed to get lock file contents because ${yarnLockPath} does not exist or is not parse-able`, + LogLevel.DEBUG + ); + return; + } + + return { dependencies }; + }; +} diff --git a/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.test.ts b/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.test.ts index 938b1c580df..4f7aa2d9dbb 100644 --- a/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.test.ts +++ b/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.test.ts @@ -5,6 +5,7 @@ import assert from 'assert'; import { execa } from 'execa'; import { NpmPackageManagerController } from './npm_package_manager_controller.js'; import { executeWithDebugLogger } from './execute_with_debugger_logger.js'; +import { LockFileReader } from './lock-file-reader/types.js'; void describe('NpmPackageManagerController', () => { const fspMock = { @@ -124,4 +125,64 @@ void describe('NpmPackageManagerController', () => { assert.equal(fspMock.writeFile.mock.callCount(), 1); }); }); + + void describe('getDependencies', () => { + void it('successfully returns dependency versions', async () => { + const existsSyncMock = mock.fn(() => true); + const lockFileReaderMock = { + getLockFileContentsFromCwd: async () => + Promise.resolve({ + dependencies: [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ], + }), + } as LockFileReader; + const npmPackageManagerController = new NpmPackageManagerController( + '/testProjectRoot', + fspMock as unknown as typeof fsp, + pathMock as unknown as typeof path, + execaMock as unknown as typeof execa, + executeWithDebugLoggerMock as unknown as typeof executeWithDebugLogger, + existsSyncMock, + lockFileReaderMock + ); + const dependencyVersions = + await npmPackageManagerController.tryGetDependencies(); + const expectedVersions = [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ]; + + assert.deepEqual(dependencyVersions, expectedVersions); + }); + }); }); diff --git a/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.ts b/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.ts index f680458be27..0277fbf7417 100644 --- a/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.ts +++ b/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.ts @@ -4,6 +4,7 @@ import { execa as _execa } from 'execa'; import * as _path from 'path'; import { executeWithDebugLogger as _executeWithDebugLogger } from './execute_with_debugger_logger.js'; import { PackageManagerControllerBase } from './package_manager_controller_base.js'; +import { NpmLockFileReader } from './lock-file-reader/npm_lock_file_reader.js'; /** * NpmPackageManagerController is an abstraction around npm commands that are needed to initialize a project and install dependencies @@ -18,13 +19,15 @@ export class NpmPackageManagerController extends PackageManagerControllerBase { protected readonly path = _path, protected readonly execa = _execa, protected readonly executeWithDebugLogger = _executeWithDebugLogger, - protected readonly existsSync = _existsSync + protected readonly existsSync = _existsSync, + protected readonly lockFileReader = new NpmLockFileReader() ) { super( cwd, 'npm', ['init', '--yes'], 'install', + lockFileReader, fsp, path, execa, diff --git a/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts b/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts index 642c8a54bf8..be83839c875 100644 --- a/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts +++ b/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts @@ -3,6 +3,7 @@ import _fsp from 'fs/promises'; import { execa as _execa } from 'execa'; import * as _path from 'path'; import { + Dependency, ExecaChildProcess, ExecaOptions, type PackageManagerController, @@ -11,6 +12,7 @@ import { LogLevel } from '../printer/printer.js'; import { printer } from '../printer.js'; import { executeWithDebugLogger as _executeWithDebugLogger } from './execute_with_debugger_logger.js'; import { getPackageManagerRunnerName } from './get_package_manager_name.js'; +import { LockFileReader } from './lock-file-reader/types.js'; /** * PackageManagerController is an abstraction around package manager commands that are needed to initialize a project and install dependencies @@ -27,6 +29,7 @@ export abstract class PackageManagerControllerBase protected readonly executable: string, protected readonly initDefault: string[], protected readonly installCommand: string, + protected readonly lockFileReader: LockFileReader, protected readonly fsp = _fsp, protected readonly path = _path, protected readonly execa = _execa, @@ -145,6 +148,16 @@ export abstract class PackageManagerControllerBase */ allowsSignalPropagation = () => true; + /** + * tryGetDependencies - Tries to retrieve dependency versions from the lock file in the project root + */ + tryGetDependencies = async (): Promise | undefined> => { + const lockFileContents = + await this.lockFileReader.getLockFileContentsFromCwd(); + + return lockFileContents?.dependencies; + }; + /** * Check if a package.json file exists in projectRoot */ diff --git a/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.test.ts b/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.test.ts index 3895e622513..3e3c7bc3423 100644 --- a/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.test.ts +++ b/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.test.ts @@ -5,6 +5,7 @@ import assert from 'assert'; import { execa } from 'execa'; import { PnpmPackageManagerController } from './pnpm_package_manager_controller.js'; import { executeWithDebugLogger } from './execute_with_debugger_logger.js'; +import { LockFileReader } from './lock-file-reader/types.js'; void describe('PnpmPackageManagerController', () => { const fspMock = { @@ -124,4 +125,64 @@ void describe('PnpmPackageManagerController', () => { assert.equal(fspMock.writeFile.mock.callCount(), 1); }); }); + + void describe('getDependencies', () => { + void it('successfully returns dependency versions', async () => { + const existsSyncMock = mock.fn(() => true); + const lockFileReaderMock = { + getLockFileContentsFromCwd: async () => + Promise.resolve({ + dependencies: [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ], + }), + } as LockFileReader; + const pnpmPackageManagerController = new PnpmPackageManagerController( + '/testProjectRoot', + fspMock as unknown as typeof fsp, + pathMock as unknown as typeof path, + execaMock as unknown as typeof execa, + executeWithDebugLoggerMock as unknown as typeof executeWithDebugLogger, + existsSyncMock, + lockFileReaderMock + ); + const dependencyVersions = + await pnpmPackageManagerController.tryGetDependencies(); + const expectedVersions = [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ]; + + assert.deepEqual(dependencyVersions, expectedVersions); + }); + }); }); diff --git a/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.ts b/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.ts index 5eb13ea7de3..2cb6fb49336 100644 --- a/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.ts +++ b/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.ts @@ -4,6 +4,7 @@ import { existsSync as _existsSync } from 'fs'; import { execa as _execa } from 'execa'; import { executeWithDebugLogger as _executeWithDebugLogger } from './execute_with_debugger_logger.js'; import { PackageManagerControllerBase } from './package_manager_controller_base.js'; +import { PnpmLockFileReader } from './lock-file-reader/pnpm_lock_file_reader.js'; /** * PnpmPackageManagerController is an abstraction around pnpm commands that are needed to initialize a project and install dependencies @@ -18,13 +19,15 @@ export class PnpmPackageManagerController extends PackageManagerControllerBase { protected readonly path = _path, protected readonly execa = _execa, protected readonly executeWithDebugLogger = _executeWithDebugLogger, - protected readonly existsSync = _existsSync + protected readonly existsSync = _existsSync, + protected readonly lockFileReader = new PnpmLockFileReader() ) { super( cwd, 'pnpm', ['init'], 'install', + lockFileReader, fsp, path, execa, diff --git a/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.test.ts b/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.test.ts index 28987b2784b..4c35e60e77e 100644 --- a/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.test.ts +++ b/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.test.ts @@ -5,6 +5,7 @@ import assert from 'assert'; import { execa } from 'execa'; import { YarnClassicPackageManagerController } from './yarn_classic_package_manager_controller.js'; import { executeWithDebugLogger } from './execute_with_debugger_logger.js'; +import { LockFileReader } from './lock-file-reader/types.js'; void describe('YarnClassicPackageManagerController', () => { const fspMock = { @@ -128,4 +129,65 @@ void describe('YarnClassicPackageManagerController', () => { assert.equal(fspMock.writeFile.mock.callCount(), 1); }); }); + + void describe('getDependencies', () => { + void it('successfully returns dependency versions', async () => { + const existsSyncMock = mock.fn(() => true); + const lockFileReaderMock = { + getLockFileContentsFromCwd: async () => + Promise.resolve({ + dependencies: [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ], + }), + } as LockFileReader; + const yarnClassicPackageManagerController = + new YarnClassicPackageManagerController( + '/testProjectRoot', + fspMock as unknown as typeof fsp, + pathMock as unknown as typeof path, + execaMock as unknown as typeof execa, + executeWithDebugLoggerMock as unknown as typeof executeWithDebugLogger, + existsSyncMock, + lockFileReaderMock + ); + const dependencyVersions = + await yarnClassicPackageManagerController.tryGetDependencies(); + const expectedVersions = [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ]; + + assert.deepEqual(dependencyVersions, expectedVersions); + }); + }); }); diff --git a/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.ts b/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.ts index 48a4330ed96..70c8e451546 100644 --- a/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.ts +++ b/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.ts @@ -4,6 +4,7 @@ import { execa as _execa } from 'execa'; import * as _path from 'path'; import { executeWithDebugLogger as _executeWithDebugLogger } from './execute_with_debugger_logger.js'; import { PackageManagerControllerBase } from './package_manager_controller_base.js'; +import { YarnClassicLockFileReader } from './lock-file-reader/yarn_classic_lock_file_reader.js'; /** * YarnClassicPackageManagerController is an abstraction around yarn classic commands that are needed to initialize a project and install dependencies @@ -18,13 +19,15 @@ export class YarnClassicPackageManagerController extends PackageManagerControlle protected readonly path = _path, protected readonly execa = _execa, protected readonly executeWithDebugLogger = _executeWithDebugLogger, - protected readonly existsSync = _existsSync + protected readonly existsSync = _existsSync, + protected readonly lockFileReader = new YarnClassicLockFileReader() ) { super( cwd, 'yarn', ['init', '--yes'], 'add', + lockFileReader, fsp, path, execa, diff --git a/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.test.ts b/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.test.ts index 4dfac585d3c..83119e1aefa 100644 --- a/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.test.ts +++ b/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.test.ts @@ -6,6 +6,7 @@ import { execa } from 'execa'; import { Printer } from '../printer/printer.js'; import { YarnModernPackageManagerController } from './yarn_modern_package_manager_controller.js'; import { executeWithDebugLogger } from './execute_with_debugger_logger.js'; +import { LockFileReader } from './lock-file-reader/types.js'; void describe('YarnModernPackageManagerController', () => { const fspMock = { @@ -123,4 +124,66 @@ void describe('YarnModernPackageManagerController', () => { assert.equal(fspMock.writeFile.mock.callCount(), 2); }); }); + + void describe('getDependencies', () => { + void it('successfully returns dependency versions', async () => { + const existsSyncMock = mock.fn(() => true); + const lockFileReaderMock = { + getLockFileContentsFromCwd: async () => + Promise.resolve({ + dependencies: [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ], + }), + } as LockFileReader; + const yarnModernPackageManagerController = + new YarnModernPackageManagerController( + '/testProjectRoot', + printerMock as unknown as Printer, + fspMock as unknown as typeof fsp, + pathMock as unknown as typeof path, + execaMock as unknown as typeof execa, + executeWithDebugLoggerMock as unknown as typeof executeWithDebugLogger, + existsSyncMock, + lockFileReaderMock + ); + const dependencyVersions = + await yarnModernPackageManagerController.tryGetDependencies(); + const expectedVersions = [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ]; + + assert.deepEqual(dependencyVersions, expectedVersions); + }); + }); }); diff --git a/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.ts b/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.ts index 0188ec889de..f4d6003f1b8 100644 --- a/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.ts +++ b/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.ts @@ -6,6 +6,7 @@ import { LogLevel, Printer } from '../printer/printer.js'; import { format } from '../format/format.js'; import { executeWithDebugLogger as _executeWithDebugLogger } from './execute_with_debugger_logger.js'; import { PackageManagerControllerBase } from './package_manager_controller_base.js'; +import { YarnModernLockFileReader } from './lock-file-reader/yarn_modern_lock_file_reader.js'; /** * YarnModernPackageManagerController is an abstraction around yarn modern (yarn v2+) commands that are needed to initialize a project and install dependencies @@ -21,13 +22,15 @@ export class YarnModernPackageManagerController extends PackageManagerController protected readonly path = _path, protected readonly execa = _execa, protected readonly executeWithDebugLogger = _executeWithDebugLogger, - protected readonly existsSync = _existsSync + protected readonly existsSync = _existsSync, + protected readonly lockFileReader = new YarnModernLockFileReader() ) { super( cwd, 'yarn', ['init', '--yes'], 'add', + lockFileReader, fsp, path, execa, diff --git a/packages/cli/src/ampx.ts b/packages/cli/src/ampx.ts index 2ddb90ccb26..c9448a58d1d 100755 --- a/packages/cli/src/ampx.ts +++ b/packages/cli/src/ampx.ts @@ -13,7 +13,7 @@ import { import { fileURLToPath } from 'node:url'; import { verifyCommandName } from './verify_command_name.js'; import { hideBin } from 'yargs/helpers'; -import { format } from '@aws-amplify/cli-core'; +import { PackageManagerControllerFactory, format } from '@aws-amplify/cli-core'; const packageJson = new PackageJsonReader().read( fileURLToPath(new URL('../package.json', import.meta.url)) @@ -27,8 +27,13 @@ if (libraryVersion == undefined) { }); } +const dependencies = await new PackageManagerControllerFactory() + .getPackageManagerController() + .tryGetDependencies(); + const usageDataEmitter = await new UsageDataEmitterFactory().getInstance( - libraryVersion + libraryVersion, + dependencies ); attachUnhandledExceptionListeners(usageDataEmitter); diff --git a/packages/cli/src/commands/sandbox/sandbox_command_factory.ts b/packages/cli/src/commands/sandbox/sandbox_command_factory.ts index ef5454a9319..eace366cd20 100644 --- a/packages/cli/src/commands/sandbox/sandbox_command_factory.ts +++ b/packages/cli/src/commands/sandbox/sandbox_command_factory.ts @@ -16,7 +16,11 @@ import { } from '@aws-amplify/platform-core'; import { SandboxEventHandlerFactory } from './sandbox_event_handler_factory.js'; import { CommandMiddleware } from '../../command_middleware.js'; -import { format, printer } from '@aws-amplify/cli-core'; +import { + PackageManagerControllerFactory, + format, + printer, +} from '@aws-amplify/cli-core'; import { S3Client } from '@aws-sdk/client-s3'; import { AmplifyClient } from '@aws-sdk/client-amplify'; import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; @@ -57,7 +61,15 @@ export const createSandboxCommand = (): CommandModule< const eventHandlerFactory = new SandboxEventHandlerFactory( sandboxBackendIdPartsResolver.resolve, - async () => await new UsageDataEmitterFactory().getInstance(libraryVersion) + async () => { + const dependencies = await new PackageManagerControllerFactory() + .getPackageManagerController() + .tryGetDependencies(); + return await new UsageDataEmitterFactory().getInstance( + libraryVersion, + dependencies + ); + } ); const commandMiddleWare = new CommandMiddleware(printer); diff --git a/packages/create-amplify/src/amplify_project_creator.test.ts b/packages/create-amplify/src/amplify_project_creator.test.ts index ea9407ebfb8..cf4675f9b14 100644 --- a/packages/create-amplify/src/amplify_project_creator.test.ts +++ b/packages/create-amplify/src/amplify_project_creator.test.ts @@ -109,6 +109,7 @@ void describe('AmplifyProjectCreator', () => { runWithPackageManager: mock.fn(() => Promise.resolve() as never), getCommand: (args: string[]) => `'npx ${args.join(' ')}'`, allowsSignalPropagation: () => true, + tryGetDependencies: mock.fn(() => Promise.resolve([])), }; const projectRootValidatorMock = { validate: mock.fn() }; const gitIgnoreInitializerMock = { ensureInitialized: mock.fn() }; diff --git a/packages/create-amplify/src/initial_project_file_generator.test.ts b/packages/create-amplify/src/initial_project_file_generator.test.ts index 8fcfb9190ac..39bed0b4889 100644 --- a/packages/create-amplify/src/initial_project_file_generator.test.ts +++ b/packages/create-amplify/src/initial_project_file_generator.test.ts @@ -17,6 +17,7 @@ void describe('InitialProjectFileGenerator', () => { runWithPackageManager: mock.fn(() => Promise.resolve() as never), getCommand: (args: string[]) => `'npx ${args.join(' ')}'`, allowsSignalPropagation: () => true, + tryGetDependencies: mock.fn(() => Promise.resolve([])), }; beforeEach(() => { executeWithDebugLoggerMock.mock.resetCalls(); diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index 06891c5af75..4ef316c3494 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -8,6 +8,7 @@ import { AppId } from '@aws-amplify/plugin-types'; import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; import { DeepPartialAmplifyGeneratedConfigs } from '@aws-amplify/plugin-types'; +import { Dependency } from '@aws-amplify/plugin-types'; import { FieldLogLevel } from 'aws-cdk-lib/aws-appsync'; import { LogLevel } from '@aws-amplify/plugin-types'; import { LogRetention } from '@aws-amplify/plugin-types'; @@ -230,7 +231,7 @@ export type UsageDataEmitter = { // @public export class UsageDataEmitterFactory { - getInstance: (libraryVersion: string) => Promise; + getInstance: (libraryVersion: string, dependencies?: Array) => Promise; } // (No @packageDocumentation comment for this package) diff --git a/packages/platform-core/src/usage-data/usage_data.ts b/packages/platform-core/src/usage-data/usage_data.ts index 68773639ed1..41b7a8b995b 100644 --- a/packages/platform-core/src/usage-data/usage_data.ts +++ b/packages/platform-core/src/usage-data/usage_data.ts @@ -16,5 +16,5 @@ export type UsageData = { accountId: string; input: { command: string; plugin: string }; codePathDurations: { platformStartup?: number; totalDuration?: number }; - projectSetting: { editor?: string }; + projectSetting: { editor?: string; details?: string }; }; diff --git a/packages/platform-core/src/usage-data/usage_data_emitter.test.ts b/packages/platform-core/src/usage-data/usage_data_emitter.test.ts index 89c53c5ad49..189c9b57d71 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter.test.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter.test.ts @@ -19,6 +19,24 @@ void describe('UsageDataEmitter', () => { let usageDataEmitter: DefaultUsageDataEmitter; const testLibraryVersion = '1.2.3'; + const testDependencies = [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + { + name: 'test-dep', + version: '1.2.4', + }, + { + name: 'some_other_dep', + version: '12.12.14', + }, + ]; const testURL = url.parse('https://aws.amazon.com/amplify/'); const onReqEndMock = mock.fn(); const onReqWriteMock = mock.fn(); @@ -91,6 +109,19 @@ void describe('UsageDataEmitter', () => { assert.ok(validate(usageDataSent.installationUuid)); assert.ok(usageDataSent.error == undefined); assert.ok(usageDataSent.downstreamException == undefined); + assert.deepStrictEqual( + usageDataSent.projectSetting.details, + JSON.stringify([ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + ]) + ); }); void test('happy case, emitFailure generates and send correct usage data', async () => { @@ -155,6 +186,7 @@ void describe('UsageDataEmitter', () => { usageDataEmitter = new DefaultUsageDataEmitter( testLibraryVersion, + testDependencies, v4(), testURL, accountIdFetcherMock diff --git a/packages/platform-core/src/usage-data/usage_data_emitter.ts b/packages/platform-core/src/usage-data/usage_data_emitter.ts index 2677c9b4479..82616760aac 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter.ts @@ -10,20 +10,29 @@ import isCI from 'is-ci'; import { SerializableError } from './serializable_error.js'; import { UsageDataEmitter } from './usage_data_emitter_factory.js'; import { AmplifyError } from '../index.js'; +import { Dependency } from '@aws-amplify/plugin-types'; /** * Entry point for sending usage data metrics */ export class DefaultUsageDataEmitter implements UsageDataEmitter { + private dependenciesToReport?: Array; /** * Constructor for UsageDataEmitter */ constructor( private readonly libraryVersion: string, + private readonly dependencies?: Array, private readonly sessionUuid = uuid(), private readonly url = getUrl(), private readonly accountIdFetcher = new AccountIdFetcher() - ) {} + ) { + const targetDependencies = ['aws-cdk', 'aws-cdk-lib']; + + this.dependenciesToReport = this.dependencies?.filter((dependency) => + targetDependencies.includes(dependency.name) + ); + } emitSuccess = async ( metrics?: Record, @@ -88,6 +97,7 @@ export class DefaultUsageDataEmitter implements UsageDataEmitter { isCi: isCI, projectSetting: { editor: process.env.npm_config_user_agent, + details: JSON.stringify(this.dependenciesToReport), }, }; }; diff --git a/packages/platform-core/src/usage-data/usage_data_emitter_factory.test.ts b/packages/platform-core/src/usage-data/usage_data_emitter_factory.test.ts index 7d7e0aaf84e..443a0227e99 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter_factory.test.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter_factory.test.ts @@ -41,7 +41,8 @@ void describe('UsageDataEmitterFactory', () => { void it('returns DefaultUsageDataEmitter by default', async () => { configControllerGet.mock.mockImplementationOnce(() => undefined); const dataEmitter = await new UsageDataEmitterFactory().getInstance( - '0.0.0' + '0.0.0', + [] ); assert.strictEqual(configControllerGet.mock.callCount(), 1); assert.strictEqual(dataEmitter instanceof DefaultUsageDataEmitter, true); @@ -51,7 +52,8 @@ void describe('UsageDataEmitterFactory', () => { configControllerGet.mock.mockImplementationOnce(() => undefined); process.env['AMPLIFY_DISABLE_TELEMETRY'] = '1'; const dataEmitter = await new UsageDataEmitterFactory().getInstance( - '0.0.0' + '0.0.0', + [] ); assert.strictEqual(dataEmitter instanceof NoOpUsageDataEmitter, true); assert.strictEqual(configControllerGet.mock.callCount(), 1); @@ -61,7 +63,8 @@ void describe('UsageDataEmitterFactory', () => { void it('returns NoOpUsageDataEmitter if local config file exists and reads true', async () => { configControllerGet.mock.mockImplementationOnce(() => false); const dataEmitter = await new UsageDataEmitterFactory().getInstance( - '0.0.0' + '0.0.0', + [] ); assert.strictEqual(configControllerGet.mock.callCount(), 1); assert.strictEqual(dataEmitter instanceof NoOpUsageDataEmitter, true); diff --git a/packages/platform-core/src/usage-data/usage_data_emitter_factory.ts b/packages/platform-core/src/usage-data/usage_data_emitter_factory.ts index 6ee9a2fb110..c8134c0e097 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter_factory.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter_factory.ts @@ -3,6 +3,7 @@ import { NoOpUsageDataEmitter } from './noop_usage_data_emitter.js'; import { DefaultUsageDataEmitter } from './usage_data_emitter.js'; import { USAGE_DATA_TRACKING_ENABLED } from './constants.js'; import { AmplifyError } from '../index.js'; +import { Dependency } from '@aws-amplify/plugin-types'; export type UsageDataEmitter = { emitSuccess: ( @@ -22,7 +23,10 @@ export class UsageDataEmitterFactory { /** * Creates UsageDataEmitter for a given library version, usage data tracking preferences */ - getInstance = async (libraryVersion: string): Promise => { + getInstance = async ( + libraryVersion: string, + dependencies?: Array + ): Promise => { const configController = configControllerFactory.getInstance( 'usage_data_preferences.json' ); @@ -37,6 +41,6 @@ export class UsageDataEmitterFactory { ) { return new NoOpUsageDataEmitter(); } - return new DefaultUsageDataEmitter(libraryVersion); + return new DefaultUsageDataEmitter(libraryVersion, dependencies); }; } diff --git a/packages/plugin-types/API.md b/packages/plugin-types/API.md index 181acf42420..b289307f054 100644 --- a/packages/plugin-types/API.md +++ b/packages/plugin-types/API.md @@ -152,6 +152,12 @@ export type DeepPartialAmplifyGeneratedConfigs = { [P in keyof T]?: P extends 'auth' | 'data' | 'storage' ? T[P] extends object ? DeepPartialAmplifyGeneratedConfigs : Partial : T[P]; }; +// @public (undocumented) +export type Dependency = { + name: string; + version: string; +}; + // @public export type DeploymentType = 'branch' | 'sandbox'; @@ -220,6 +226,7 @@ export type PackageManagerController = { runWithPackageManager: (args: string[] | undefined, dir: string, options?: ExecaOptions) => ExecaChildProcess; getCommand: (args: string[]) => string; allowsSignalPropagation: () => boolean; + tryGetDependencies: () => Promise | undefined>; }; // @public (undocumented) diff --git a/packages/plugin-types/src/package_manager_controller.ts b/packages/plugin-types/src/package_manager_controller.ts index 8e53789641e..d583960c434 100644 --- a/packages/plugin-types/src/package_manager_controller.ts +++ b/packages/plugin-types/src/package_manager_controller.ts @@ -23,6 +23,8 @@ export type ExecaChildProcess = { stderr: Readable | null; } & Promise; +export type Dependency = { name: string; version: string }; + export type PackageManagerController = { initializeProject: () => Promise; initializeTsConfig: (targetDir: string) => Promise; @@ -37,4 +39,5 @@ export type PackageManagerController = { ) => ExecaChildProcess; getCommand: (args: string[]) => string; allowsSignalPropagation: () => boolean; + tryGetDependencies: () => Promise | undefined>; };