From a277feab6f23604031b3142e51f88e2d005f8f54 Mon Sep 17 00:00:00 2001 From: aws-asolidu Date: Mon, 14 Apr 2025 10:39:03 -0700 Subject: [PATCH 01/12] feat(sagemaker-connect): sagemaker explorer, user can list spaces #2124 ## Problem - User should be able to view their Sagemaker spaces so that they can remote connect to it. ## Solution - Fetch spaces and apps using Sagemaker client - Add Sagemaker node to explorer and list spaces --- package-lock.json | 504 ++++++++++++++++++ packages/core/package.json | 21 +- .../icons/aws/sagemaker/code-editor.svg | 4 + .../icons/aws/sagemaker/jupyter-lab.svg | 4 + .../sagemaker/explorer/sagemakerParentNode.ts | 52 ++ .../sagemaker/explorer/sagemakerSpaceNode.ts | 79 +++ .../core/src/awsService/sagemaker/utils.ts | 43 ++ packages/core/src/awsexplorer/regionNode.ts | 6 + packages/core/src/shared/clients/sagemaker.ts | 55 ++ .../explorer/sagemakerParentNode.test.ts | 58 ++ .../explorer/sagemakerSpaceNode.test.ts | 80 +++ .../test/awsService/sagemaker/utils.test.ts | 66 +++ .../shared/clients/sagemakerClient.test.ts | 62 +++ 13 files changed, 1031 insertions(+), 3 deletions(-) create mode 100644 packages/core/resources/icons/aws/sagemaker/code-editor.svg create mode 100644 packages/core/resources/icons/aws/sagemaker/jupyter-lab.svg create mode 100644 packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts create mode 100644 packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts create mode 100644 packages/core/src/awsService/sagemaker/utils.ts create mode 100644 packages/core/src/shared/clients/sagemaker.ts create mode 100644 packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts create mode 100644 packages/core/src/test/awsService/sagemaker/explorer/sagemakerSpaceNode.test.ts create mode 100644 packages/core/src/test/awsService/sagemaker/utils.test.ts create mode 100644 packages/core/src/test/shared/clients/sagemakerClient.test.ts diff --git a/package-lock.json b/package-lock.json index f2c10f779b1..09922395ec8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7273,6 +7273,509 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-sagemaker": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sagemaker/-/client-sagemaker-3.693.0.tgz", + "integrity": "sha512-iInrrb7V9f0CRBiVCaaxCbpoBRQ5BqxX4elRYI6gE/pSDD2tPqmRfm4reahMtTUcKg1jaSGuvqJLfOpp0HTozQ==", + "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/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/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", + "@smithy/util-waiter": "^3.1.8", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.693.0.tgz", + "integrity": "sha512-QEynrBC26x6TG9ZMzApR/kZ3lmt4lEIs2D+cHuDxt6fDGzahBUsQFBwJqhizzsM97JJI5YvmJhmihoYjdSSaXA==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.693.0.tgz", + "integrity": "sha512-UEDbYlYtK/e86OOMyFR4zEPyenIxDzO2DRdz3fwVW7RzZ94wfmSwBh/8skzPTuY1G7sI064cjHW0b0QG01Sdtg==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.693.0.tgz", + "integrity": "sha512-4S2y7VEtvdnjJX4JPl4kDQlslxXEZFnC50/UXVUYSt/AMc5A/GgspFNA5FVz4E3Gwpfobbf23hR2NBF8AGvYoQ==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.693.0.tgz", + "integrity": "sha512-v6Z/kWmLFqRLDPEwl9hJGhtTgIFHjZugSfF1Yqffdxf4n1AWgtHS7qSegakuMyN5pP4K2tvUD8qHJ+gGe2Bw2A==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.693.0.tgz", + "integrity": "sha512-sL8MvwNJU7ZpD7/d2VVb3by1GknIJUxzTIgYtVkDVA/ojo+KRQSSHxcj0EWWXF5DTSh2Tm+LrEug3y1ZyKHsDA==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.693.0.tgz", + "integrity": "sha512-kvaa4mXhCCOuW7UQnBhYqYfgWmwy7WSBSDClutwSLPZvgrhYj2l16SD2lN4IfYdxARYMJJ1lFYp3/jJG/9Yk4Q==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.693.0.tgz", + "integrity": "sha512-42WMsBjTNnjYxYuM3qD/Nq+8b7UdMopUq5OduMDxoM3mFTV6PXMMnfI4Z1TNnR4tYRvPXAnuNltF6xmjKbSJRA==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.693.0.tgz", + "integrity": "sha512-479UlJxY+BFjj3pJFYUNC0DCMrykuG7wBAXfsvZqQxKUa83DnH5Q1ID/N2hZLkxjGd4ZW0AC3lTOMxFelGzzpQ==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.693.0.tgz", + "integrity": "sha512-8LB210Pr6VeCiSb2hIra+sAH4KUBLyGaN50axHtIgufVK8jbKIctTZcVY5TO9Se+1107TsruzeXS7VeqVdJfFA==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.693.0.tgz", + "integrity": "sha512-BCki6sAZ5jYwIN/t3ElCiwerHad69ipHwPsDCxJQyeiOnJ8HG+lEpnVIfrnI8A0fLQNSF3Gtx6ahfBpKiv1Oug==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.693.0.tgz", + "integrity": "sha512-dXnXDPr+wIiJ1TLADACI1g9pkSB21KkMIko2u4CJ2JCBoxi5IqeTnVoa6YcC8GdFNVRl+PorZ3Zqfmf1EOTC6w==", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.693.0.tgz", + "integrity": "sha512-0LDmM+VxXp0u3rG0xQRWD/q6Ubi7G8I44tBPahevD5CaiDZTkmNTrVUf0VEJgVe0iCKBppACMBDkLB0/ETqkFw==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.693.0.tgz", + "integrity": "sha512-/KUq/KEpFFbQmNmpp7SpAtFAdViquDfD2W0QcG07zYBfz9MwE2ig48ALynXm5sMpRmnG7sJXjdvPtTsSVPfkiw==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.693.0.tgz", + "integrity": "sha512-YLUkMsUY0GLW/nfwlZ69cy1u07EZRmsv8Z9m0qW317/EZaVx59hcvmcvb+W4bFqj5E8YImTjoGfE4cZ0F9mkyw==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.693.0.tgz", + "integrity": "sha512-nDBTJMk1l/YmFULGfRbToOA2wjf+FkQT4dMgYCv+V9uSYsMzQj8A7Tha2dz9yv4vnQgYaEiErQ8d7HVyXcVEoA==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.693.0.tgz", + "integrity": "sha512-eo4F6DRQ/kxS3gxJpLRv+aDNy76DxQJL5B3DPzpr9Vkq0ygVoi4GT5oIZLVaAVIJmi6k5qq9dLsYZfWLUxJJSg==", + "dependencies": { + "@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-sdk/client-sagemaker/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.693.0.tgz", + "integrity": "sha512-6EUfuKOujtddy18OLJUaXfKBgs+UcbZ6N/3QV4iOkubCUdeM1maIqs++B9bhCbWeaeF5ORizJw5FTwnyNjE/mw==", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.693.0.tgz", + "integrity": "sha512-td0OVX8m5ZKiXtecIDuzY3Y3UZIzvxEr57Hp21NOwieqKCG2UeyQWWeGPv0FQaU7dpTkvFmVNI+tx9iB8V/Nhg==", + "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-sdk/client-sagemaker/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@aws-sdk/client-ssm": { "version": "3.693.0", "license": "Apache-2.0", @@ -26727,6 +27230,7 @@ "@aws-sdk/client-iam": "<3.731.0", "@aws-sdk/client-lambda": "<3.731.0", "@aws-sdk/client-s3": "<3.731.0", + "@aws-sdk/client-sagemaker": "<3.696.0", "@aws-sdk/client-ssm": "<3.731.0", "@aws-sdk/client-sso": "<3.731.0", "@aws-sdk/client-sso-oidc": "<3.731.0", diff --git a/packages/core/package.json b/packages/core/package.json index fedea219112..d527208ff1f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -392,26 +392,40 @@ "fontCharacter": "\\f1dc" } }, - "aws-schemas-registry": { + "aws-sagemaker-code-editor": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1dd" } }, - "aws-schemas-schema": { + "aws-sagemaker-jupyter-lab": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1de" } }, - "aws-stepfunctions-preview": { + "aws-schemas-registry": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1df" } + }, + "aws-schemas-schema": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e0" + } + }, + "aws-stepfunctions-preview": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e1" + } } } }, @@ -512,6 +526,7 @@ "@aws-sdk/client-iam": "<3.731.0", "@aws-sdk/client-lambda": "<3.731.0", "@aws-sdk/client-s3": "<3.731.0", + "@aws-sdk/client-sagemaker": "<3.696.0", "@aws-sdk/client-ssm": "<3.731.0", "@aws-sdk/client-sso": "<3.731.0", "@aws-sdk/client-sso-oidc": "<3.731.0", diff --git a/packages/core/resources/icons/aws/sagemaker/code-editor.svg b/packages/core/resources/icons/aws/sagemaker/code-editor.svg new file mode 100644 index 00000000000..03e2f35f05c --- /dev/null +++ b/packages/core/resources/icons/aws/sagemaker/code-editor.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/core/resources/icons/aws/sagemaker/jupyter-lab.svg b/packages/core/resources/icons/aws/sagemaker/jupyter-lab.svg new file mode 100644 index 00000000000..c2a31bc0363 --- /dev/null +++ b/packages/core/resources/icons/aws/sagemaker/jupyter-lab.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts b/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts new file mode 100644 index 00000000000..225295ad670 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts @@ -0,0 +1,52 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { AWSTreeNodeBase } from '../../../shared/treeview/nodes/awsTreeNodeBase' +import { SagemakerClient } from '../../../shared/clients/sagemaker' +import { makeChildrenNodes } from '../../../shared/treeview/utils' +import { updateInPlace } from '../../../shared/utilities/collectionUtils' +import { SagemakerSpaceNode } from './sagemakerSpaceNode' +import { PlaceholderNode } from '../../../shared/treeview/nodes/placeholderNode' + +export const parentContextValue = 'awsSagemakerParentNode' + +export class SagemakerParentNode extends AWSTreeNodeBase { + protected readonly placeHolderMessage = '[No Sagemaker Spaces Found]' + protected sagemakerSpaceNodes: Map + public override readonly contextValue: string = parentContextValue + + public constructor( + public override readonly regionCode: string, + protected readonly sagemakerClient: SagemakerClient + ) { + super('Sagemaker', vscode.TreeItemCollapsibleState.Collapsed) + this.sagemakerSpaceNodes = new Map() + } + + public override async getChildren(): Promise { + const result = await makeChildrenNodes({ + getChildNodes: async () => { + await this.updateChildren() + return [...this.sagemakerSpaceNodes.values()] + }, + getNoChildrenPlaceholderNode: async () => new PlaceholderNode(this, this.placeHolderMessage), + sort: (nodeA, nodeB) => nodeA.name.localeCompare(nodeB.name), + }) + + return result + } + + public async updateChildren(): Promise { + const spaceAppMap = await this.sagemakerClient.fetchSpaceApps() + + updateInPlace( + this.sagemakerSpaceNodes, + spaceAppMap.keys(), + (key) => this.sagemakerSpaceNodes.get(key)!.updateSpace(spaceAppMap.get(key)!), + (key) => new SagemakerSpaceNode(this, this.sagemakerClient, this.regionCode, spaceAppMap.get(key)!) + ) + } +} diff --git a/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts b/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts new file mode 100644 index 00000000000..e10ee03a83f --- /dev/null +++ b/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts @@ -0,0 +1,79 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { AppType } from '@aws-sdk/client-sagemaker' +import { SagemakerClient, SagemakerSpaceApp } from '../../../shared/clients/sagemaker' +import { AWSResourceNode } from '../../../shared/treeview/nodes/awsResourceNode' +import { AWSTreeNodeBase } from '../../../shared/treeview/nodes/awsTreeNodeBase' +import { SagemakerParentNode } from './sagemakerParentNode' +import { generateSpaceStatus } from '../utils' +import { getIcon } from '../../../shared/icons' + +export class SagemakerSpaceNode extends AWSTreeNodeBase implements AWSResourceNode { + public constructor( + public readonly parent: SagemakerParentNode, + public readonly client: SagemakerClient, + public override readonly regionCode: string, + public readonly spaceApp: SagemakerSpaceApp + ) { + super('') + this.updateSpace(spaceApp) + this.contextValue = 'awsSagemakerSpaceRunningNode' + } + + public updateSpace(spaceApp: SagemakerSpaceApp) { + this.label = this.buildLabel() + this.description = this.buildDescription() + this.tooltip = new vscode.MarkdownString(this.buildTooltip()) + this.iconPath = this.getAppIcon() + } + + public get name(): string { + return this.spaceApp.SpaceName ?? `(no name)` + } + + public get arn(): string { + return 'placeholder-arn' + } + + public async getAppArn() { + const appDetails = await this.client.describeApp({ + DomainId: this.spaceApp.DomainId, + AppName: this.spaceApp.App?.AppName, + AppType: this.spaceApp.SpaceSettingsSummary?.AppType, + SpaceName: this.spaceApp.SpaceName, + }) + + return appDetails.AppArn + } + + private buildLabel(): string { + const status = generateSpaceStatus(this.spaceApp.Status, this.spaceApp.App?.Status) + return `${this.name} (${status})` + } + + private buildDescription(): string { + return `${this.spaceApp.SpaceSharingSettingsSummary?.SharingType ?? 'Unknown'} space` + } + private buildTooltip() { + const spaceName = this.spaceApp?.SpaceName ?? '-' + const appType = this.spaceApp?.SpaceSettingsSummary?.AppType ?? '-' + const domainId = this.spaceApp?.DomainId ?? '-' + const owner = this.spaceApp?.OwnershipSettingsSummary?.OwnerUserProfileName ?? '-' + + return `**Space:** ${spaceName} \n\n**Application:** ${appType} \n\n**Domain ID:** ${domainId} \n\n**User Profile:** ${owner}` + } + + private getAppIcon() { + if (this.spaceApp.SpaceSettingsSummary?.AppType === AppType.CodeEditor) { + return getIcon('aws-sagemaker-code-editor') + } + + if (this.spaceApp.SpaceSettingsSummary?.AppType === AppType.JupyterLab) { + return getIcon('aws-sagemaker-jupyter-lab') + } + } +} diff --git a/packages/core/src/awsService/sagemaker/utils.ts b/packages/core/src/awsService/sagemaker/utils.ts new file mode 100644 index 00000000000..a770e6ee42c --- /dev/null +++ b/packages/core/src/awsService/sagemaker/utils.ts @@ -0,0 +1,43 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AppStatus, SpaceStatus } from '@aws-sdk/client-sagemaker' + +export function generateSpaceStatus(spaceStatus?: string, appStatus?: string) { + if ( + spaceStatus === SpaceStatus.Failed || + spaceStatus === SpaceStatus.Delete_Failed || + spaceStatus === SpaceStatus.Update_Failed || + (appStatus === AppStatus.Failed && spaceStatus !== SpaceStatus.Updating) + ) { + return 'Failed' + } + + if (spaceStatus === SpaceStatus.InService && appStatus === AppStatus.InService) { + return 'Running' + } + + if (spaceStatus === SpaceStatus.InService && appStatus === AppStatus.Pending) { + return 'Starting' + } + + if (spaceStatus === SpaceStatus.Updating) { + return 'Updating' + } + + if (spaceStatus === SpaceStatus.InService && appStatus === AppStatus.Deleting) { + return 'Stopping' + } + + if (spaceStatus === SpaceStatus.InService && (appStatus === AppStatus.Deleted || !appStatus)) { + return 'Stopped' + } + + if (spaceStatus === SpaceStatus.Deleting) { + return 'Deleting' + } + + return 'Unknown' +} diff --git a/packages/core/src/awsexplorer/regionNode.ts b/packages/core/src/awsexplorer/regionNode.ts index 10e8d975fe8..d78bcbec2a4 100644 --- a/packages/core/src/awsexplorer/regionNode.ts +++ b/packages/core/src/awsexplorer/regionNode.ts @@ -32,6 +32,8 @@ import { getEcsRootNode } from '../awsService/ecs/model' import { compareTreeItems, TreeShim } from '../shared/treeview/utils' import { Ec2ParentNode } from '../awsService/ec2/explorer/ec2ParentNode' import { Ec2Client } from '../shared/clients/ec2' +import { SagemakerParentNode } from '../awsService/sagemaker/explorer/sagemakerParentNode' +import { SagemakerClient } from '../shared/clients/sagemaker' interface ServiceNode { allRegions?: boolean @@ -96,6 +98,10 @@ const serviceCandidates: ServiceNode[] = [ serviceId: 's3', createFn: (regionCode: string) => new S3Node(new S3Client(regionCode)), }, + { + serviceId: 'api.sagemaker', + createFn: (regionCode: string) => new SagemakerParentNode(regionCode, new SagemakerClient(regionCode)), + }, { serviceId: 'schemas', createFn: (regionCode: string) => new SchemasNode(new DefaultSchemaClient(regionCode)), diff --git a/packages/core/src/shared/clients/sagemaker.ts b/packages/core/src/shared/clients/sagemaker.ts new file mode 100644 index 00000000000..b3462311829 --- /dev/null +++ b/packages/core/src/shared/clients/sagemaker.ts @@ -0,0 +1,55 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + AppDetails, + DescribeAppCommand, + DescribeAppCommandInput, + DescribeAppCommandOutput, + ListAppsCommandInput, + ListSpacesCommandInput, + SageMakerClient, + SpaceDetails, + paginateListApps, + paginateListSpaces, +} from '@aws-sdk/client-sagemaker' +import { ClientWrapper } from './clientWrapper' +import { AsyncCollection } from '../utilities/asyncCollection' + +export interface SagemakerSpaceApp extends SpaceDetails { + App?: AppDetails +} +export class SagemakerClient extends ClientWrapper { + public constructor(public override readonly regionCode: string) { + super(regionCode, SageMakerClient) + } + + public listSpaces(request: ListSpacesCommandInput = {}): AsyncCollection { + return this.makePaginatedRequest(paginateListSpaces, request, (page) => page.Spaces) + } + + public listApps(request: ListAppsCommandInput = {}): AsyncCollection { + return this.makePaginatedRequest(paginateListApps, request, (page) => page.Apps) + } + + public describeApp(request: DescribeAppCommandInput): Promise { + return this.makeRequest(DescribeAppCommand, request) + } + + public async fetchSpaceApps(): Promise> { + const appMap = await this.listApps() + .flatten() + .toMap((app) => `${app.DomainId}-${app.SpaceName}` as string) + + const spaceApps = await this.listSpaces() + .flatten() + .map((space) => { + const key = `${space.DomainId}-${space.SpaceName}` as string + return { ...space, App: appMap.get(key) } + }) + .toMap((space) => `${space.DomainId}-${space.SpaceName}` as string) + return spaceApps + } +} diff --git a/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts b/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts new file mode 100644 index 00000000000..20c5bf3a87a --- /dev/null +++ b/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts @@ -0,0 +1,58 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as sinon from 'sinon' +import { SagemakerClient, SagemakerSpaceApp } from '../../../../shared/clients/sagemaker' +import { SagemakerParentNode } from '../../../../awsService/sagemaker/explorer/sagemakerParentNode' +import { assertNodeListOnlyHasPlaceholderNode } from '../../../utilities/explorerNodeAssertions' +import assert from 'assert' + +describe('sagemakerParentNode', function () { + let testNode: SagemakerParentNode + let client: SagemakerClient + let fetchSpaceAppsStub: sinon.SinonStub<[], Promise>> + const testRegion = 'testRegion' + + before(function () { + client = new SagemakerClient(testRegion) + }) + + beforeEach(function () { + fetchSpaceAppsStub = sinon.stub(SagemakerClient.prototype, 'fetchSpaceApps') + testNode = new SagemakerParentNode(testRegion, client) + }) + + afterEach(function () { + fetchSpaceAppsStub.restore() + }) + + after(function () { + sinon.restore() + }) + + it('returns placeholder node if no children are present', async function () { + fetchSpaceAppsStub.returns(Promise.resolve(new Map())) + const childNodes = await testNode.getChildren() + assertNodeListOnlyHasPlaceholderNode(childNodes) + fetchSpaceAppsStub.restore() + }) + + it('has child nodes', async function () { + const spaceApps: SagemakerSpaceApp[] = [ + { SpaceName: 'name1', DomainId: 'domain1' }, + { SpaceName: 'name2', DomainId: 'domain2' }, + ] + + const spaceAppsMap = new Map() + for (const space of spaceApps) { + spaceAppsMap.set(`${space.DomainId}-${space.SpaceName}` as string, space) + } + + fetchSpaceAppsStub.returns(Promise.resolve(spaceAppsMap)) + const childNodes = await testNode.getChildren() + assert.strictEqual(childNodes.length, spaceApps.length, 'Unexpected child count') + fetchSpaceAppsStub.restore() + }) +}) diff --git a/packages/core/src/test/awsService/sagemaker/explorer/sagemakerSpaceNode.test.ts b/packages/core/src/test/awsService/sagemaker/explorer/sagemakerSpaceNode.test.ts new file mode 100644 index 00000000000..37e48f0c1f4 --- /dev/null +++ b/packages/core/src/test/awsService/sagemaker/explorer/sagemakerSpaceNode.test.ts @@ -0,0 +1,80 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as sinon from 'sinon' +import assert from 'assert' +import { AppType } from '@aws-sdk/client-sagemaker' +import { SagemakerClient, SagemakerSpaceApp } from '../../../../shared/clients/sagemaker' +import { SagemakerSpaceNode } from '../../../../awsService/sagemaker/explorer/sagemakerSpaceNode' +import { SagemakerParentNode } from '../../../../awsService/sagemaker/explorer/sagemakerParentNode' + +describe('SagemakerSpaceNode', function () { + const testRegion = 'testRegion' + let client: SagemakerClient + let testParent: SagemakerParentNode + let testSpaceApp: SagemakerSpaceApp + let describeAppStub: sinon.SinonStub + + beforeEach(function () { + client = new SagemakerClient(testRegion) + testParent = new SagemakerParentNode(testRegion, client) + + testSpaceApp = { + SpaceName: 'TestSpace', + DomainId: 'd-12345', + App: { AppName: 'TestApp', Status: 'InService' }, + SpaceSettingsSummary: { AppType: AppType.JupyterLab }, + OwnershipSettingsSummary: { OwnerUserProfileName: 'test-user' }, + SpaceSharingSettingsSummary: { SharingType: 'Private' }, + Status: 'InService', + } + + describeAppStub = sinon.stub(SagemakerClient.prototype, 'describeApp') + }) + + afterEach(function () { + sinon.restore() + }) + + it('initializes with correct label, description, and tooltip', function () { + const node = new SagemakerSpaceNode(testParent, client, testRegion, testSpaceApp) + + assert.strictEqual(node.label, 'TestSpace (Running)') + assert.strictEqual(node.description, 'Private space') + assert.ok(node.tooltip instanceof vscode.MarkdownString) + assert.ok((node.tooltip as vscode.MarkdownString).value.includes('**Space:** TestSpace')) + }) + + it('falls back to defaults if optional fields are missing', function () { + const partialApp: SagemakerSpaceApp = { + SpaceName: undefined, + DomainId: 'domainId', + Status: 'Failed', + } + + const node = new SagemakerSpaceNode(testParent, client, testRegion, partialApp) + + assert.strictEqual(node.label, '(no name) (Failed)') + assert.strictEqual(node.description, 'Unknown space') + assert.ok((node.tooltip as vscode.MarkdownString).value.includes('**Space:** -')) + }) + + it('returns ARN from describeApp', async function () { + describeAppStub.resolves({ AppArn: 'arn:aws:sagemaker:1234:app/TestApp' }) + + const node = new SagemakerSpaceNode(testParent, client, testRegion, testSpaceApp) + const arn = await node.getAppArn() + + assert.strictEqual(arn, 'arn:aws:sagemaker:1234:app/TestApp') + sinon.assert.calledOnce(describeAppStub) + sinon.assert.calledWithExactly(describeAppStub, { + DomainId: 'd-12345', + AppName: 'TestApp', + AppType: AppType.JupyterLab, + SpaceName: 'TestSpace', + }) + }) +}) diff --git a/packages/core/src/test/awsService/sagemaker/utils.test.ts b/packages/core/src/test/awsService/sagemaker/utils.test.ts new file mode 100644 index 00000000000..b7376790106 --- /dev/null +++ b/packages/core/src/test/awsService/sagemaker/utils.test.ts @@ -0,0 +1,66 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AppStatus, SpaceStatus } from '@aws-sdk/client-sagemaker' +import { generateSpaceStatus } from '../../../awsService/sagemaker/utils' +import * as assert from 'assert' + +describe('generateSpaceStatus', function () { + it('returns Failed if space status is Failed', function () { + assert.strictEqual(generateSpaceStatus(SpaceStatus.Failed, AppStatus.InService), 'Failed') + }) + + it('returns Failed if space status is Delete_Failed', function () { + assert.strictEqual(generateSpaceStatus(SpaceStatus.Delete_Failed, AppStatus.InService), 'Failed') + }) + + it('returns Failed if space status is Update_Failed', function () { + assert.strictEqual(generateSpaceStatus(SpaceStatus.Update_Failed, AppStatus.InService), 'Failed') + }) + + it('returns Failed if app status is Failed and space status is not Updating', function () { + assert.strictEqual(generateSpaceStatus(SpaceStatus.Deleting, AppStatus.Failed), 'Failed') + }) + + it('does not return Failed if app status is Failed but space status is Updating', function () { + assert.strictEqual(generateSpaceStatus(SpaceStatus.Updating, AppStatus.Failed), 'Updating') + }) + + it('returns Running if both statuses are InService', function () { + assert.strictEqual(generateSpaceStatus(SpaceStatus.InService, AppStatus.InService), 'Running') + }) + + it('returns Starting if app is Pending and space is InService', function () { + assert.strictEqual(generateSpaceStatus(SpaceStatus.InService, AppStatus.Pending), 'Starting') + }) + + it('returns Updating if space status is Updating', function () { + assert.strictEqual(generateSpaceStatus(SpaceStatus.Updating, AppStatus.Deleting), 'Updating') + }) + + it('returns Stopping if app is Deleting and space is InService', function () { + assert.strictEqual(generateSpaceStatus(SpaceStatus.InService, AppStatus.Deleting), 'Stopping') + }) + + it('returns Stopped if app is Deleted and space is InService', function () { + assert.strictEqual(generateSpaceStatus(SpaceStatus.InService, AppStatus.Deleted), 'Stopped') + }) + + it('returns Stopped if app status is undefined and space is InService', function () { + assert.strictEqual(generateSpaceStatus(SpaceStatus.InService, undefined), 'Stopped') + }) + + it('returns Deleting if space is Deleting', function () { + assert.strictEqual(generateSpaceStatus(SpaceStatus.Deleting, AppStatus.InService), 'Deleting') + }) + + it('returns Unknown if none of the above match', function () { + assert.strictEqual(generateSpaceStatus(undefined, undefined), 'Unknown') + assert.strictEqual( + generateSpaceStatus('SomeOtherStatus' as SpaceStatus, 'RandomAppStatus' as AppStatus), + 'Unknown' + ) + }) +}) diff --git a/packages/core/src/test/shared/clients/sagemakerClient.test.ts b/packages/core/src/test/shared/clients/sagemakerClient.test.ts new file mode 100644 index 00000000000..612903b3833 --- /dev/null +++ b/packages/core/src/test/shared/clients/sagemakerClient.test.ts @@ -0,0 +1,62 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as sinon from 'sinon' +import * as assert from 'assert' +import { SagemakerClient } from '../../../shared/clients/sagemaker' +import { AppDetails, SpaceDetails } from '@aws-sdk/client-sagemaker' +import { intoCollection } from '../../../shared/utilities/collectionUtils' + +describe('SagemakerClient.fetchSpaceApps', function () { + const region = 'test-region' + let client: SagemakerClient + let listAppsStub: sinon.SinonStub + + const appDetails: AppDetails[] = [ + { AppName: 'app1', DomainId: 'domain1', SpaceName: 'space1' }, + { AppName: 'app2', DomainId: 'domain2', SpaceName: 'space2' }, + ] + + const spaceDetails: SpaceDetails[] = [ + { SpaceName: 'space1', DomainId: 'domain1' }, + { SpaceName: 'space2', DomainId: 'domain2' }, + ] + + beforeEach(function () { + client = new SagemakerClient(region) + + listAppsStub = sinon.stub(client, 'listApps').returns(intoCollection([appDetails])) + sinon.stub(client, 'listSpaces').returns(intoCollection([spaceDetails])) + }) + + afterEach(function () { + sinon.restore() + }) + + it('returns a map of space details with corresponding app details', async function () { + const result = await client.fetchSpaceApps() + + assert.strictEqual(result.size, 2) + + const key1 = 'domain1-space1' + const key2 = 'domain2-space2' + + assert.ok(result.has(key1), 'Expected result to have key for domain1-space1') + assert.ok(result.has(key2), 'Expected result to have key for domain2-space2') + + assert.deepStrictEqual(result.get(key1)?.App?.AppName, 'app1') + assert.deepStrictEqual(result.get(key2)?.App?.AppName, 'app2') + }) + + it('returns map even if some spaces have no matching apps', async function () { + listAppsStub.returns(intoCollection([{ AppName: 'app1', DomainId: 'domain1', SpaceName: 'space1' }])) + + const result = await client.fetchSpaceApps() + const key2 = 'domain2-space2' + + assert.strictEqual(result.size, 2) + assert.strictEqual(result.get(key2)?.App, undefined) + }) +}) From 8b4a357cb328ccf40e6355e9c008ffb41f6baa01 Mon Sep 17 00:00:00 2001 From: Newton Der Date: Thu, 19 Jun 2025 11:52:12 -0700 Subject: [PATCH 02/12] feat(sagemaker): Filtering of SageMaker Studio spaces, both manual and automatic (#2129) ## Problem When the user views SageMaker Studio Spaces in the AWS Toolkit extension, by default it shows all Spaces from the `SageMaker:ListSpaces` API call. We want to show only the Spaces relevant to the user. ## Solution We enable both upfront and manual filtering for the user. Upfront filtering - Filter out Unified Studio Spaces - Filter out Spaces not owned by the user, based on detection of their UserProfileName in the response from `STS:GetCallerIdentity`. We support IAM user, IAM Identity Center user, and STS assumed-role session names. Manual filtering - Allows the user to manually filter out Spaces via a dropdown of checkboxes. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --------- Co-authored-by: Alvin Solidum Co-authored-by: Newton Der --- package-lock.json | 1208 +++++++++++++++++ packages/amazonq/package.json | 20 +- packages/core/package.json | 1 + packages/core/package.nls.json | 1 + .../src/awsService/sagemaker/activation.ts | 17 + .../core/src/awsService/sagemaker/commands.ts | 69 + .../sagemaker/explorer/constants.ts | 20 + .../sagemaker/explorer/sagemakerParentNode.ts | 128 +- .../core/src/awsService/sagemaker/utils.ts | 35 + packages/core/src/extensionNode.ts | 3 + .../core/src/shared/clients/clientWrapper.ts | 16 +- packages/core/src/shared/clients/sagemaker.ts | 76 +- packages/core/src/shared/globalState.ts | 2 + .../core/src/shared/settings-toolkit.gen.ts | 3 +- .../explorer/sagemakerParentNode.test.ts | 278 +++- .../shared/clients/sagemakerClient.test.ts | 72 +- packages/core/src/testLint/gitSecrets.test.ts | 2 +- packages/toolkit/package.json | 51 +- src.gen/@amzn/sagemaker-client/1.0.0.tgz | Bin 0 -> 434877 bytes 19 files changed, 1939 insertions(+), 63 deletions(-) create mode 100644 packages/core/src/awsService/sagemaker/activation.ts create mode 100644 packages/core/src/awsService/sagemaker/commands.ts create mode 100644 packages/core/src/awsService/sagemaker/explorer/constants.ts create mode 100644 src.gen/@amzn/sagemaker-client/1.0.0.tgz diff --git a/package-lock.json b/package-lock.json index 2ab471ef8da..5fbd98019e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,6 +68,966 @@ "resolved": "src.gen/@amzn/codewhisperer-streaming", "link": true }, + "node_modules/@amzn/sagemaker-client": { + "version": "1.0.0", + "resolved": "file:src.gen/@amzn/sagemaker-client/1.0.0.tgz", + "integrity": "sha512-rNMUzeACaCiIqR8aQo3G99xR+Qy6zhbGi9+6XRG5proUKetO3584dclmSnIUvDvzLWosFcl4GyP8tFqiahc6Jg==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.363.0", + "@aws-sdk/credential-provider-node": "3.363.0", + "@aws-sdk/middleware-host-header": "3.363.0", + "@aws-sdk/middleware-logger": "3.363.0", + "@aws-sdk/middleware-recursion-detection": "3.363.0", + "@aws-sdk/middleware-signing": "3.363.0", + "@aws-sdk/middleware-user-agent": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.363.0", + "@aws-sdk/util-user-agent-node": "3.363.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-retry": "^1.0.3", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.3", + "@smithy/util-utf8": "^1.0.1", + "@smithy/util-waiter": "^1.0.1", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/client-sso": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.363.0.tgz", + "integrity": "sha512-PZ+HfKSgS4hlMnJzG+Ev8/mgHd/b/ETlJWPSWjC/f2NwVoBQkBnqHjdyEx7QjF6nksJozcVh5Q+kkYLKc/QwBQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.363.0", + "@aws-sdk/middleware-logger": "3.363.0", + "@aws-sdk/middleware-recursion-detection": "3.363.0", + "@aws-sdk/middleware-user-agent": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.363.0", + "@aws-sdk/util-user-agent-node": "3.363.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.1", + "@smithy/middleware-retry": "^1.0.2", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.0.1", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.0.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.2", + "@smithy/util-utf8": "^1.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.363.0.tgz", + "integrity": "sha512-V3Ebiq/zNtDS/O92HUWGBa7MY59RYSsqWd+E0XrXv6VYTA00RlMTbNcseivNgp2UghOgB9a20Nkz6EqAeIN+RQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.363.0", + "@aws-sdk/middleware-logger": "3.363.0", + "@aws-sdk/middleware-recursion-detection": "3.363.0", + "@aws-sdk/middleware-user-agent": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.363.0", + "@aws-sdk/util-user-agent-node": "3.363.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.1", + "@smithy/middleware-retry": "^1.0.2", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.0.1", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.0.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.2", + "@smithy/util-utf8": "^1.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/client-sts": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.363.0.tgz", + "integrity": "sha512-0jj14WvBPJQ8xr72cL0mhlmQ90tF0O0wqXwSbtog6PsC8+KDE6Yf+WsxsumyI8E5O8u3eYijBL+KdqG07F/y/w==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/credential-provider-node": "3.363.0", + "@aws-sdk/middleware-host-header": "3.363.0", + "@aws-sdk/middleware-logger": "3.363.0", + "@aws-sdk/middleware-recursion-detection": "3.363.0", + "@aws-sdk/middleware-sdk-sts": "3.363.0", + "@aws-sdk/middleware-signing": "3.363.0", + "@aws-sdk/middleware-user-agent": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.363.0", + "@aws-sdk/util-user-agent-node": "3.363.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.1", + "@smithy/middleware-retry": "^1.0.1", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.1", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.2", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.1", + "@smithy/util-utf8": "^1.0.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.363.0.tgz", + "integrity": "sha512-VAQ3zITT2Q0acht0HezouYnMFKZ2vIOa20X4zQA3WI0HfaP4D6ga6KaenbDcb/4VFiqfqiRHfdyXHP0ThcDRMA==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.363.0.tgz", + "integrity": "sha512-ZYN+INoqyX5FVC3rqUxB6O8nOWkr0gHRRBm1suoOlmuFJ/WSlW/uUGthRBY5x1AQQnBF8cpdlxZzGHd41lFVNw==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.363.0", + "@aws-sdk/credential-provider-process": "3.363.0", + "@aws-sdk/credential-provider-sso": "3.363.0", + "@aws-sdk/credential-provider-web-identity": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/credential-provider-imds": "^1.0.1", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.363.0.tgz", + "integrity": "sha512-C1qXFIN2yMxD6pGgug0vR1UhScOki6VqdzuBHzXZAGu7MOjvgHNdscEcb3CpWnITHaPL2ztkiw75T1sZ7oIgQg==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.363.0", + "@aws-sdk/credential-provider-ini": "3.363.0", + "@aws-sdk/credential-provider-process": "3.363.0", + "@aws-sdk/credential-provider-sso": "3.363.0", + "@aws-sdk/credential-provider-web-identity": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/credential-provider-imds": "^1.0.1", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.363.0.tgz", + "integrity": "sha512-fOKAINU7Rtj2T8pP13GdCt+u0Ml3gYynp8ki+1jMZIQ+Ju/MdDOqZpKMFKicMn3Z1ttUOgqr+grUdus6z8ceBQ==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.363.0.tgz", + "integrity": "sha512-5RUZ5oM0lwZSo3EehT0dXggOjgtxFogpT3cZvoLGtIwrPBvm8jOQPXQUlaqCj10ThF1sYltEyukz/ovtDwYGew==", + "dependencies": { + "@aws-sdk/client-sso": "3.363.0", + "@aws-sdk/token-providers": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.363.0.tgz", + "integrity": "sha512-Z6w7fjgy79pAax580wdixbStQw10xfyZ+hOYLcPudoYFKjoNx0NQBejg5SwBzCF/HQL23Ksm9kDfbXDX9fkPhA==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.363.0.tgz", + "integrity": "sha512-FobpclDCf5Y1ueyJDmb9MqguAdPssNMlnqWQpujhYVABq69KHu73fSCWSauFPUrw7YOpV8kG1uagDF0POSxHzA==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-logger": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.363.0.tgz", + "integrity": "sha512-SSGgthScYnFGTOw8EzbkvquqweFmvn7uJihkpFekbtBNGC/jGOGO+8ziHjTQ8t/iI/YKubEwv+LMi0f77HKSEg==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.363.0.tgz", + "integrity": "sha512-MWD/57QgI/N7fG8rtzDTUdSqNpYohQfgj9XCFAoVeI/bU4usrkOrew43L4smJG4XrDxlNT8lSJlDtd64tuiUZA==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.363.0.tgz", + "integrity": "sha512-ri8YaQvXP6odteVTMfxPqFR26Q0h9ejtqhUDv47P34FaKXedEM4nC6ix6o+5FEYj6l8syGyktftZ5O70NoEhug==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/token-providers": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.363.0.tgz", + "integrity": "sha512-6+0aJ1zugNgsMmhTtW2LBWxOVSaXCUk2q3xyTchSXkNzallYaRiZMRkieW+pKNntnu0g5H1T0zyfCO0tbXwxEA==", + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/types": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.357.0.tgz", + "integrity": "sha512-/riCRaXg3p71BeWnShrai0y0QTdXcouPSM0Cn1olZbzTf7s71aLEewrc96qFrL70XhY4XvnxMpqQh+r43XIL3g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/util-endpoints": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.357.0.tgz", + "integrity": "sha512-XHKyS5JClT9su9hDif715jpZiWHQF9gKZXER8tW0gOizU3R9cyWc9EsJ2BRhFNhi7nt/JF/CLUEc5qDx3ETbUw==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.363.0.tgz", + "integrity": "sha512-fk9ymBUIYbxiGm99Cn+kAAXmvMCWTf/cHAcB79oCXV4ELXdPa9lN5xQhZRFNxLUeXG4OAMEuCAUUuZEj8Fnc1Q==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/types": "^1.1.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.363.0.tgz", + "integrity": "sha512-Fli/dvgGA9hdnQUrYb1//wNSFlK2jAfdJcfNXA6SeBYzSeH5pVGYF4kXF0FCdnMA3Fef+Zn1zAP/hw9v8VJHWQ==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-5imgGUlZL4dW4YWdMYAKLmal9ny/tlenM81QZY7xYyb76z9Z/QOg7oM5Ak9HQl8QfFTlGVWwcMXl+54jroRgEQ==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/config-resolver": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-1.1.0.tgz", + "integrity": "sha512-7WD9eZHp46BxAjNGHJLmxhhyeiNWkBdVStd7SUJPUZqQGeIO/REtIrcIfKUfdiHTQ9jyu2SYoqvzqqaFc6987w==", + "dependencies": { + "@smithy/types": "^1.2.0", + "@smithy/util-config-provider": "^1.1.0", + "@smithy/util-middleware": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/credential-provider-imds": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-1.1.0.tgz", + "integrity": "sha512-kUMOdEu3RP6ozH0Ga8OeMP8gSkBsK1UqZZKyPLFnpZHrtZuHSSt7M7gsHYB/bYQBZAo3o7qrGmRty3BubYtYxQ==", + "dependencies": { + "@smithy/node-config-provider": "^1.1.0", + "@smithy/property-provider": "^1.2.0", + "@smithy/types": "^1.2.0", + "@smithy/url-parser": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/fetch-http-handler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-1.1.0.tgz", + "integrity": "sha512-N22C9R44u5WGlcY+Wuv8EXmCAq62wWwriRAuoczMEwAIjPbvHSthyPSLqI4S7kAST1j6niWg8kwpeJ3ReAv3xg==", + "dependencies": { + "@smithy/protocol-http": "^1.2.0", + "@smithy/querystring-builder": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-base64": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/hash-node": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-1.1.0.tgz", + "integrity": "sha512-yiNKDGMzrQjnpnbLfkYKo+HwIxmBAsv0AI++QIJwvhfkLpUTBylelkv6oo78/YqZZS6h+bGfl0gILJsKE2wAKQ==", + "dependencies": { + "@smithy/types": "^1.2.0", + "@smithy/util-buffer-from": "^1.1.0", + "@smithy/util-utf8": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/invalid-dependency": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-1.1.0.tgz", + "integrity": "sha512-h2rXn68ClTwzPXYzEUNkz+0B/A0Hz8YdFNTiEwlxkwzkETGKMxmsrQGFXwYm3jd736R5vkXcClXz1ddKrsaBEQ==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/is-array-buffer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-1.1.0.tgz", + "integrity": "sha512-twpQ/n+3OWZJ7Z+xu43MJErmhB/WO/mMTnqR6PwWQShvSJ/emx5d1N59LQZk6ZpTAeuRWrc+eHhkzTp9NFjNRQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-content-length": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-1.1.0.tgz", + "integrity": "sha512-iNxwhZ7Xc5+LjeDElEOi/Nh8fFsc9Dw9+5w7h7/GLFIU0RgAwBJuJtcP1vNTOwzW4B3hG+gRu8sQLqA9OEaTwA==", + "dependencies": { + "@smithy/protocol-http": "^1.2.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-endpoint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-1.1.0.tgz", + "integrity": "sha512-PvpazNjVpxX2ICrzoFYCpFnjB39DKCpZds8lRpAB3p6HGrx6QHBaNvOzVhJGBf0jcAbfCdc5/W0n9z8VWaSSww==", + "dependencies": { + "@smithy/middleware-serde": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/url-parser": "^1.1.0", + "@smithy/util-middleware": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-retry": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-1.1.0.tgz", + "integrity": "sha512-lINKYxIvT+W20YFOtHBKeGm7npuJg0/YCoShttU7fVpsmU+a2rdb9zrJn1MHqWfUL6DhTAWGa0tH2O7l4XrDcw==", + "dependencies": { + "@smithy/protocol-http": "^1.2.0", + "@smithy/service-error-classification": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-middleware": "^1.1.0", + "@smithy/util-retry": "^1.1.0", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-serde": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-1.1.0.tgz", + "integrity": "sha512-RiBMxhxuO9VTjHsjJvhzViyceoLhU6gtrnJGpAXY43wE49IstXIGEQz8MT50/hOq5EumX16FCpup0r5DVyfqNQ==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-stack": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-1.1.0.tgz", + "integrity": "sha512-XynYiIvXNea2BbLcppvpNK0zu8o2woJqgnmxqYTn4FWagH/Hr2QIk8LOsUz7BIJ4tooFhmx8urHKCdlPbbPDCA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/node-config-provider": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-1.1.0.tgz", + "integrity": "sha512-2G4TlzUnmTrUY26VKTonQqydwb+gtM/mcl+TqDP8CnWtJKVL8ElPpKgLGScP04bPIRY9x2/10lDdoaRXDqPuCw==", + "dependencies": { + "@smithy/property-provider": "^1.2.0", + "@smithy/shared-ini-file-loader": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/node-http-handler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-1.1.0.tgz", + "integrity": "sha512-d3kRriEgaIiGXLziAM8bjnaLn1fthCJeTLZIwEIpzQqe6yPX0a+yQoLCTyjb2fvdLwkMoG4p7THIIB5cj5lkbg==", + "dependencies": { + "@smithy/abort-controller": "^1.1.0", + "@smithy/protocol-http": "^1.2.0", + "@smithy/querystring-builder": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/property-provider": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-1.2.0.tgz", + "integrity": "sha512-qlJd9gT751i4T0t/hJAyNGfESfi08Fek8QiLcysoKPgR05qHhG0OYhlaCJHhpXy4ECW0lHyjvFM1smrCLIXVfw==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/protocol-http": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.2.0.tgz", + "integrity": "sha512-GfGfruksi3nXdFok5RhgtOnWe5f6BndzYfmEXISD+5gAGdayFGpjWu5pIqIweTudMtse20bGbc+7MFZXT1Tb8Q==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/querystring-builder": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-1.1.0.tgz", + "integrity": "sha512-gDEi4LxIGLbdfjrjiY45QNbuDmpkwh9DX4xzrR2AzjjXpxwGyfSpbJaYhXARw9p17VH0h9UewnNQXNwaQyYMDA==", + "dependencies": { + "@smithy/types": "^1.2.0", + "@smithy/util-uri-escape": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/querystring-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-1.1.0.tgz", + "integrity": "sha512-Lm/FZu2qW3XX+kZ4WPwr+7aAeHf1Lm84UjNkKyBu16XbmEV7ukfhXni2aIwS2rcVf8Yv5E7wchGGpOFldj9V4Q==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/service-error-classification": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-1.1.0.tgz", + "integrity": "sha512-OCTEeJ1igatd5kFrS2VDlYbainNNpf7Lj1siFOxnRWqYOP9oNvC5HOJBd3t+Z8MbrmehBtuDJ2QqeBsfeiNkww==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/shared-ini-file-loader": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-1.1.0.tgz", + "integrity": "sha512-S/v33zvCWzFyGZGlsEF0XsZtNNR281UhR7byk3nRfsgw5lGpg51rK/zjMgulM+h6NSuXaFILaYrw1I1v4kMcuA==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/smithy-client": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-1.1.0.tgz", + "integrity": "sha512-j32SGgVhv2G9nBTmel9u3OXux8KG20ssxuFakJrEeDug3kqbl1qrGzVLCe+Eib402UDtA0Sp1a4NZ2SEXDBxag==", + "dependencies": { + "@smithy/middleware-stack": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-stream": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", + "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/url-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-1.1.0.tgz", + "integrity": "sha512-tpvi761kzboiLNGEWczuybMPCJh6WHB3cz9gWAG95mSyaKXmmX8ZcMxoV+irZfxDqLwZVJ22XTumu32S7Ow8aQ==", + "dependencies": { + "@smithy/querystring-parser": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-base64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-1.1.0.tgz", + "integrity": "sha512-FpYmDmVbOXAxqvoVCwqehUN0zXS+lN8V7VS9O7I8MKeVHdSTsZzlwiMEvGoyTNOXWn8luF4CTDYgNHnZViR30g==", + "dependencies": { + "@smithy/util-buffer-from": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-body-length-browser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-1.1.0.tgz", + "integrity": "sha512-cep3ioRxzRZ2Jbp3Kly7gy6iNVefYXiT6ETt8W01RQr3uwi1YMkrbU1p3lMR4KhX/91Nrk6UOgX1RH+oIt48RQ==", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-body-length-node": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-1.1.0.tgz", + "integrity": "sha512-fRHRjkUuT5em4HZoshySXmB1n3HAU7IS232s+qU4TicexhyGJpXMK/2+c56ePOIa1FOK2tV1Q3J/7Mae35QVSw==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-buffer-from": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-1.1.0.tgz", + "integrity": "sha512-9m6NXE0ww+ra5HKHCHig20T+FAwxBAm7DIdwc/767uGWbRcY720ybgPacQNB96JMOI7xVr/CDa3oMzKmW4a+kw==", + "dependencies": { + "@smithy/is-array-buffer": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-config-provider": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-1.1.0.tgz", + "integrity": "sha512-rQ47YpNmF6Is4I9GiE3T3+0xQ+r7RKRKbmHYyGSbyep/0cSf9kteKcI0ssJTvveJ1K4QvwrxXj1tEFp/G2UqxQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-defaults-mode-browser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-1.1.0.tgz", + "integrity": "sha512-0bWhs1e412bfC5gwPCMe8Zbz0J8UoZ/meEQdo6MYj8Ne+c+QZ+KxVjx0a1dFYOclvM33SslL9dP0odn8kfblkg==", + "dependencies": { + "@smithy/property-provider": "^1.2.0", + "@smithy/types": "^1.2.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-defaults-mode-node": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-1.1.0.tgz", + "integrity": "sha512-440e25TUH2b+TeK5CwsjYFrI9ShVOgA31CoxCKiv4ncSK4ZM68XW5opYxQmzMbRWARGEMu2XEUeBmOgMU2RLsw==", + "dependencies": { + "@smithy/config-resolver": "^1.1.0", + "@smithy/credential-provider-imds": "^1.1.0", + "@smithy/node-config-provider": "^1.1.0", + "@smithy/property-provider": "^1.2.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-hex-encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-1.1.0.tgz", + "integrity": "sha512-7UtIE9eH0u41zpB60Jzr0oNCQ3hMJUabMcKRUVjmyHTXiWDE4vjSqN6qlih7rCNeKGbioS7f/y2Jgym4QZcKFg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-middleware": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-1.1.0.tgz", + "integrity": "sha512-6hhckcBqVgjWAqLy2vqlPZ3rfxLDhFWEmM7oLh2POGvsi7j0tHkbN7w4DFhuBExVJAbJ/qqxqZdRY6Fu7/OezQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-retry": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-1.1.0.tgz", + "integrity": "sha512-ygQW5HBqYXpR3ua09UciS0sL7UGJzGiktrKkOuEJwARoUuzz40yaEGU6xd9Gs7KBmAaFC8gMfnghHtwZ2nyBCQ==", + "dependencies": { + "@smithy/service-error-classification": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-1.1.0.tgz", + "integrity": "sha512-w3lsdGsntaLQIrwDWJkIFKrFscgZXwU/oxsse09aSTNv5TckPhDeYea3LhsDrU5MGAG3vprhVZAKr33S45coVA==", + "dependencies": { + "@smithy/fetch-http-handler": "^1.1.0", + "@smithy/node-http-handler": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-base64": "^1.1.0", + "@smithy/util-buffer-from": "^1.1.0", + "@smithy/util-hex-encoding": "^1.1.0", + "@smithy/util-utf8": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-uri-escape": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-1.1.0.tgz", + "integrity": "sha512-/jL/V1xdVRt5XppwiaEU8Etp5WHZj609n0xMTuehmCqdoOFbId1M+aEeDWZsQ+8JbEB/BJ6ynY2SlYmOaKtt8w==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-1.1.0.tgz", + "integrity": "sha512-p/MYV+JmqmPyjdgyN2UxAeYDj9cBqCjp0C/NsTWnnjoZUVqoeZ6IrW915L9CAKWVECgv9lVQGc4u/yz26/bI1A==", + "dependencies": { + "@smithy/util-buffer-from": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-waiter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-1.1.0.tgz", + "integrity": "sha512-S6FNIB3UJT+5Efd/0DeziO5Rs82QAMODHW4v2V3oNRrwaBigY/7Yx3SiLudZuF9WpVsV08Ih3BjIH34nzZiinQ==", + "dependencies": { + "@smithy/abort-controller": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", "license": "Apache-2.0", @@ -89,6 +1049,19 @@ "tslib": "^2.6.2" } }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@aws-crypto/sha1-browser": { "version": "5.2.0", "license": "Apache-2.0", @@ -10315,6 +11288,232 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.363.0.tgz", + "integrity": "sha512-1yy2Ac50FO8BrODaw5bPWvVrRhaVLqXTFH6iHB+dJLPUkwtY5zLM3Mp+9Ilm7kME+r7oIB1wuO6ZB1Lf4ZszIw==", + "dependencies": { + "@aws-sdk/middleware-signing": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sts/node_modules/@aws-sdk/types": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.357.0.tgz", + "integrity": "sha512-/riCRaXg3p71BeWnShrai0y0QTdXcouPSM0Cn1olZbzTf7s71aLEewrc96qFrL70XhY4XvnxMpqQh+r43XIL3g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sts/node_modules/@smithy/types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", + "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.363.0.tgz", + "integrity": "sha512-/7qia715pt9JKYIPDGu22WmdZxD8cfF/5xB+1kmILg7ZtjO0pPuTaCNJ7xiIuFd7Dn7JXp5lop08anX/GOhNRQ==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/protocol-http": "^1.1.0", + "@smithy/signature-v4": "^1.0.1", + "@smithy/types": "^1.1.0", + "@smithy/util-middleware": "^1.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@aws-sdk/types": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.357.0.tgz", + "integrity": "sha512-/riCRaXg3p71BeWnShrai0y0QTdXcouPSM0Cn1olZbzTf7s71aLEewrc96qFrL70XhY4XvnxMpqQh+r43XIL3g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/eventstream-codec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-1.1.0.tgz", + "integrity": "sha512-3tEbUb8t8an226jKB6V/Q2XU/J53lCwCzULuBPEaF4JjSh+FlCMp7TmogE/Aij5J9DwlsZ4VAD/IRDuQ/0ZtMw==", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^1.2.0", + "@smithy/util-hex-encoding": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/is-array-buffer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-1.1.0.tgz", + "integrity": "sha512-twpQ/n+3OWZJ7Z+xu43MJErmhB/WO/mMTnqR6PwWQShvSJ/emx5d1N59LQZk6ZpTAeuRWrc+eHhkzTp9NFjNRQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/property-provider": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-1.2.0.tgz", + "integrity": "sha512-qlJd9gT751i4T0t/hJAyNGfESfi08Fek8QiLcysoKPgR05qHhG0OYhlaCJHhpXy4ECW0lHyjvFM1smrCLIXVfw==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/protocol-http": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.2.0.tgz", + "integrity": "sha512-GfGfruksi3nXdFok5RhgtOnWe5f6BndzYfmEXISD+5gAGdayFGpjWu5pIqIweTudMtse20bGbc+7MFZXT1Tb8Q==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/signature-v4": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-1.1.0.tgz", + "integrity": "sha512-fDo3m7YqXBs7neciOePPd/X9LPm5QLlDMdIC4m1H6dgNLnXfLMFNIxEfPyohGA8VW9Wn4X8lygnPSGxDZSmp0Q==", + "dependencies": { + "@smithy/eventstream-codec": "^1.1.0", + "@smithy/is-array-buffer": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-hex-encoding": "^1.1.0", + "@smithy/util-middleware": "^1.1.0", + "@smithy/util-uri-escape": "^1.1.0", + "@smithy/util-utf8": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", + "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/util-buffer-from": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-1.1.0.tgz", + "integrity": "sha512-9m6NXE0ww+ra5HKHCHig20T+FAwxBAm7DIdwc/767uGWbRcY720ybgPacQNB96JMOI7xVr/CDa3oMzKmW4a+kw==", + "dependencies": { + "@smithy/is-array-buffer": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/util-hex-encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-1.1.0.tgz", + "integrity": "sha512-7UtIE9eH0u41zpB60Jzr0oNCQ3hMJUabMcKRUVjmyHTXiWDE4vjSqN6qlih7rCNeKGbioS7f/y2Jgym4QZcKFg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/util-middleware": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-1.1.0.tgz", + "integrity": "sha512-6hhckcBqVgjWAqLy2vqlPZ3rfxLDhFWEmM7oLh2POGvsi7j0tHkbN7w4DFhuBExVJAbJ/qqxqZdRY6Fu7/OezQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/util-uri-escape": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-1.1.0.tgz", + "integrity": "sha512-/jL/V1xdVRt5XppwiaEU8Etp5WHZj609n0xMTuehmCqdoOFbId1M+aEeDWZsQ+8JbEB/BJ6ynY2SlYmOaKtt8w==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/util-utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-1.1.0.tgz", + "integrity": "sha512-p/MYV+JmqmPyjdgyN2UxAeYDj9cBqCjp0C/NsTWnnjoZUVqoeZ6IrW915L9CAKWVECgv9lVQGc4u/yz26/bI1A==", + "dependencies": { + "@smithy/util-buffer-from": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/middleware-ssec": { "version": "3.693.0", "license": "Apache-2.0", @@ -11370,6 +12569,14 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "dependencies": { + "tslib": "^2.3.1" + } + }, "node_modules/@aws-sdk/xml-builder": { "version": "3.693.0", "license": "Apache-2.0", @@ -26199,6 +27406,7 @@ "dependencies": { "@amzn/amazon-q-developer-streaming-client": "file:../../src.gen/@amzn/amazon-q-developer-streaming-client", "@amzn/codewhisperer-streaming": "file:../../src.gen/@amzn/codewhisperer-streaming", + "@amzn/sagemaker-client": "file:../../src.gen/@amzn/sagemaker-client/1.0.0.tgz", "@aws-sdk/client-api-gateway": "<3.731.0", "@aws-sdk/client-apprunner": "<3.731.0", "@aws-sdk/client-cloudcontrol": "<3.731.0", diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index 8f398613ffb..54147a40321 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -1277,26 +1277,40 @@ "fontCharacter": "\\f1dc" } }, - "aws-schemas-registry": { + "aws-sagemaker-code-editor": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1dd" } }, - "aws-schemas-schema": { + "aws-sagemaker-jupyter-lab": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1de" } }, - "aws-stepfunctions-preview": { + "aws-schemas-registry": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1df" } + }, + "aws-schemas-schema": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e0" + } + }, + "aws-stepfunctions-preview": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e1" + } } }, "walkthroughs": [ diff --git a/packages/core/package.json b/packages/core/package.json index d3ab32bf32e..a9c7dfbd0f1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -515,6 +515,7 @@ "dependencies": { "@amzn/amazon-q-developer-streaming-client": "file:../../src.gen/@amzn/amazon-q-developer-streaming-client", "@amzn/codewhisperer-streaming": "file:../../src.gen/@amzn/codewhisperer-streaming", + "@amzn/sagemaker-client": "file:../../src.gen/@amzn/sagemaker-client/1.0.0.tgz", "@aws-sdk/client-api-gateway": "<3.731.0", "@aws-sdk/client-apprunner": "<3.731.0", "@aws-sdk/client-cloudcontrol": "<3.731.0", diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index aa1ac167917..8daff18a48d 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -225,6 +225,7 @@ "AWS.command.s3.createFolder": "Create Folder...", "AWS.command.s3.uploadFile": "Upload Files...", "AWS.command.s3.uploadFileToParent": "Upload to Parent...", + "AWS.command.sagemaker.filterSpaces": "Filter Sagemaker Spaces", "AWS.command.stepFunctions.createStateMachineFromTemplate": "Create a new Step Functions state machine", "AWS.command.stepFunctions.publishStateMachine": "Publish state machine to Step Functions", "AWS.command.stepFunctions.openWithWorkflowStudio": "Open with Workflow Studio", diff --git a/packages/core/src/awsService/sagemaker/activation.ts b/packages/core/src/awsService/sagemaker/activation.ts new file mode 100644 index 00000000000..4de3fb533db --- /dev/null +++ b/packages/core/src/awsService/sagemaker/activation.ts @@ -0,0 +1,17 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Commands } from '../../shared/vscode/commands2' +import { ExtContext } from '../../shared/extensions' +import { SagemakerParentNode } from './explorer/sagemakerParentNode' +import { filterSpaceAppsByDomainUserProfiles } from './commands' + +export async function activate(ctx: ExtContext): Promise { + ctx.extensionContext.subscriptions.push( + Commands.register('aws.sagemaker.filterSpaceApps', async (node: SagemakerParentNode) => { + await filterSpaceAppsByDomainUserProfiles(node) + }) + ) +} diff --git a/packages/core/src/awsService/sagemaker/commands.ts b/packages/core/src/awsService/sagemaker/commands.ts new file mode 100644 index 00000000000..19032190dfb --- /dev/null +++ b/packages/core/src/awsService/sagemaker/commands.ts @@ -0,0 +1,69 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as nls from 'vscode-nls' +import { getLogger } from '../../shared/logger/logger' +import { SagemakerConstants } from './explorer/constants' +import { SagemakerParentNode } from './explorer/sagemakerParentNode' +import { DomainKeyDelimiter } from './utils' + +const localize = nls.loadMessageBundle() + +export async function filterSpaceAppsByDomainUserProfiles(parentNode: SagemakerParentNode): Promise { + if (parentNode.domainUserProfiles.size === 0) { + // if parentNode has not been expanded, domainUserProfiles will be empty + // if so, this will attempt to populate domainUserProfiles + await parentNode.updateChildren() + if (parentNode.domainUserProfiles.size === 0) { + getLogger().info(SagemakerConstants.NoSpaceToFilter) + void vscode.window.showInformationMessage(SagemakerConstants.NoSpaceToFilter) + return + } + } + + // Sort by domain name and user profile + const sortedDomainUserProfiles = new Map( + [...parentNode.domainUserProfiles].sort((a, b) => { + const domainNameA = a[1].domain.DomainName || '' + const domainNameB = b[1].domain.DomainName || '' + + const [_domainIdA, userProfileA] = a[0].split(DomainKeyDelimiter) + const [_domainIdB, userProfileB] = b[0].split(DomainKeyDelimiter) + + return domainNameA.localeCompare(domainNameB) || userProfileA.localeCompare(userProfileB) + }) + ) + + const previousSelection = await parentNode.getSelectedDomainUsers() + const items: (vscode.QuickPickItem & { key: string })[] = [] + + for (const [key, userMetadata] of sortedDomainUserProfiles) { + const [_, userProfile] = key.split(DomainKeyDelimiter) + items.push({ + label: userProfile, + detail: `In domain: ${userMetadata.domain?.DomainName}`, + picked: previousSelection.has(key), + key, + }) + } + + const placeholder = localize(SagemakerConstants.FilterPlaceholderKey, SagemakerConstants.FilterPlaceholderMessage) + const result = await vscode.window.showQuickPick(items, { + placeHolder: placeholder, + canPickMany: true, + matchOnDetail: true, + }) + + if (!result) { + return // User canceled. + } + + const newSelection = result.map((r) => r.key) + if (newSelection.length !== previousSelection.size || newSelection.some((key) => !previousSelection.has(key))) { + parentNode.saveSelectedDomainUsers(newSelection) + await vscode.commands.executeCommand('aws.refreshAwsExplorerNode', parentNode) + } +} diff --git a/packages/core/src/awsService/sagemaker/explorer/constants.ts b/packages/core/src/awsService/sagemaker/explorer/constants.ts new file mode 100644 index 00000000000..b9d7c3348b5 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/explorer/constants.ts @@ -0,0 +1,20 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +export abstract class SagemakerConstants { + static readonly PlaceHolderMessage = '[No Sagemaker Spaces Found]' + static readonly EnableIdentityFilteringSetting = 'aws.sagemaker.studio.spaces.enableIdentityFiltering' + static readonly SelectedDomainUsersState = 'aws.sagemaker.selectedDomainUsers' + static readonly FilterPlaceholderKey = 'aws.filterSagemakerSpacesPlaceholder' + static readonly FilterPlaceholderMessage = 'Filter spaces by user profile or domain (unselect to hide)' + static readonly NoSpaceToFilter = 'No spaces to filter' + + static readonly IamUserArnRegex = /^arn:aws[a-z\-]*:iam::\d{12}:user\/?([a-zA-Z_0-9+=,.@\-_]+)$/ + static readonly IamSessionArnRegex = + /^arn:aws[a-z\-]*:sts::\d{12}:assumed-role\/?[a-zA-Z_0-9+=,.@\-_]+\/([a-zA-Z_0-9+=,.@\-_]+)$/ + static readonly IdentityCenterArnRegex = + /^arn:aws[a-z\-]*:sts::\d{12}:assumed-role\/?AWSReservedSSO[a-zA-Z_0-9+=,.@\-_]+\/([a-zA-Z_0-9+=,.@\-_]+)$/ + static readonly SpecialCharacterRegex = /[+=,.@\-_]/g +} diff --git a/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts b/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts index 225295ad670..902cd68fedc 100644 --- a/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts +++ b/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts @@ -4,19 +4,34 @@ */ import * as vscode from 'vscode' +import { GetCallerIdentityResponse } from 'aws-sdk/clients/sts' +import { DescribeDomainResponse } from '@amzn/sagemaker-client' +import { SagemakerClient, SagemakerSpaceApp } from '../../../shared/clients/sagemaker' +import { DefaultStsClient } from '../../../shared/clients/stsClient' +import globals from '../../../shared/extensionGlobals' import { AWSTreeNodeBase } from '../../../shared/treeview/nodes/awsTreeNodeBase' -import { SagemakerClient } from '../../../shared/clients/sagemaker' +import { PlaceholderNode } from '../../../shared/treeview/nodes/placeholderNode' import { makeChildrenNodes } from '../../../shared/treeview/utils' import { updateInPlace } from '../../../shared/utilities/collectionUtils' +import { isRemoteWorkspace } from '../../../shared/vscode/env' +import { SagemakerConstants } from './constants' import { SagemakerSpaceNode } from './sagemakerSpaceNode' -import { PlaceholderNode } from '../../../shared/treeview/nodes/placeholderNode' +import { getDomainSpaceKey, getDomainUserProfileKey, getRemoteAppMetadata, getSpaceAppsForUserProfile } from '../utils' export const parentContextValue = 'awsSagemakerParentNode' +export type SelectedDomainUsers = [string, string[]][] + +export interface UserProfileMetadata { + domain: DescribeDomainResponse +} export class SagemakerParentNode extends AWSTreeNodeBase { - protected readonly placeHolderMessage = '[No Sagemaker Spaces Found]' protected sagemakerSpaceNodes: Map + protected stsClient: DefaultStsClient public override readonly contextValue: string = parentContextValue + domainUserProfiles: Map = new Map() + spaceApps: Map = new Map() + callerIdentity: GetCallerIdentityResponse = {} public constructor( public override readonly regionCode: string, @@ -24,6 +39,7 @@ export class SagemakerParentNode extends AWSTreeNodeBase { ) { super('Sagemaker', vscode.TreeItemCollapsibleState.Collapsed) this.sagemakerSpaceNodes = new Map() + this.stsClient = new DefaultStsClient(regionCode) } public override async getChildren(): Promise { @@ -32,21 +48,117 @@ export class SagemakerParentNode extends AWSTreeNodeBase { await this.updateChildren() return [...this.sagemakerSpaceNodes.values()] }, - getNoChildrenPlaceholderNode: async () => new PlaceholderNode(this, this.placeHolderMessage), + getNoChildrenPlaceholderNode: async () => new PlaceholderNode(this, SagemakerConstants.PlaceHolderMessage), sort: (nodeA, nodeB) => nodeA.name.localeCompare(nodeB.name), }) return result } + public async getLocalSelectedDomainUsers(): Promise { + /** + * By default, filter userProfileNames that match the detected IAM user, IAM assumed role + * session name, or Identity Center username + * */ + const iamMatches = + this.callerIdentity.Arn?.match(SagemakerConstants.IamUserArnRegex) || + this.callerIdentity.Arn?.match(SagemakerConstants.IamSessionArnRegex) + const idcMatches = this.callerIdentity.Arn?.match(SagemakerConstants.IdentityCenterArnRegex) + + const matches = + /** + * Only filter IAM users / assumed-role sessions if the user has enabled this option + * Or filter Identity Center username if user is authenticated via IdC + * */ + iamMatches && vscode.workspace.getConfiguration().get(SagemakerConstants.EnableIdentityFilteringSetting) + ? iamMatches + : idcMatches + ? idcMatches + : undefined + + const userProfilePrefix = + matches && matches.length >= 2 + ? `${matches[1].replaceAll(SagemakerConstants.SpecialCharacterRegex, '-')}-` + : '' + + return getSpaceAppsForUserProfile([...this.spaceApps.values()], userProfilePrefix) + } + + public async getRemoteSelectedDomainUsers(): Promise { + const remoteAppMetadata = getRemoteAppMetadata() + const userProfilePrefix = `${remoteAppMetadata.UserProfileName}-` + + return getSpaceAppsForUserProfile([...this.spaceApps.values()], userProfilePrefix) + } + + public async getDefaultSelectedDomainUsers(): Promise { + if (isRemoteWorkspace()) { + return this.getRemoteSelectedDomainUsers() + } else { + return this.getLocalSelectedDomainUsers() + } + } + + public async getSelectedDomainUsers(): Promise> { + const selectedDomainUsersMap = new Map( + globals.globalState.get(SagemakerConstants.SelectedDomainUsersState, []) + ) + + const defaultSelectedDomainUsers = await this.getDefaultSelectedDomainUsers() + + // Get selectedDomainUsers from globalState. If it doesn't exist, then default to defaultSelectedDomainUsers + const selectedDomainUsers = new Set( + selectedDomainUsersMap?.get(this.callerIdentity?.Arn || '') || defaultSelectedDomainUsers + ) + + return selectedDomainUsers + } + + public saveSelectedDomainUsers(selectedDomainUsers: string[]) { + const selectedDomainUsersMap = new Map( + globals.globalState.get(SagemakerConstants.SelectedDomainUsersState, []) + ) + + if (this.callerIdentity.Arn) { + selectedDomainUsersMap?.set(this.callerIdentity.Arn, selectedDomainUsers) + globals.globalState.tryUpdate(SagemakerConstants.SelectedDomainUsersState, [...selectedDomainUsersMap]) + } + } + public async updateChildren(): Promise { - const spaceAppMap = await this.sagemakerClient.fetchSpaceApps() + const [spaceApps, domains] = await this.sagemakerClient.fetchSpaceAppsAndDomains() + this.spaceApps = spaceApps + + this.callerIdentity = await this.stsClient.getCallerIdentity() + const selectedDomainUsers = await this.getSelectedDomainUsers() + this.domainUserProfiles.clear() + + for (const app of spaceApps.values()) { + const domainId = app.DomainId + const userProfile = app.OwnershipSettingsSummary?.OwnerUserProfileName + if (!domainId || !userProfile) { + continue + } + + // populate domainUserProfiles for filtering + const domainUserProfileKey = getDomainUserProfileKey(domainId, userProfile) + const domainSpaceKey = getDomainSpaceKey(domainId, app.SpaceName || '') + + this.domainUserProfiles.set(domainUserProfileKey, { + domain: domains.get(domainId) as DescribeDomainResponse, + }) + + if (!selectedDomainUsers.has(domainUserProfileKey) && app.SpaceName) { + spaceApps.delete(domainSpaceKey) + continue + } + } updateInPlace( this.sagemakerSpaceNodes, - spaceAppMap.keys(), - (key) => this.sagemakerSpaceNodes.get(key)!.updateSpace(spaceAppMap.get(key)!), - (key) => new SagemakerSpaceNode(this, this.sagemakerClient, this.regionCode, spaceAppMap.get(key)!) + spaceApps.keys(), + (key) => this.sagemakerSpaceNodes.get(key)!.updateSpace(spaceApps.get(key)!), + (key) => new SagemakerSpaceNode(this, this.sagemakerClient, this.regionCode, spaceApps.get(key)!) ) } } diff --git a/packages/core/src/awsService/sagemaker/utils.ts b/packages/core/src/awsService/sagemaker/utils.ts index a770e6ee42c..c7cd00c8b8f 100644 --- a/packages/core/src/awsService/sagemaker/utils.ts +++ b/packages/core/src/awsService/sagemaker/utils.ts @@ -4,6 +4,17 @@ */ import { AppStatus, SpaceStatus } from '@aws-sdk/client-sagemaker' +import { SagemakerSpaceApp } from '../../shared/clients/sagemaker' + +export const DomainKeyDelimiter = '__' + +export function getDomainSpaceKey(domainId: string, spaceName: string): string { + return `${domainId}${DomainKeyDelimiter}${spaceName}` +} + +export function getDomainUserProfileKey(domainId: string, userProfileName: string): string { + return `${domainId}${DomainKeyDelimiter}${userProfileName}` +} export function generateSpaceStatus(spaceStatus?: string, appStatus?: string) { if ( @@ -41,3 +52,27 @@ export function generateSpaceStatus(spaceStatus?: string, appStatus?: string) { return 'Unknown' } + +export interface RemoteAppMetadata { + DomainId: string + UserProfileName: string +} + +export function getRemoteAppMetadata(): RemoteAppMetadata { + return { + DomainId: 'd-abcdefg123456', + UserProfileName: 'dernewtz-jorus', + } +} + +export function getSpaceAppsForUserProfile(spaceApps: SagemakerSpaceApp[], userProfilePrefix: string): string[] { + return spaceApps.reduce((result: string[], app: SagemakerSpaceApp) => { + if (app.OwnershipSettingsSummary?.OwnerUserProfileName?.startsWith(userProfilePrefix)) { + result.push( + getDomainUserProfileKey(app.DomainId || '', app.OwnershipSettingsSummary?.OwnerUserProfileName || '') + ) + } + + return result + }, [] as string[]) +} diff --git a/packages/core/src/extensionNode.ts b/packages/core/src/extensionNode.ts index 8e759263623..97785456e9b 100644 --- a/packages/core/src/extensionNode.ts +++ b/packages/core/src/extensionNode.ts @@ -41,6 +41,7 @@ import { activate as activateRedshift } from './awsService/redshift/activation' import { activate as activateDocumentDb } from './docdb/activation' import { activate as activateIamPolicyChecks } from './awsService/accessanalyzer/activation' import { activate as activateNotifications } from './notifications/activation' +import { activate as activateSagemaker } from './awsService/sagemaker/activation' import { SchemaService } from './shared/schemas' import { AwsResourceManager } from './dynamicResources/awsResourceManager' import globals from './shared/extensionGlobals' @@ -185,6 +186,8 @@ export async function activate(context: vscode.ExtensionContext) { await activateSchemas(extContext) + await activateSagemaker(extContext) + if (!isSageMaker()) { // Amazon Q Tree setup. learnMoreAmazonQCommand.register() diff --git a/packages/core/src/shared/clients/clientWrapper.ts b/packages/core/src/shared/clients/clientWrapper.ts index 456a5c1e5cd..a90d009eb18 100644 --- a/packages/core/src/shared/clients/clientWrapper.ts +++ b/packages/core/src/shared/clients/clientWrapper.ts @@ -19,11 +19,23 @@ export abstract class ClientWrapper implements vscode.Dispo public constructor( public readonly regionCode: string, - private readonly clientType: AwsClientConstructor + private readonly clientType: AwsClientConstructor, + private readonly isSageMaker: boolean = false ) {} protected getClient(ignoreCache: boolean = false) { - const args = { serviceClient: this.clientType, region: this.regionCode } + const args = { + serviceClient: this.clientType, + region: this.regionCode, + ...(this.isSageMaker + ? { + clientOptions: { + endpoint: `https://sagemaker.${this.regionCode}.amazonaws.com`, + region: this.regionCode, + }, + } + : {}), + } return ignoreCache ? globals.sdkClientBuilderV3.createAwsService(args) : globals.sdkClientBuilderV3.getAwsService(args) diff --git a/packages/core/src/shared/clients/sagemaker.ts b/packages/core/src/shared/clients/sagemaker.ts index b3462311829..a4ad9083e8c 100644 --- a/packages/core/src/shared/clients/sagemaker.ts +++ b/packages/core/src/shared/clients/sagemaker.ts @@ -8,29 +8,39 @@ import { DescribeAppCommand, DescribeAppCommandInput, DescribeAppCommandOutput, + DescribeDomainCommand, + DescribeDomainCommandInput, + DescribeDomainCommandOutput, + DescribeDomainResponse, ListAppsCommandInput, ListSpacesCommandInput, SageMakerClient, SpaceDetails, paginateListApps, paginateListSpaces, -} from '@aws-sdk/client-sagemaker' +} from '@amzn/sagemaker-client' +import { isEmpty } from 'lodash' +import { sleep } from '../utilities/timeoutUtils' import { ClientWrapper } from './clientWrapper' import { AsyncCollection } from '../utilities/asyncCollection' +import { getDomainSpaceKey } from '../../awsService/sagemaker/utils' +import { getLogger } from '../logger/logger' export interface SagemakerSpaceApp extends SpaceDetails { App?: AppDetails } export class SagemakerClient extends ClientWrapper { public constructor(public override readonly regionCode: string) { - super(regionCode, SageMakerClient) + super(regionCode, SageMakerClient, true) } public listSpaces(request: ListSpacesCommandInput = {}): AsyncCollection { + // @ts-ignore: Suppressing type mismatch on paginator return type return this.makePaginatedRequest(paginateListSpaces, request, (page) => page.Spaces) } public listApps(request: ListAppsCommandInput = {}): AsyncCollection { + // @ts-ignore: Suppressing type mismatch on paginator return type return this.makePaginatedRequest(paginateListApps, request, (page) => page.Apps) } @@ -38,18 +48,54 @@ export class SagemakerClient extends ClientWrapper { return this.makeRequest(DescribeAppCommand, request) } - public async fetchSpaceApps(): Promise> { - const appMap = await this.listApps() - .flatten() - .toMap((app) => `${app.DomainId}-${app.SpaceName}` as string) - - const spaceApps = await this.listSpaces() - .flatten() - .map((space) => { - const key = `${space.DomainId}-${space.SpaceName}` as string - return { ...space, App: appMap.get(key) } - }) - .toMap((space) => `${space.DomainId}-${space.SpaceName}` as string) - return spaceApps + public describeDomain(request: DescribeDomainCommandInput): Promise { + return this.makeRequest(DescribeDomainCommand, request) + } + + public async fetchSpaceAppsAndDomains(): Promise< + [Map, Map] + > { + try { + const appMap: Map = await this.listApps() + .flatten() + .filter((app) => !!app.DomainId && !!app.SpaceName) + .toMap((app) => getDomainSpaceKey(app.DomainId || '', app.SpaceName || '')) + + const spaceApps: Map = await this.listSpaces() + .flatten() + .filter((space) => !!space.DomainId && !!space.SpaceName) + .map((space) => { + const key = getDomainSpaceKey(space.DomainId || '', space.SpaceName || '') + return { ...space, App: appMap.get(key) } + }) + .toMap((space) => getDomainSpaceKey(space.DomainId || '', space.SpaceName || '')) + + // Get de-duped list of domain IDs for all of the spaces + const domainIds: string[] = [...new Set([...spaceApps].map(([_, spaceApp]) => spaceApp.DomainId || ''))] + + // Get details for each domain + const domains: [string, DescribeDomainResponse][] = await Promise.all( + domainIds.map(async (domainId, index) => { + await sleep(index * 100) + const response = await this.describeDomain({ DomainId: domainId }) + return [domainId, response] + }) + ) + + const domainsMap = new Map(domains) + + const filteredSpaceApps = new Map( + [...spaceApps] + // Filter out SageMaker Unified Studio domains + .filter(([_, spaceApp]) => + isEmpty(domainsMap.get(spaceApp.DomainId || '')?.DomainSettings?.UnifiedStudioSettings) + ) + ) + + return [filteredSpaceApps, domainsMap] + } catch (err: any) { + getLogger().error('Failed to fetch space apps: %s', err) + throw err + } } } diff --git a/packages/core/src/shared/globalState.ts b/packages/core/src/shared/globalState.ts index 13db46b430a..2ec0a328d24 100644 --- a/packages/core/src/shared/globalState.ts +++ b/packages/core/src/shared/globalState.ts @@ -79,6 +79,8 @@ export type globalKey = | 'aws.toolkit.lambda.walkthroughSelected' | 'aws.toolkit.lambda.walkthroughCompleted' | 'aws.toolkit.appComposer.templateToOpenOnStart' + // List of Domain-Users to show/hide Sagemaker SpaceApps in AWS Explorer. + | 'aws.sagemaker.selectedDomainUsers' /** * Extension-local (not visible to other vscode extensions) shared state which persists after IDE diff --git a/packages/core/src/shared/settings-toolkit.gen.ts b/packages/core/src/shared/settings-toolkit.gen.ts index 10020cf51f9..0ff044edd7e 100644 --- a/packages/core/src/shared/settings-toolkit.gen.ts +++ b/packages/core/src/shared/settings-toolkit.gen.ts @@ -51,7 +51,8 @@ export const toolkitSettings = { "aws.lambda.recentlyUploaded": {}, "aws.accessAnalyzer.policyChecks.checkNoNewAccessFilePath": {}, "aws.accessAnalyzer.policyChecks.checkAccessNotGrantedFilePath": {}, - "aws.accessAnalyzer.policyChecks.cloudFormationParameterFilePath": {} + "aws.accessAnalyzer.policyChecks.cloudFormationParameterFilePath": {}, + "aws.sagemaker.studio.spaces.enableIdentityFiltering": {} } export default toolkitSettings diff --git a/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts b/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts index 20c5bf3a87a..d412b5b9b4c 100644 --- a/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts +++ b/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts @@ -4,28 +4,49 @@ */ import * as sinon from 'sinon' +import * as vscode from 'vscode' +import { DescribeDomainResponse } from '@amzn/sagemaker-client' +import { GetCallerIdentityResponse } from 'aws-sdk/clients/sts' import { SagemakerClient, SagemakerSpaceApp } from '../../../../shared/clients/sagemaker' import { SagemakerParentNode } from '../../../../awsService/sagemaker/explorer/sagemakerParentNode' +import { DefaultStsClient } from '../../../../shared/clients/stsClient' import { assertNodeListOnlyHasPlaceholderNode } from '../../../utilities/explorerNodeAssertions' import assert from 'assert' describe('sagemakerParentNode', function () { let testNode: SagemakerParentNode let client: SagemakerClient - let fetchSpaceAppsStub: sinon.SinonStub<[], Promise>> + let fetchSpaceAppsAndDomainsStub: sinon.SinonStub< + [], + Promise<[Map, Map]> + > + let getCallerIdentityStub: sinon.SinonStub<[], Promise> const testRegion = 'testRegion' + const domainsMap: Map = new Map([ + ['domain1', { DomainId: 'domain1', DomainName: 'domainName1' }], + ['domain2', { DomainId: 'domain2', DomainName: 'domainName2' }], + ]) + const getConfigTrue = { + get: () => true, + } + const getConfigFalse = { + get: () => false, + } before(function () { client = new SagemakerClient(testRegion) }) beforeEach(function () { - fetchSpaceAppsStub = sinon.stub(SagemakerClient.prototype, 'fetchSpaceApps') + fetchSpaceAppsAndDomainsStub = sinon.stub(SagemakerClient.prototype, 'fetchSpaceAppsAndDomains') + getCallerIdentityStub = sinon.stub(DefaultStsClient.prototype, 'getCallerIdentity') testNode = new SagemakerParentNode(testRegion, client) }) afterEach(function () { - fetchSpaceAppsStub.restore() + fetchSpaceAppsAndDomainsStub.restore() + getCallerIdentityStub.restore() + sinon.restore() }) after(function () { @@ -33,26 +54,251 @@ describe('sagemakerParentNode', function () { }) it('returns placeholder node if no children are present', async function () { - fetchSpaceAppsStub.returns(Promise.resolve(new Map())) + fetchSpaceAppsAndDomainsStub.returns( + Promise.resolve([new Map(), new Map()]) + ) + getCallerIdentityStub.returns( + Promise.resolve({ + UserId: 'test-userId', + Account: '123456789012', + Arn: 'arn:aws:iam::123456789012:user/test-user', + }) + ) + const childNodes = await testNode.getChildren() assertNodeListOnlyHasPlaceholderNode(childNodes) - fetchSpaceAppsStub.restore() }) it('has child nodes', async function () { - const spaceApps: SagemakerSpaceApp[] = [ - { SpaceName: 'name1', DomainId: 'domain1' }, - { SpaceName: 'name2', DomainId: 'domain2' }, - ] + const spaceAppsMap: Map = new Map([ + [ + 'domain1__name1', + { + SpaceName: 'name1', + DomainId: 'domain1', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, + Status: 'InService', + }, + ], + [ + 'domain2__name2', + { + SpaceName: 'name2', + DomainId: 'domain2', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, + Status: 'InService', + }, + ], + ]) + + fetchSpaceAppsAndDomainsStub.returns(Promise.resolve([spaceAppsMap, domainsMap])) + getCallerIdentityStub.returns( + Promise.resolve({ + UserId: 'test-userId', + Account: '123456789012', + Arn: 'arn:aws:iam::123456789012:user/test-user', + }) + ) + sinon + .stub(vscode.workspace, 'getConfiguration') + .returns(getConfigFalse as unknown as vscode.WorkspaceConfiguration) + + const childNodes = await testNode.getChildren() + assert.strictEqual(childNodes.length, spaceAppsMap.size, 'Unexpected child count') + assert.strictEqual(childNodes[0].label, 'name1 (Stopped)', 'Unexpected node label') + assert.strictEqual(childNodes[1].label, 'name2 (Stopped)', 'Unexpected node label') + }) + + it('filters spaces owned by user profiles that match the IAM user', async function () { + const spaceAppsMap: Map = new Map([ + [ + 'domain1__name1', + { + SpaceName: 'name1', + DomainId: 'domain1', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, + Status: 'InService', + }, + ], + [ + 'domain2__name2', + { + SpaceName: 'name2', + DomainId: 'domain2', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, + Status: 'InService', + }, + ], + ]) + + fetchSpaceAppsAndDomainsStub.returns(Promise.resolve([spaceAppsMap, domainsMap])) + getCallerIdentityStub.returns( + Promise.resolve({ + UserId: 'test-userId', + Account: '123456789012', + Arn: 'arn:aws:iam::123456789012:user/user2', + }) + ) + sinon + .stub(vscode.workspace, 'getConfiguration') + .returns(getConfigTrue as unknown as vscode.WorkspaceConfiguration) + + const childNodes = await testNode.getChildren() + assert.strictEqual(childNodes.length, 1, 'Unexpected child count') + assert.strictEqual(childNodes[0].label, 'name2 (Stopped)', 'Unexpected node label') + }) + + it('filters spaces owned by user profiles that match the IAM assumed-role session name', async function () { + const spaceAppsMap: Map = new Map([ + [ + 'domain1__name1', + { + SpaceName: 'name1', + DomainId: 'domain1', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, + Status: 'InService', + }, + ], + [ + 'domain2__name2', + { + SpaceName: 'name2', + DomainId: 'domain2', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, + Status: 'InService', + }, + ], + ]) - const spaceAppsMap = new Map() - for (const space of spaceApps) { - spaceAppsMap.set(`${space.DomainId}-${space.SpaceName}` as string, space) - } + fetchSpaceAppsAndDomainsStub.returns(Promise.resolve([spaceAppsMap, domainsMap])) + getCallerIdentityStub.returns( + Promise.resolve({ + UserId: 'test-userId', + Account: '123456789012', + Arn: 'arn:aws:sts::123456789012:assumed-role/UserRole/user2', + }) + ) + sinon + .stub(vscode.workspace, 'getConfiguration') + .returns(getConfigTrue as unknown as vscode.WorkspaceConfiguration) - fetchSpaceAppsStub.returns(Promise.resolve(spaceAppsMap)) const childNodes = await testNode.getChildren() - assert.strictEqual(childNodes.length, spaceApps.length, 'Unexpected child count') - fetchSpaceAppsStub.restore() + assert.strictEqual(childNodes.length, 1, 'Unexpected child count') + assert.strictEqual(childNodes[0].label, 'name2 (Stopped)', 'Unexpected node label') + }) + + it('filters spaces owned by user profiles that match the Identity Center user', async function () { + const spaceAppsMap: Map = new Map([ + [ + 'domain1__name1', + { + SpaceName: 'name1', + DomainId: 'domain1', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, + Status: 'InService', + }, + ], + [ + 'domain2__name2', + { + SpaceName: 'name2', + DomainId: 'domain2', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, + Status: 'InService', + }, + ], + ]) + + fetchSpaceAppsAndDomainsStub.returns(Promise.resolve([spaceAppsMap, domainsMap])) + getCallerIdentityStub.returns( + Promise.resolve({ + UserId: 'test-userId', + Account: '123456789012', + Arn: 'arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_MyPermissionSet_abcd1234/user2', + }) + ) + sinon + .stub(vscode.workspace, 'getConfiguration') + .returns(getConfigFalse as unknown as vscode.WorkspaceConfiguration) + + const childNodes = await testNode.getChildren() + assert.strictEqual(childNodes.length, 1, 'Unexpected child count') + assert.strictEqual(childNodes[0].label, 'name2 (Stopped)', 'Unexpected node label') + }) + + describe('getLocalSelectedDomainUsers', function () { + const createSpaceApp = (ownerName: string): SagemakerSpaceApp => ({ + SpaceName: 'space1', + DomainId: 'domain1', + Status: 'InService', + OwnershipSettingsSummary: { + OwnerUserProfileName: ownerName, + }, + }) + + beforeEach(function () { + testNode = new SagemakerParentNode(testRegion, client) + }) + + it('matches IAM user ARN when filtering is enabled', async function () { + testNode.callerIdentity = { + Arn: 'arn:aws:iam::123456789012:user/user1', + } + + testNode.spaceApps = new Map([ + ['domain1__space1', createSpaceApp('user1-abc')], + ['domain1__space2', createSpaceApp('user2-xyz')], + ]) + + sinon.stub(vscode.workspace, 'getConfiguration').returns(getConfigTrue as any) + + const result = await testNode.getLocalSelectedDomainUsers() + assert.deepStrictEqual(result, ['domain1__user1-abc'], 'Should match only user1-prefixed space') + }) + + it('matches IAM assumed-role ARN when filtering is enabled', async function () { + testNode.callerIdentity = { + Arn: 'arn:aws:sts::123456789012:assumed-role/SomeRole/user2', + } + + testNode.spaceApps = new Map([ + ['domain1__space1', createSpaceApp('user2-xyz')], + ['domain1__space2', createSpaceApp('user3-def')], + ]) + + sinon.stub(vscode.workspace, 'getConfiguration').returns(getConfigTrue as any) + + const result = await testNode.getLocalSelectedDomainUsers() + assert.deepStrictEqual(result, ['domain1__user2-xyz'], 'Should match only user2-prefixed space') + }) + + it('matches Identity Center ARN when IAM filtering is disabled', async function () { + testNode.callerIdentity = { + Arn: 'arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_PermissionSet_abcd/user3', + } + + testNode.spaceApps = new Map([ + ['domain1__space1', createSpaceApp('user3-aaa')], + ['domain1__space2', createSpaceApp('other-user')], + ]) + + sinon.stub(vscode.workspace, 'getConfiguration').returns(getConfigFalse as any) + + const result = await testNode.getLocalSelectedDomainUsers() + assert.deepStrictEqual(result, ['domain1__user3-aaa'], 'Should match only user3-prefixed space') + }) + + it('returns empty array if no match is found', async function () { + testNode.callerIdentity = { + Arn: 'arn:aws:iam::123456789012:user/no-match', + } + + testNode.spaceApps = new Map([['domain1__space1', createSpaceApp('someone-else')]]) + + sinon.stub(vscode.workspace, 'getConfiguration').returns(getConfigTrue as any) + + const result = await testNode.getLocalSelectedDomainUsers() + assert.deepStrictEqual(result, [], 'Should return empty list when no prefix matches') + }) }) }) diff --git a/packages/core/src/test/shared/clients/sagemakerClient.test.ts b/packages/core/src/test/shared/clients/sagemakerClient.test.ts index 612903b3833..678345d006a 100644 --- a/packages/core/src/test/shared/clients/sagemakerClient.test.ts +++ b/packages/core/src/test/shared/clients/sagemakerClient.test.ts @@ -6,10 +6,11 @@ import * as sinon from 'sinon' import * as assert from 'assert' import { SagemakerClient } from '../../../shared/clients/sagemaker' -import { AppDetails, SpaceDetails } from '@aws-sdk/client-sagemaker' +import { AppDetails, SpaceDetails, DescribeDomainCommandOutput } from '@aws-sdk/client-sagemaker' +import { DescribeDomainResponse } from '@amzn/sagemaker-client' import { intoCollection } from '../../../shared/utilities/collectionUtils' -describe('SagemakerClient.fetchSpaceApps', function () { +describe('SagemakerClient.fetchSpaceAppsAndDomains', function () { const region = 'test-region' let client: SagemakerClient let listAppsStub: sinon.SinonStub @@ -17,18 +18,41 @@ describe('SagemakerClient.fetchSpaceApps', function () { const appDetails: AppDetails[] = [ { AppName: 'app1', DomainId: 'domain1', SpaceName: 'space1' }, { AppName: 'app2', DomainId: 'domain2', SpaceName: 'space2' }, + { AppName: 'app3', DomainId: 'domain2', SpaceName: 'space3' }, ] const spaceDetails: SpaceDetails[] = [ { SpaceName: 'space1', DomainId: 'domain1' }, { SpaceName: 'space2', DomainId: 'domain2' }, + { SpaceName: 'space3', DomainId: 'domain2' }, + { SpaceName: 'space4', DomainId: 'domain3' }, ] + const domain1: DescribeDomainResponse = { DomainId: 'domain1', DomainName: 'domainName1' } + const domain2: DescribeDomainResponse = { DomainId: 'domain2', DomainName: 'domainName2' } + const domain3: DescribeDomainResponse = { + DomainId: 'domain3', + DomainName: 'domainName3', + DomainSettings: { UnifiedStudioSettings: { DomainId: 'unifiedStudioDomain1' } }, + } + beforeEach(function () { client = new SagemakerClient(region) listAppsStub = sinon.stub(client, 'listApps').returns(intoCollection([appDetails])) sinon.stub(client, 'listSpaces').returns(intoCollection([spaceDetails])) + sinon.stub(client, 'describeDomain').callsFake(async ({ DomainId }) => { + switch (DomainId) { + case 'domain1': + return domain1 as DescribeDomainCommandOutput + case 'domain2': + return domain2 as DescribeDomainCommandOutput + case 'domain3': + return domain3 as DescribeDomainCommandOutput + default: + return {} as DescribeDomainCommandOutput + } + }) }) afterEach(function () { @@ -36,27 +60,47 @@ describe('SagemakerClient.fetchSpaceApps', function () { }) it('returns a map of space details with corresponding app details', async function () { - const result = await client.fetchSpaceApps() + const [spaceApps, domains] = await client.fetchSpaceAppsAndDomains() + + assert.strictEqual(spaceApps.size, 3) + assert.strictEqual(domains.size, 3) + + const spaceAppKey1 = 'domain1__space1' + const spaceAppKey2 = 'domain2__space2' + const spaceAppKey3 = 'domain2__space3' - assert.strictEqual(result.size, 2) + assert.ok(spaceApps.has(spaceAppKey1), 'Expected spaceApps to have key for domain1__space1') + assert.ok(spaceApps.has(spaceAppKey2), 'Expected spaceApps to have key for domain2__space2') + assert.ok(spaceApps.has(spaceAppKey3), 'Expected spaceApps to have key for domain2__space3') - const key1 = 'domain1-space1' - const key2 = 'domain2-space2' + assert.deepStrictEqual(spaceApps.get(spaceAppKey1)?.App?.AppName, 'app1') + assert.deepStrictEqual(spaceApps.get(spaceAppKey2)?.App?.AppName, 'app2') + assert.deepStrictEqual(spaceApps.get(spaceAppKey3)?.App?.AppName, 'app3') - assert.ok(result.has(key1), 'Expected result to have key for domain1-space1') - assert.ok(result.has(key2), 'Expected result to have key for domain2-space2') + const domainKey1 = 'domain1' + const domainKey2 = 'domain2' - assert.deepStrictEqual(result.get(key1)?.App?.AppName, 'app1') - assert.deepStrictEqual(result.get(key2)?.App?.AppName, 'app2') + assert.ok(domains.has(domainKey1), 'Expected domains to have key for domain1') + assert.ok(domains.has(domainKey2), 'Expected domains to have key for domain2') + + assert.deepStrictEqual(domains.get(domainKey1)?.DomainName, 'domainName1') + assert.deepStrictEqual(domains.get(domainKey2)?.DomainName, 'domainName2') }) it('returns map even if some spaces have no matching apps', async function () { listAppsStub.returns(intoCollection([{ AppName: 'app1', DomainId: 'domain1', SpaceName: 'space1' }])) - const result = await client.fetchSpaceApps() - const key2 = 'domain2-space2' + const [spaceApps] = await client.fetchSpaceAppsAndDomains() + for (const space of spaceApps) { + console.log(space[0]) + console.log(space[1]) + } + + const spaceAppKey2 = 'domain2__space2' + const spaceAppKey3 = 'domain2__space3' - assert.strictEqual(result.size, 2) - assert.strictEqual(result.get(key2)?.App, undefined) + assert.strictEqual(spaceApps.size, 3) + assert.strictEqual(spaceApps.get(spaceAppKey2)?.App, undefined) + assert.strictEqual(spaceApps.get(spaceAppKey3)?.App, undefined) }) }) diff --git a/packages/core/src/testLint/gitSecrets.test.ts b/packages/core/src/testLint/gitSecrets.test.ts index fce29585d1c..d57d0763e2d 100644 --- a/packages/core/src/testLint/gitSecrets.test.ts +++ b/packages/core/src/testLint/gitSecrets.test.ts @@ -23,7 +23,7 @@ describe('git-secrets', function () { /** git-secrets patterns that will not cause a failure during the scan */ function setAllowListPatterns(gitSecrets: string) { - const allowListPatterns: string[] = ['"accountId": "123456789012"'] + const allowListPatterns: string[] = ['"accountId": "123456789012"', "Account: '123456789012'"] for (const pattern of allowListPatterns) { // Returns non-zero exit code if pattern already exists diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index e4a6639c3a1..e03c8c9c688 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -295,6 +295,11 @@ "default": "", "description": "A JSON formatted file that specifies template parameter values, a stack policy, and tags. Only parameters are used from this file.", "scope": "machine-overridable" + }, + "aws.sagemaker.studio.spaces.enableIdentityFiltering": { + "type": "boolean", + "default": false, + "description": "Enable automatic filtration of spaces based on your AWS identity." } } }, @@ -1240,6 +1245,10 @@ { "command": "aws.newThreatComposerFile", "when": "false" + }, + { + "command": "aws.sagemaker.filterSpaceApps", + "when": "false" } ], "editor/title": [ @@ -1601,6 +1610,11 @@ "when": "view == aws.explorer && viewItem == awsRegionNode", "group": "0@1" }, + { + "command": "aws.sagemaker.filterSpaceApps", + "when": "view == aws.explorer && viewItem == awsSagemakerParentNode", + "group": "inline@1" + }, { "command": "aws.toolkit.lambda.createServerlessLandProject", "when": "view == aws.explorer && viewItem == awsLambdaNode || viewItem == awsRegionNode", @@ -1771,6 +1785,11 @@ "when": "view == aws.explorer && viewItem =~ /^awsIotCertificateNode.(Things|Policies)/", "group": "0@1" }, + { + "command": "aws.sagemaker.filterSpaceApps", + "when": "view == aws.explorer && viewItem == awsSagemakerParentNode", + "group": "0@1" + }, { "command": "aws.s3.createBucket", "when": "view == aws.explorer && viewItem == awsS3Node", @@ -2561,6 +2580,18 @@ } } }, + { + "command": "aws.sagemaker.filterSpaceApps", + "title": "%AWS.command.sagemaker.filterSpaces%", + "category": "%AWS.title%", + "enablement": "isCloud9 || !aws.isWebExtHost", + "icon": "$(extensions-filter)", + "cloud9": { + "cn": { + "category": "%AWS.title.cn%" + } + } + }, { "command": "aws.ec2.startInstance", "title": "%AWS.command.ec2.startInstance%", @@ -4645,26 +4676,40 @@ "fontCharacter": "\\f1dc" } }, - "aws-schemas-registry": { + "aws-sagemaker-code-editor": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1dd" } }, - "aws-schemas-schema": { + "aws-sagemaker-jupyter-lab": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1de" } }, - "aws-stepfunctions-preview": { + "aws-schemas-registry": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1df" } + }, + "aws-schemas-schema": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e0" + } + }, + "aws-stepfunctions-preview": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e1" + } } }, "notebooks": [ diff --git a/src.gen/@amzn/sagemaker-client/1.0.0.tgz b/src.gen/@amzn/sagemaker-client/1.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..4821da0e727f9e46715a8c7a6c5d0d9969459cdb GIT binary patch literal 434877 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

2R}Cl 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�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 W~obo5vkI>nlkB4=D7cTgn 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 c=-WGr@@5Bh?;OgW~uW$yRGq(fo<2ZPYxLI3~& literal 0 HcmV?d00001 From 8f055d530e3f06336f76d56e00eef19d3b31d25f Mon Sep 17 00:00:00 2001 From: aws-asolidu Date: Thu, 26 Jun 2025 15:02:47 -0700 Subject: [PATCH 03/12] feat(sagemaker-connect): remote connect to sagemaker spaces (#2132) ## Problem As part of sagemaker-connect project: - User able to connect to sagemaker space from toolkit extension - User able to connect to sagemaker space using deeplink uri ## Solution - Connect using sagemaker startSession API which starts an ssm session - Use remote-ssh extension to handle the ssh connection - Use custom proxy command to handle initial connection and reconnection - Use a detached local endpoint that is able to call startSession to handle connection and reconnection --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Co-authored-by: Newton Der Co-authored-by: Edward Sun --- package-lock.json | 5842 +++++++++++------ packages/core/package.json | 1 + .../icons/fonts/aws-toolkit-icons.woff | Bin 0 -> 59412 bytes packages/core/resources/sagemaker_connect | 130 + packages/core/resources/sagemaker_connect.ps1 | 128 + packages/core/src/auth/auth.ts | 4 + packages/core/src/auth/credentials/store.ts | 2 +- .../src/awsService/sagemaker/activation.ts | 15 +- .../core/src/awsService/sagemaker/commands.ts | 75 +- .../awsService/sagemaker/credentialMapping.ts | 170 + .../sagemaker/detached-server/credentials.ts | 52 + .../detached-server/routes/getSession.ts | 50 + .../detached-server/routes/getSessionAsync.ts | 69 + .../detached-server/routes/refreshToken.ts | 46 + .../sagemaker/detached-server/server.ts | 107 + .../sagemaker/detached-server/sessionStore.ts | 135 + .../sagemaker/detached-server/utils.ts | 106 + .../sagemaker/explorer/sagemakerParentNode.ts | 2 +- .../sagemaker/explorer/sagemakerSpaceNode.ts | 19 +- .../core/src/awsService/sagemaker/model.ts | 194 + .../core/src/awsService/sagemaker/types.ts | 30 + .../src/awsService/sagemaker/uriHandlers.ts | 37 + .../core/src/awsService/sagemaker/utils.ts | 26 + packages/core/src/shared/clients/sagemaker.ts | 7 + packages/core/src/shared/logger/logger.ts | 1 + packages/core/src/shared/sshConfig.ts | 15 +- packages/core/src/shared/vscode/uriHandler.ts | 3 +- .../sagemaker/credentialMapping.test.ts | 154 + .../detached-server/credentials.test.ts | 89 + .../detached-server/routes/getSession.test.ts | 97 + .../routes/getSessionAsync.test.ts | 96 + .../routes/refreshToken.test.ts | 74 + .../detached-server/sessionStore.test.ts | 141 + .../sagemaker/detached-server/utils.test.ts | 46 + .../test/awsService/sagemaker/model.test.ts | 205 + .../awsService/sagemaker/uriHandlers.test.ts | 59 + .../shared/clients/sagemakerClient.test.ts | 6 +- packages/core/src/testLint/gitSecrets.test.ts | 7 +- packages/core/tsconfig.json | 3 +- packages/core/webpack.config.js | 1 + packages/toolkit/package.json | 25 +- packages/toolkit/scripts/build/copyFiles.ts | 16 +- packages/toolkit/tsconfig.json | 3 +- tsconfig.json | 2 +- 44 files changed, 6285 insertions(+), 2005 deletions(-) create mode 100644 packages/core/resources/icons/fonts/aws-toolkit-icons.woff create mode 100755 packages/core/resources/sagemaker_connect create mode 100644 packages/core/resources/sagemaker_connect.ps1 create mode 100644 packages/core/src/awsService/sagemaker/credentialMapping.ts create mode 100644 packages/core/src/awsService/sagemaker/detached-server/credentials.ts create mode 100644 packages/core/src/awsService/sagemaker/detached-server/routes/getSession.ts create mode 100644 packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts create mode 100644 packages/core/src/awsService/sagemaker/detached-server/routes/refreshToken.ts create mode 100644 packages/core/src/awsService/sagemaker/detached-server/server.ts create mode 100644 packages/core/src/awsService/sagemaker/detached-server/sessionStore.ts create mode 100644 packages/core/src/awsService/sagemaker/detached-server/utils.ts create mode 100644 packages/core/src/awsService/sagemaker/model.ts create mode 100644 packages/core/src/awsService/sagemaker/types.ts create mode 100644 packages/core/src/awsService/sagemaker/uriHandlers.ts create mode 100644 packages/core/src/test/awsService/sagemaker/credentialMapping.test.ts create mode 100644 packages/core/src/test/awsService/sagemaker/detached-server/credentials.test.ts create mode 100644 packages/core/src/test/awsService/sagemaker/detached-server/routes/getSession.test.ts create mode 100644 packages/core/src/test/awsService/sagemaker/detached-server/routes/getSessionAsync.test.ts create mode 100644 packages/core/src/test/awsService/sagemaker/detached-server/routes/refreshToken.test.ts create mode 100644 packages/core/src/test/awsService/sagemaker/detached-server/sessionStore.test.ts create mode 100644 packages/core/src/test/awsService/sagemaker/detached-server/utils.test.ts create mode 100644 packages/core/src/test/awsService/sagemaker/model.test.ts create mode 100644 packages/core/src/test/awsService/sagemaker/uriHandlers.test.ts diff --git a/package-lock.json b/package-lock.json index c82af9f9e8d..ea7c0842597 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6516,1218 +6516,1004 @@ } }, "node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.637.0", - "license": "Apache-2.0", + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.730.0.tgz", + "integrity": "sha512-iJt2pL6RqWg7R3pja1WfcC2+oTjwaKFYndNE9oUQqyc6RN24XWUtGy9JnWqTUOy8jYzaP2eoF00fGeasSBX+Dw==", "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/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/core": "3.730.0", + "@aws-sdk/credential-provider-node": "3.730.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.730.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/types": { - "version": "3.609.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/client-sso": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.730.0.tgz", + "integrity": "sha512-mI8kqkSuVlZklewEmN7jcbBMyVODBld3MsTjCKSl5ztduuPX69JD7nXLnWWPkw1PX4aGTO24AEoRMGNxntoXUg==", "dependencies": { - "@smithy/types": "^3.3.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.730.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.4", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/core": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.730.0.tgz", + "integrity": "sha512-jonKyR+2GcqbZj2WDICZS0c633keLc9qwXnePu83DfAoFXMMIMyoR/7FOGf8F3OrIdGh8KzE9VvST+nZCK9EJA==", "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/types": "3.723.0", + "@smithy/core": "^3.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/signature-v4": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.730.0.tgz", + "integrity": "sha512-fFXgo3jBXLWqu8I07Hd96mS7RjrtpDgm3bZShm0F3lKtqDQF+hObFWq9A013SOE+RjMLVfbABhToXAYct3FcBw==", "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.730.0.tgz", + "integrity": "sha512-1aF3elbCzpVhWLAuV63iFElfLOqLGGTp4fkf2VAFIDO3hjshpXUQssTgIWiBwwtJYJdOSxaFrCU7u8frjr/5aQ==", "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-stream": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.730.0.tgz", + "integrity": "sha512-zwsxkBuQuPp06o45ATAnznHzj3+ibop/EaTytNzSv0O87Q59K/jnS/bdtv1n6bhe99XCieRNTihvtS7YklzK7A==", "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2": { - "version": "3.695.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.730.0.tgz", + "integrity": "sha512-ztRjh1edY7ut2wwrj1XqHtqPY/NXEYIk5fYf04KKsp8zBi81ScVqP7C+Cst6PFKixjgLSG6RsqMx9GSAalVv0Q==", "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/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-sdk-ec2": "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", - "@smithy/util-waiter": "^3.1.8", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-ini": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.730.0.tgz", + "integrity": "sha512-cNKUQ81eptfZN8MlSqwUq3+5ln8u/PcY57UmLZ+npxUHanqO1akpgcpNsLpmsIkoXGbtSQrLuDUgH86lS/SWOw==", "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", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.730.0.tgz", + "integrity": "sha512-SdI2xrTbquJLMxUh5LpSwB8zfiKq3/jso53xWRgrVfeDlrSzZuyV6QghaMs3KEEjcNzwEnTfSIjGQyRXG9VrEw==", "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", + "@aws-sdk/client-sso": "3.730.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/token-providers": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.693.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sts": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.730.0.tgz", + "integrity": "sha512-l5vdPmvF/d890pbvv5g1GZrdjaSQkyPH/Bc8dO/ZqkWxkIP8JNgl48S2zgf4DkP3ik9K2axWO828L5RsMDQzdA==", "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", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/core": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.723.0.tgz", + "integrity": "sha512-LLVzLvk299pd7v4jN9yOSaWDZDfH0SnBPb6q+FDPaOCMGBY8kuwQso7e/ozIKSmZHRMGO3IZrflasHM+rI+2YQ==", "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", + "@aws-sdk/types": "3.723.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-logger": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.723.0.tgz", + "integrity": "sha512-chASQfDG5NJ8s5smydOEnNK7N0gDMyuPbx7dYYcm1t/PKtnVfvWF+DHCTrRC2Ej76gLJVCVizlAJKM8v8Kg3cg==", "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", + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.723.0.tgz", + "integrity": "sha512-7usZMtoynT9/jxL/rkuDOFQ0C2mhXl4yCm67Rg7GNTstl67u7w5WN1aIRImMeztaKlw8ExjoTyo6WTs1Kceh7A==", "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", + "@aws-sdk/types": "3.723.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.693.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.730.0.tgz", + "integrity": "sha512-aPMZvNmf2a42B41au3bA3ODU4HfHka2nYT/SAIhhVXH1ENYfAmZo7FraFPxetKepFMCtL7j4QE6/LDucK6liIw==", "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", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@smithy/core": "^3.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/nested-clients": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.730.0.tgz", + "integrity": "sha512-vilIgf1/7kre8DdE5zAQkDOwHFb/TahMn/6j2RZwFLlK7cDk91r19deSiVYnKQkupDMtOfNceNqnorM4I3PDzw==", "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", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.730.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.723.0.tgz", + "integrity": "sha512-tGF/Cvch3uQjZIj34LY2mg8M2Dr4kYG8VU8Yd0dFnB1ybOEOveIK/9ypUo9ycZpB9oO6q01KRe5ijBaxNueUQg==", "dependencies": { - "@aws-sdk/core": "3.693.0", - "@aws-sdk/types": "3.692.0", - "@smithy/property-provider": "^3.1.9", - "@smithy/types": "^3.7.0", + "@aws-sdk/types": "3.723.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.693.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/token-providers": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.730.0.tgz", + "integrity": "sha512-BSPssGj54B/AABWXARIPOT/1ybFahM1ldlfmXy9gRmZi/afe9geWJGlFYCCt3PmqR+1Ny5XIjSfue+kMd//drQ==", "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/protocol-http": "^4.1.6", - "@smithy/types": "^3.7.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-logger": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/types": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.723.0.tgz", + "integrity": "sha512-LmK3kwiMZG1y5g3LGihT9mNkeNOmwEyPk6HGcJqh0wOSV4QpWoKu2epyKE4MLQNUUlz2kOVbVbOrwmI6ZcteuA==", "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/types": "^3.7.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-endpoints": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.730.0.tgz", + "integrity": "sha512-1KTFuVnk+YtLgWr6TwDiggcDqtPpOY2Cszt3r2lkXfaEAX6kHyOZi1vdvxXjPU5LsOBJem8HZ7KlkmrEi+xowg==", "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/protocol-http": "^4.1.6", - "@smithy/types": "^3.7.0", + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.723.0.tgz", + "integrity": "sha512-Wh9I6j2jLhNFq6fmXydIpqD1WyQLyTfSxjW9B+PXSnPyk3jtQW8AKQur7p97rO8LAUzVI0bv8kb3ZzDEVbquIg==", "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", + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "bowser": "^2.11.0", "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.730.0.tgz", + "integrity": "sha512-yBvkOAjqsDEl1va4eHNOhnFBk0iCY/DBFNyhvtTMqPF4NO+MITWpFs3J9JtZKzJlQ6x0Yb9TLQ8NhDjEISz5Ug==", "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", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/token-providers": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/abort-controller": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", + "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", "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", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.693.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-endpoints": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/config-resolver": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", + "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/types": "^3.7.0", - "@smithy/util-endpoints": "^2.1.5", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/core": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.5.3.tgz", + "integrity": "sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA==", "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/types": "^3.7.0", - "bowser": "^2.11.0", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", + "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", "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", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.4.tgz", + "integrity": "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==", "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/hash-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", + "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", + "@smithy/types": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/invalid-dependency": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", + "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", "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/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/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", - "@smithy/util-waiter": "^3.1.8", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sso": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-content-length": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", + "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", "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", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-endpoint": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.11.tgz", + "integrity": "sha512-zDogwtRLzKl58lVS8wPcARevFZNBOOqnmzWWxVe9XiaXU2CADFjvJ9XfNibgkOWs08sxLuSr81NrpY4mgp9OwQ==", "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", + "@smithy/core": "^3.5.3", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-middleware": "^4.0.4", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-retry": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.12.tgz", + "integrity": "sha512-wvIH70c4e91NtRxdaLZF+mbLZ/HcC6yg7ySKUiufL6ESp6zJUSnJucZ309AvG9nqCFHSRB5I6T3Ez1Q9wCh0Ww==", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.5", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "tslib": "^2.6.2", + "uuid": "^9.0.1" }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.693.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sts": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-serde": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", + "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", "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", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/core": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-stack": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", + "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", "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", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-config-provider": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", + "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", "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", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-http-handler": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", + "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", "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", + "@smithy/abort-controller": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.693.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/property-provider": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", + "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", "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", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/protocol-http": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", + "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", "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", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-builder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", + "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", "dependencies": { - "@aws-sdk/core": "3.693.0", - "@aws-sdk/types": "3.692.0", - "@smithy/property-provider": "^3.1.9", - "@smithy/types": "^3.7.0", + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.693.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", + "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/protocol-http": "^4.1.6", - "@smithy/types": "^3.7.0", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-logger": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/service-error-classification": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.5.tgz", + "integrity": "sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA==", "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/types": "^3.7.0", - "tslib": "^2.6.2" + "@smithy/types": "^4.3.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", + "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/protocol-http": "^4.1.6", - "@smithy/types": "^3.7.0", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/signature-v4": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", + "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", "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", + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/smithy-client": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.3.tgz", + "integrity": "sha512-xxzNYgA0HD6ETCe5QJubsxP0hQH3QK3kbpJz3QrosBCuIWyEXLR/CO5hFb2OeawEKUxMNhz3a1nuJNN2np2RMA==", "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", + "@smithy/core": "^3.5.3", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/token-providers": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/types": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", + "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", "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": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-endpoints": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/url-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", + "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/types": "^3.7.0", - "@smithy/util-endpoints": "^2.1.5", + "@smithy/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/types": "^3.7.0", - "bowser": "^2.11.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", "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": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", + "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-lambda": { - "version": "3.637.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.19.tgz", + "integrity": "sha512-mvLMh87xSmQrV5XqnUYEPoiFFeEGYeAKIDDKdhE2ahqitm8OHM3aSvhqL6rrK6wm1brIk90JhxDf5lf2hbrLbQ==", "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", - "@smithy/util-waiter": "^3.1.2", + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/types": { - "version": "3.609.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.19.tgz", + "integrity": "sha512-8tYnx+LUfj6m+zkUUIrIQJxPM1xVxfRBvoGHua7R/i6qAxOMjqR6CpEpDwKoIs1o0+hOjGvkKE23CafKL0vJ9w==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.4", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-endpoints": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", + "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", - "@smithy/util-base64": "^3.0.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-middleware": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", + "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-retry": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.5.tgz", + "integrity": "sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg==", "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", + "@smithy/service-error-classification": "^4.0.5", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3": { - "version": "3.693.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-stream": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.2.tgz", + "integrity": "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2": { + "version": "3.695.0", "license": "Apache-2.0", "dependencies": { - "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/client-sso-oidc": "3.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-bucket-endpoint": "3.693.0", - "@aws-sdk/middleware-expect-continue": "3.693.0", - "@aws-sdk/middleware-flexible-checksums": "3.693.0", "@aws-sdk/middleware-host-header": "3.693.0", - "@aws-sdk/middleware-location-constraint": "3.693.0", "@aws-sdk/middleware-logger": "3.693.0", "@aws-sdk/middleware-recursion-detection": "3.693.0", - "@aws-sdk/middleware-sdk-s3": "3.693.0", - "@aws-sdk/middleware-ssec": "3.693.0", + "@aws-sdk/middleware-sdk-ec2": "3.693.0", "@aws-sdk/middleware-user-agent": "3.693.0", "@aws-sdk/region-config-resolver": "3.693.0", - "@aws-sdk/signature-v4-multi-region": "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", - "@aws-sdk/xml-builder": "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-blob-browser": "^3.1.8", "@smithy/hash-node": "^3.0.9", - "@smithy/hash-stream-node": "^3.1.8", "@smithy/invalid-dependency": "^3.0.9", - "@smithy/md5-js": "^3.0.9", "@smithy/middleware-content-length": "^3.0.11", "@smithy/middleware-endpoint": "^3.2.2", "@smithy/middleware-retry": "^3.0.26", @@ -7747,16 +7533,17 @@ "@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", "@smithy/util-waiter": "^3.1.8", - "tslib": "^2.6.2" + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -7803,7 +7590,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso-oidc": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso-oidc": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -7854,7 +7641,7 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sts": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sts": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -7903,7 +7690,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/core": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/core": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -7923,7 +7710,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-http": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -7942,7 +7729,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-ini": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -7966,7 +7753,7 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-node": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -7987,7 +7774,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-sso": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8004,7 +7791,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8021,7 +7808,7 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-host-header": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8034,7 +7821,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-logger": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8046,7 +7833,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8059,7 +7846,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-user-agent": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8075,7 +7862,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/region-config-resolver": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/region-config-resolver": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8090,7 +7877,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/token-providers": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8107,7 +7894,7 @@ "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-endpoints": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8120,7 +7907,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8130,7 +7917,7 @@ "tslib": "^2.6.2" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-user-agent-node": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8152,7 +7939,7 @@ } } }, - "node_modules/@aws-sdk/client-s3/node_modules/@smithy/is-array-buffer": { + "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/is-array-buffer": { "version": "3.0.0", "license": "Apache-2.0", "dependencies": { @@ -8162,7 +7949,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-buffer-from": { + "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/util-buffer-from": { "version": "3.0.0", "license": "Apache-2.0", "dependencies": { @@ -8173,7 +7960,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-utf8": { + "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/util-utf8": { "version": "3.0.0", "license": "Apache-2.0", "dependencies": { @@ -8184,10 +7971,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker": { + "node_modules/@aws-sdk/client-iam": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sagemaker/-/client-sagemaker-3.693.0.tgz", - "integrity": "sha512-iInrrb7V9f0CRBiVCaaxCbpoBRQ5BqxX4elRYI6gE/pSDD2tPqmRfm4reahMtTUcKg1jaSGuvqJLfOpp0HTozQ==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -8230,18 +8016,15 @@ "@smithy/util-retry": "^3.0.9", "@smithy/util-utf8": "^3.0.0", "@smithy/util-waiter": "^3.1.8", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" + "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/client-sso": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sso": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.693.0.tgz", - "integrity": "sha512-QEynrBC26x6TG9ZMzApR/kZ3lmt4lEIs2D+cHuDxt6fDGzahBUsQFBwJqhizzsM97JJI5YvmJhmihoYjdSSaXA==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -8286,10 +8069,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/client-sso-oidc": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sso-oidc": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.693.0.tgz", - "integrity": "sha512-UEDbYlYtK/e86OOMyFR4zEPyenIxDzO2DRdz3fwVW7RzZ94wfmSwBh/8skzPTuY1G7sI064cjHW0b0QG01Sdtg==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -8338,10 +8120,9 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/client-sts": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sts": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.693.0.tgz", - "integrity": "sha512-4S2y7VEtvdnjJX4JPl4kDQlslxXEZFnC50/UXVUYSt/AMc5A/GgspFNA5FVz4E3Gwpfobbf23hR2NBF8AGvYoQ==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -8388,10 +8169,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/core": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/core": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.693.0.tgz", - "integrity": "sha512-v6Z/kWmLFqRLDPEwl9hJGhtTgIFHjZugSfF1Yqffdxf4n1AWgtHS7qSegakuMyN5pP4K2tvUD8qHJ+gGe2Bw2A==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/core": "^2.5.2", @@ -8409,10 +8189,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-http": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-http": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.693.0.tgz", - "integrity": "sha512-sL8MvwNJU7ZpD7/d2VVb3by1GknIJUxzTIgYtVkDVA/ojo+KRQSSHxcj0EWWXF5DTSh2Tm+LrEug3y1ZyKHsDA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "3.693.0", "@aws-sdk/types": "3.692.0", @@ -8429,10 +8208,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-ini": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-ini": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.693.0.tgz", - "integrity": "sha512-kvaa4mXhCCOuW7UQnBhYqYfgWmwy7WSBSDClutwSLPZvgrhYj2l16SD2lN4IfYdxARYMJJ1lFYp3/jJG/9Yk4Q==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "3.693.0", "@aws-sdk/credential-provider-env": "3.693.0", @@ -8454,10 +8232,9 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-node": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-node": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.693.0.tgz", - "integrity": "sha512-42WMsBjTNnjYxYuM3qD/Nq+8b7UdMopUq5OduMDxoM3mFTV6PXMMnfI4Z1TNnR4tYRvPXAnuNltF6xmjKbSJRA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/credential-provider-env": "3.693.0", "@aws-sdk/credential-provider-http": "3.693.0", @@ -8476,10 +8253,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-sso": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-sso": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.693.0.tgz", - "integrity": "sha512-479UlJxY+BFjj3pJFYUNC0DCMrykuG7wBAXfsvZqQxKUa83DnH5Q1ID/N2hZLkxjGd4ZW0AC3lTOMxFelGzzpQ==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-sso": "3.693.0", "@aws-sdk/core": "3.693.0", @@ -8494,10 +8270,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-web-identity": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.693.0.tgz", - "integrity": "sha512-8LB210Pr6VeCiSb2hIra+sAH4KUBLyGaN50axHtIgufVK8jbKIctTZcVY5TO9Se+1107TsruzeXS7VeqVdJfFA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "3.693.0", "@aws-sdk/types": "3.692.0", @@ -8512,10 +8287,9 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-host-header": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-host-header": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.693.0.tgz", - "integrity": "sha512-BCki6sAZ5jYwIN/t3ElCiwerHad69ipHwPsDCxJQyeiOnJ8HG+lEpnVIfrnI8A0fLQNSF3Gtx6ahfBpKiv1Oug==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/protocol-http": "^4.1.6", @@ -8526,10 +8300,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-logger": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-logger": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.693.0.tgz", - "integrity": "sha512-dXnXDPr+wIiJ1TLADACI1g9pkSB21KkMIko2u4CJ2JCBoxi5IqeTnVoa6YcC8GdFNVRl+PorZ3Zqfmf1EOTC6w==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/types": "^3.7.0", @@ -8539,10 +8312,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-recursion-detection": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.693.0.tgz", - "integrity": "sha512-0LDmM+VxXp0u3rG0xQRWD/q6Ubi7G8I44tBPahevD5CaiDZTkmNTrVUf0VEJgVe0iCKBppACMBDkLB0/ETqkFw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/protocol-http": "^4.1.6", @@ -8553,10 +8325,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-user-agent": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-user-agent": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.693.0.tgz", - "integrity": "sha512-/KUq/KEpFFbQmNmpp7SpAtFAdViquDfD2W0QcG07zYBfz9MwE2ig48ALynXm5sMpRmnG7sJXjdvPtTsSVPfkiw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "3.693.0", "@aws-sdk/types": "3.692.0", @@ -8570,10 +8341,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/region-config-resolver": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/region-config-resolver": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.693.0.tgz", - "integrity": "sha512-YLUkMsUY0GLW/nfwlZ69cy1u07EZRmsv8Z9m0qW317/EZaVx59hcvmcvb+W4bFqj5E8YImTjoGfE4cZ0F9mkyw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/node-config-provider": "^3.1.10", @@ -8586,10 +8356,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/token-providers": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/token-providers": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.693.0.tgz", - "integrity": "sha512-nDBTJMk1l/YmFULGfRbToOA2wjf+FkQT4dMgYCv+V9uSYsMzQj8A7Tha2dz9yv4vnQgYaEiErQ8d7HVyXcVEoA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/property-provider": "^3.1.9", @@ -8604,10 +8373,9 @@ "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/util-endpoints": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-endpoints": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.693.0.tgz", - "integrity": "sha512-eo4F6DRQ/kxS3gxJpLRv+aDNy76DxQJL5B3DPzpr9Vkq0ygVoi4GT5oIZLVaAVIJmi6k5qq9dLsYZfWLUxJJSg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/types": "^3.7.0", @@ -8618,10 +8386,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/util-user-agent-browser": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.693.0.tgz", - "integrity": "sha512-6EUfuKOujtddy18OLJUaXfKBgs+UcbZ6N/3QV4iOkubCUdeM1maIqs++B9bhCbWeaeF5ORizJw5FTwnyNjE/mw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/types": "^3.7.0", @@ -8629,10 +8396,9 @@ "tslib": "^2.6.2" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/util-user-agent-node": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-user-agent-node": { "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.693.0.tgz", - "integrity": "sha512-td0OVX8m5ZKiXtecIDuzY3Y3UZIzvxEr57Hp21NOwieqKCG2UeyQWWeGPv0FQaU7dpTkvFmVNI+tx9iB8V/Nhg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/middleware-user-agent": "3.693.0", "@aws-sdk/types": "3.692.0", @@ -8652,10 +8418,9 @@ } } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/is-array-buffer": { + "node_modules/@aws-sdk/client-iam/node_modules/@smithy/is-array-buffer": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", - "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -8663,10 +8428,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/util-buffer-from": { + "node_modules/@aws-sdk/client-iam/node_modules/@smithy/util-buffer-from": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", - "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^3.0.0", "tslib": "^2.6.2" @@ -8675,10 +8439,9 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/util-utf8": { + "node_modules/@aws-sdk/client-iam/node_modules/@smithy/util-utf8": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", - "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" @@ -8687,30 +8450,154 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ssm": { + "node_modules/@aws-sdk/client-lambda": { + "version": "3.637.0", + "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", + "@smithy/util-waiter": "^3.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "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-sdk/client-lambda/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/client-sso-oidc": "3.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-bucket-endpoint": "3.693.0", + "@aws-sdk/middleware-expect-continue": "3.693.0", + "@aws-sdk/middleware-flexible-checksums": "3.693.0", "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-location-constraint": "3.693.0", "@aws-sdk/middleware-logger": "3.693.0", "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-sdk-s3": "3.693.0", + "@aws-sdk/middleware-ssec": "3.693.0", "@aws-sdk/middleware-user-agent": "3.693.0", "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/signature-v4-multi-region": "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", + "@aws-sdk/xml-builder": "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-blob-browser": "^3.1.8", "@smithy/hash-node": "^3.0.9", + "@smithy/hash-stream-node": "^3.1.8", "@smithy/invalid-dependency": "^3.0.9", + "@smithy/md5-js": "^3.0.9", "@smithy/middleware-content-length": "^3.0.11", "@smithy/middleware-endpoint": "^3.2.2", "@smithy/middleware-retry": "^3.0.26", @@ -8730,17 +8617,16 @@ "@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", "@smithy/util-waiter": "^3.1.8", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" + "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/client-sso": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8787,7 +8673,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/client-sso-oidc": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso-oidc": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8838,7 +8724,7 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/client-sts": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sts": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8887,7 +8773,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/core": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/core": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8907,7 +8793,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-http": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8926,7 +8812,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-ini": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8946,487 +8832,2528 @@ "engines": { "node": ">=16.0.0" }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.693.0" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "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-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "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-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "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-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "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-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "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-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "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-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "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-sdk/client-s3/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "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-sdk/client-s3/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "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-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@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-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "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-sdk/client-s3/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sagemaker/-/client-sagemaker-3.693.0.tgz", + "integrity": "sha512-iInrrb7V9f0CRBiVCaaxCbpoBRQ5BqxX4elRYI6gE/pSDD2tPqmRfm4reahMtTUcKg1jaSGuvqJLfOpp0HTozQ==", + "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/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/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", + "@smithy/util-waiter": "^3.1.8", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.693.0.tgz", + "integrity": "sha512-QEynrBC26x6TG9ZMzApR/kZ3lmt4lEIs2D+cHuDxt6fDGzahBUsQFBwJqhizzsM97JJI5YvmJhmihoYjdSSaXA==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.693.0.tgz", + "integrity": "sha512-UEDbYlYtK/e86OOMyFR4zEPyenIxDzO2DRdz3fwVW7RzZ94wfmSwBh/8skzPTuY1G7sI064cjHW0b0QG01Sdtg==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.693.0.tgz", + "integrity": "sha512-4S2y7VEtvdnjJX4JPl4kDQlslxXEZFnC50/UXVUYSt/AMc5A/GgspFNA5FVz4E3Gwpfobbf23hR2NBF8AGvYoQ==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.693.0.tgz", + "integrity": "sha512-v6Z/kWmLFqRLDPEwl9hJGhtTgIFHjZugSfF1Yqffdxf4n1AWgtHS7qSegakuMyN5pP4K2tvUD8qHJ+gGe2Bw2A==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.693.0.tgz", + "integrity": "sha512-sL8MvwNJU7ZpD7/d2VVb3by1GknIJUxzTIgYtVkDVA/ojo+KRQSSHxcj0EWWXF5DTSh2Tm+LrEug3y1ZyKHsDA==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.693.0.tgz", + "integrity": "sha512-kvaa4mXhCCOuW7UQnBhYqYfgWmwy7WSBSDClutwSLPZvgrhYj2l16SD2lN4IfYdxARYMJJ1lFYp3/jJG/9Yk4Q==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.693.0.tgz", + "integrity": "sha512-42WMsBjTNnjYxYuM3qD/Nq+8b7UdMopUq5OduMDxoM3mFTV6PXMMnfI4Z1TNnR4tYRvPXAnuNltF6xmjKbSJRA==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.693.0.tgz", + "integrity": "sha512-479UlJxY+BFjj3pJFYUNC0DCMrykuG7wBAXfsvZqQxKUa83DnH5Q1ID/N2hZLkxjGd4ZW0AC3lTOMxFelGzzpQ==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.693.0.tgz", + "integrity": "sha512-8LB210Pr6VeCiSb2hIra+sAH4KUBLyGaN50axHtIgufVK8jbKIctTZcVY5TO9Se+1107TsruzeXS7VeqVdJfFA==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.693.0.tgz", + "integrity": "sha512-BCki6sAZ5jYwIN/t3ElCiwerHad69ipHwPsDCxJQyeiOnJ8HG+lEpnVIfrnI8A0fLQNSF3Gtx6ahfBpKiv1Oug==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.693.0.tgz", + "integrity": "sha512-dXnXDPr+wIiJ1TLADACI1g9pkSB21KkMIko2u4CJ2JCBoxi5IqeTnVoa6YcC8GdFNVRl+PorZ3Zqfmf1EOTC6w==", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.693.0.tgz", + "integrity": "sha512-0LDmM+VxXp0u3rG0xQRWD/q6Ubi7G8I44tBPahevD5CaiDZTkmNTrVUf0VEJgVe0iCKBppACMBDkLB0/ETqkFw==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.693.0.tgz", + "integrity": "sha512-/KUq/KEpFFbQmNmpp7SpAtFAdViquDfD2W0QcG07zYBfz9MwE2ig48ALynXm5sMpRmnG7sJXjdvPtTsSVPfkiw==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.693.0.tgz", + "integrity": "sha512-YLUkMsUY0GLW/nfwlZ69cy1u07EZRmsv8Z9m0qW317/EZaVx59hcvmcvb+W4bFqj5E8YImTjoGfE4cZ0F9mkyw==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.693.0.tgz", + "integrity": "sha512-nDBTJMk1l/YmFULGfRbToOA2wjf+FkQT4dMgYCv+V9uSYsMzQj8A7Tha2dz9yv4vnQgYaEiErQ8d7HVyXcVEoA==", + "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-sdk/client-sagemaker/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.693.0.tgz", + "integrity": "sha512-eo4F6DRQ/kxS3gxJpLRv+aDNy76DxQJL5B3DPzpr9Vkq0ygVoi4GT5oIZLVaAVIJmi6k5qq9dLsYZfWLUxJJSg==", + "dependencies": { + "@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-sdk/client-sagemaker/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.693.0.tgz", + "integrity": "sha512-6EUfuKOujtddy18OLJUaXfKBgs+UcbZ6N/3QV4iOkubCUdeM1maIqs++B9bhCbWeaeF5ORizJw5FTwnyNjE/mw==", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.693.0.tgz", + "integrity": "sha512-td0OVX8m5ZKiXtecIDuzY3Y3UZIzvxEr57Hp21NOwieqKCG2UeyQWWeGPv0FQaU7dpTkvFmVNI+tx9iB8V/Nhg==", + "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-sdk/client-sagemaker/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm": { + "version": "3.693.0", + "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/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/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", + "@smithy/util-waiter": "^3.1.8", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@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-sdk/client-ssm/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "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-sdk/client-ssm/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.637.0", + "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-sdk/client-sso-oidc": { + "version": "3.637.0", + "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-sdk/client-sso-oidc/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "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-sdk/client-sso-oidc/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "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-sdk/client-sso/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.637.0", + "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-sdk/client-sts/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "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-sdk/client-sts/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.635.0", + "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-sdk/credential-provider-cognito-identity": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.730.0.tgz", + "integrity": "sha512-Ynp67VkpaaFubqPrqGxLbg5XuS+QTjR7JVhZvjNO6Su4tQVKBFSfQpDIXTyggD9UVixXy4NB9cqg30uvebDeiw==", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@aws-sdk/types": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.723.0.tgz", + "integrity": "sha512-LmK3kwiMZG1y5g3LGihT9mNkeNOmwEyPk6HGcJqh0wOSV4QpWoKu2epyKE4MLQNUUlz2kOVbVbOrwmI6ZcteuA==", + "dependencies": { + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@smithy/property-provider": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", + "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@smithy/types": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", + "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.693.0", + "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-sdk/credential-provider-env/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "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-sdk/credential-provider-http": { + "version": "3.635.0", + "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-sdk/credential-provider-http/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "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-sdk/credential-provider-ini": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/credential-provider-env": "3.758.0", + "@aws-sdk/credential-provider-http": "3.758.0", + "@aws-sdk/credential-provider-process": "3.758.0", + "@aws-sdk/credential-provider-sso": "3.758.0", + "@aws-sdk/credential-provider-web-identity": "3.758.0", + "@aws-sdk/nested-clients": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/client-sso": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/client-sso": "3.758.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/token-providers": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/middleware-logger": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@smithy/core": "^3.1.5", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/token-providers": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/nested-clients": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/util-endpoints": { + "version": "3.743.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "@smithy/util-endpoints": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/hash-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-retry": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.693.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", "license": "Apache-2.0", + "peer": true, "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", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.693.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", "license": "Apache-2.0", + "peer": true, "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", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.693.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@aws-sdk/core": "3.693.0", - "@aws-sdk/types": "3.692.0", - "@smithy/property-provider": "^3.1.9", - "@smithy/types": "^3.7.0", + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.693.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.693.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/property-provider": { + "version": "4.0.1", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/protocol-http": "^4.1.6", - "@smithy/types": "^3.7.0", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/middleware-logger": { - "version": "3.693.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/protocol-http": { + "version": "5.0.1", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/types": "^3.7.0", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.693.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/querystring-builder": { + "version": "4.0.1", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/protocol-http": "^4.1.6", - "@smithy/types": "^3.7.0", + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.693.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/querystring-parser": { + "version": "4.0.1", "license": "Apache-2.0", + "peer": true, "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", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.693.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/service-error-classification": { + "version": "4.0.1", "license": "Apache-2.0", + "peer": true, "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" + "@smithy/types": "^4.1.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/token-providers": { - "version": "3.693.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", "license": "Apache-2.0", + "peer": true, "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", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.693.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/util-endpoints": { - "version": "3.693.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/signature-v4": { + "version": "5.0.1", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/types": "^3.7.0", - "@smithy/util-endpoints": "^2.1.5", + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.693.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/smithy-client": { + "version": "4.1.6", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@aws-sdk/types": "3.692.0", - "@smithy/types": "^3.7.0", - "bowser": "^2.11.0", + "@smithy/core": "^3.1.5", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.693.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/types": { + "version": "4.1.0", "license": "Apache-2.0", + "peer": true, "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": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/url-parser": { + "version": "4.0.1", "license": "Apache-2.0", + "peer": true, "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-base64": { + "version": "4.0.0", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@smithy/util-utf8": { - "version": "3.0.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.637.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", "license": "Apache-2.0", + "peer": true, "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": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.637.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", "license": "Apache-2.0", + "peer": true, "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", + "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.637.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/types": { - "version": "3.609.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.4", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.7", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", - "@smithy/util-base64": "^3.0.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.7", "license": "Apache-2.0", + "peer": true, "dependencies": { + "@smithy/config-resolver": "^4.0.1", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-utf8": { - "version": "3.0.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-endpoints": { + "version": "3.0.1", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/types": { - "version": "3.609.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-middleware": { + "version": "4.0.1", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.4", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-retry": { + "version": "4.0.1", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", - "@smithy/util-base64": "^3.0.0", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-stream": { + "version": "4.1.2", "license": "Apache-2.0", + "peer": true, "dependencies": { + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-utf8": { - "version": "3.0.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-utf8": { + "version": "4.0.0", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", + "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sts": { + "node_modules/@aws-sdk/credential-provider-node": { "version": "3.637.0", "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/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", - "@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/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", - "@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-sdk/client-sts/node_modules/@aws-sdk/types": { - "version": "3.609.0", + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.620.1", "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" }, @@ -9434,75 +11361,78 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.4", + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.637.0", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", + "@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", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "license": "Apache-2.0", - "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.637.0" } }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-utf8": { - "version": "3.0.0", + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.620.1", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^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-sdk/client-sts/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.621.0", "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", + "@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-sdk/core": { - "version": "3.635.0", + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/types": { + "version": "3.609.0", "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-sdk/credential-provider-env": { + "node_modules/@aws-sdk/credential-provider-process": { "version": "3.693.0", "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" }, @@ -9510,7 +11440,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/credential-provider-env/node_modules/@aws-sdk/core": { + "node_modules/@aws-sdk/credential-provider-process/node_modules/@aws-sdk/core": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9530,25 +11460,23 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.635.0", + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.637.0", "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/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/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.1.3", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/credential-provider-http/node_modules/@aws-sdk/types": { + "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/types": { "version": "3.609.0", "license": "Apache-2.0", "dependencies": { @@ -9559,89 +11487,23 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.4", - "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-sdk/credential-provider-ini": { + "node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.758.0", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/core": "3.758.0", - "@aws-sdk/credential-provider-env": "3.758.0", - "@aws-sdk/credential-provider-http": "3.758.0", - "@aws-sdk/credential-provider-process": "3.758.0", - "@aws-sdk/credential-provider-sso": "3.758.0", - "@aws-sdk/credential-provider-web-identity": "3.758.0", "@aws-sdk/nested-clients": "3.758.0", "@aws-sdk/types": "3.734.0", - "@smithy/credential-provider-imds": "^4.0.1", "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/client-sso": { - "version": "3.758.0", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/middleware-host-header": "3.734.0", - "@aws-sdk/middleware-logger": "3.734.0", - "@aws-sdk/middleware-recursion-detection": "3.734.0", - "@aws-sdk/middleware-user-agent": "3.758.0", - "@aws-sdk/region-config-resolver": "3.734.0", - "@aws-sdk/types": "3.734.0", - "@aws-sdk/util-endpoints": "3.743.0", - "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", - "@smithy/config-resolver": "^4.0.1", - "@smithy/core": "^3.1.5", - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/hash-node": "^4.0.1", - "@smithy/invalid-dependency": "^4.0.1", - "@smithy/middleware-content-length": "^4.0.1", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/middleware-retry": "^4.0.7", - "@smithy/middleware-serde": "^4.0.2", - "@smithy/middleware-stack": "^4.0.1", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/protocol-http": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.7", - "@smithy/util-defaults-mode-node": "^4.0.7", - "@smithy/util-endpoints": "^3.0.1", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-retry": "^4.0.1", - "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/core": { + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/core": { "version": "3.758.0", "license": "Apache-2.0", "peer": true, @@ -9662,95 +11524,11 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.758.0", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/property-provider": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.758.0", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/property-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/util-stream": "^4.1.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.758.0", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.758.0", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@aws-sdk/client-sso": "3.758.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/token-providers": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.734.0", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/middleware-logger": { + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/types": { "version": "3.734.0", "license": "Apache-2.0", "peer": true, "dependencies": { - "@aws-sdk/types": "3.734.0", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, @@ -9758,13 +11536,11 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.734.0", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/abort-controller": { + "version": "4.0.1", "license": "Apache-2.0", "peer": true, "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, @@ -9772,116 +11548,81 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.758.0", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/core": { + "version": "3.1.5", "license": "Apache-2.0", "peer": true, "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@aws-sdk/util-endpoints": "3.743.0", - "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.734.0", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/token-providers": { - "version": "3.758.0", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@aws-sdk/nested-clients": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/types": { - "version": "3.734.0", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", "license": "Apache-2.0", "peer": true, "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/util-endpoints": { - "version": "3.743.0", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", "license": "Apache-2.0", "peer": true, "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/types": "^4.1.0", - "@smithy/util-endpoints": "^3.0.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.734.0", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", "license": "Apache-2.0", "peer": true, "dependencies": { - "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", - "bowser": "^2.11.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.758.0", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", "license": "Apache-2.0", "peer": true, "dependencies": { - "@aws-sdk/middleware-user-agent": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/node-config-provider": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/abort-controller": { + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/middleware-stack": { "version": "4.0.1", "license": "Apache-2.0", "peer": true, @@ -9893,84 +11634,73 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/config-resolver": { + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/node-config-provider": { "version": "4.0.1", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/core": { - "version": "3.1.5", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/middleware-serde": "^4.0.2", + "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-stream": "^4.1.2", - "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/credential-provider-imds": { + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/property-provider": { "version": "4.0.1", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/property-provider": "^4.0.1", "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/fetch-http-handler": { + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/protocol-http": { "version": "5.0.1", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/protocol-http": "^5.0.1", - "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", - "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/hash-node": { + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/querystring-builder": { "version": "4.0.1", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.1.0", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", + "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/invalid-dependency": { + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/querystring-parser": { "version": "4.0.1", "license": "Apache-2.0", "peer": true, @@ -9982,72 +11712,70 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/is-array-buffer": { - "version": "4.0.0", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", "license": "Apache-2.0", "peer": true, "dependencies": { + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-content-length": { - "version": "4.0.1", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/signature-v4": { + "version": "5.0.1", "license": "Apache-2.0", "peer": true, "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-endpoint": { - "version": "4.0.6", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/smithy-client": { + "version": "4.1.6", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/core": "^3.1.5", - "@smithy/middleware-serde": "^4.0.2", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", - "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-retry": { - "version": "4.0.7", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/types": { + "version": "4.1.0", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/service-error-classification": "^4.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-retry": "^4.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" + "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-serde": { - "version": "4.0.2", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/url-parser": { + "version": "4.0.1", "license": "Apache-2.0", "peer": true, "dependencies": { + "@smithy/querystring-parser": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, @@ -10055,61 +11783,55 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-stack": { - "version": "4.0.1", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-base64": { + "version": "4.0.0", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/node-config-provider": { - "version": "4.0.1", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/node-http-handler": { - "version": "4.0.3", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/abort-controller": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/querystring-builder": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/property-provider": { - "version": "4.0.1", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/protocol-http": { - "version": "5.0.1", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-middleware": { + "version": "4.0.1", "license": "Apache-2.0", "peer": true, "dependencies": { @@ -10120,65 +11842,114 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/querystring-builder": { - "version": "4.0.1", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-stream": { + "version": "4.1.2", "license": "Apache-2.0", "peer": true, "dependencies": { + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", "@smithy/types": "^4.1.0", - "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/querystring-parser": { - "version": "4.0.1", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/service-error-classification": { - "version": "4.0.1", + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-utf8": { + "version": "4.0.0", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/types": "^4.1.0" + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/shared-ini-file-loader": { - "version": "4.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.730.0.tgz", + "integrity": "sha512-Z25yfmHOehgIDVyY8h7GmAEbodHD2iLgNmrBBkkJXCE6d4GwDet3Qeyw4bQPPyuycBtYOUiz5Oco03+YGOEhYA==", "dependencies": { - "@smithy/types": "^4.1.0", + "@aws-sdk/client-cognito-identity": "3.730.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/credential-provider-cognito-identity": "3.730.0", + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-ini": "3.730.0", + "@aws-sdk/credential-provider-node": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/signature-v4": { - "version": "5.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/client-sso": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.730.0.tgz", + "integrity": "sha512-mI8kqkSuVlZklewEmN7jcbBMyVODBld3MsTjCKSl5ztduuPX69JD7nXLnWWPkw1PX4aGTO24AEoRMGNxntoXUg==", "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-uri-escape": "^4.0.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.730.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -10186,663 +11957,754 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/smithy-client": { - "version": "4.1.6", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/core": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.730.0.tgz", + "integrity": "sha512-jonKyR+2GcqbZj2WDICZS0c633keLc9qwXnePu83DfAoFXMMIMyoR/7FOGf8F3OrIdGh8KzE9VvST+nZCK9EJA==", "dependencies": { - "@smithy/core": "^3.1.5", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/middleware-stack": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-stream": "^4.1.2", + "@aws-sdk/types": "3.723.0", + "@smithy/core": "^3.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/signature-v4": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/types": { - "version": "4.1.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.730.0.tgz", + "integrity": "sha512-fFXgo3jBXLWqu8I07Hd96mS7RjrtpDgm3bZShm0F3lKtqDQF+hObFWq9A013SOE+RjMLVfbABhToXAYct3FcBw==", "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/url-parser": { - "version": "4.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.730.0.tgz", + "integrity": "sha512-1aF3elbCzpVhWLAuV63iFElfLOqLGGTp4fkf2VAFIDO3hjshpXUQssTgIWiBwwtJYJdOSxaFrCU7u8frjr/5aQ==", "dependencies": { - "@smithy/querystring-parser": "^4.0.1", - "@smithy/types": "^4.1.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-stream": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.730.0.tgz", + "integrity": "sha512-zwsxkBuQuPp06o45ATAnznHzj3+ibop/EaTytNzSv0O87Q59K/jnS/bdtv1n6bhe99XCieRNTihvtS7YklzK7A==", + "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-base64": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.730.0.tgz", + "integrity": "sha512-ztRjh1edY7ut2wwrj1XqHtqPY/NXEYIk5fYf04KKsp8zBi81ScVqP7C+Cst6PFKixjgLSG6RsqMx9GSAalVv0Q==", "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-ini": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-body-length-browser": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.730.0.tgz", + "integrity": "sha512-cNKUQ81eptfZN8MlSqwUq3+5ln8u/PcY57UmLZ+npxUHanqO1akpgcpNsLpmsIkoXGbtSQrLuDUgH86lS/SWOw==", "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-body-length-node": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.730.0.tgz", + "integrity": "sha512-SdI2xrTbquJLMxUh5LpSwB8zfiKq3/jso53xWRgrVfeDlrSzZuyV6QghaMs3KEEjcNzwEnTfSIjGQyRXG9VrEw==", "dependencies": { + "@aws-sdk/client-sso": "3.730.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/token-providers": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-buffer-from": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.730.0.tgz", + "integrity": "sha512-l5vdPmvF/d890pbvv5g1GZrdjaSQkyPH/Bc8dO/ZqkWxkIP8JNgl48S2zgf4DkP3ik9K2axWO828L5RsMDQzdA==", "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-config-provider": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.723.0.tgz", + "integrity": "sha512-LLVzLvk299pd7v4jN9yOSaWDZDfH0SnBPb6q+FDPaOCMGBY8kuwQso7e/ozIKSmZHRMGO3IZrflasHM+rI+2YQ==", "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.7", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-logger": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.723.0.tgz", + "integrity": "sha512-chASQfDG5NJ8s5smydOEnNK7N0gDMyuPbx7dYYcm1t/PKtnVfvWF+DHCTrRC2Ej76gLJVCVizlAJKM8v8Kg3cg==", "dependencies": { - "@smithy/property-provider": "^4.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "bowser": "^2.11.0", + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.7", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.723.0.tgz", + "integrity": "sha512-7usZMtoynT9/jxL/rkuDOFQ0C2mhXl4yCm67Rg7GNTstl67u7w5WN1aIRImMeztaKlw8ExjoTyo6WTs1Kceh7A==", "dependencies": { - "@smithy/config-resolver": "^4.0.1", - "@smithy/credential-provider-imds": "^4.0.1", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/property-provider": "^4.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", + "@aws-sdk/types": "3.723.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-endpoints": { - "version": "3.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.730.0.tgz", + "integrity": "sha512-aPMZvNmf2a42B41au3bA3ODU4HfHka2nYT/SAIhhVXH1ENYfAmZo7FraFPxetKepFMCtL7j4QE6/LDucK6liIw==", "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/types": "^4.1.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@smithy/core": "^3.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-hex-encoding": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/nested-clients": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.730.0.tgz", + "integrity": "sha512-vilIgf1/7kre8DdE5zAQkDOwHFb/TahMn/6j2RZwFLlK7cDk91r19deSiVYnKQkupDMtOfNceNqnorM4I3PDzw==", "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.730.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-middleware": { - "version": "4.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.723.0.tgz", + "integrity": "sha512-tGF/Cvch3uQjZIj34LY2mg8M2Dr4kYG8VU8Yd0dFnB1ybOEOveIK/9ypUo9ycZpB9oO6q01KRe5ijBaxNueUQg==", "dependencies": { - "@smithy/types": "^4.1.0", + "@aws-sdk/types": "3.723.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-retry": { - "version": "4.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/token-providers": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.730.0.tgz", + "integrity": "sha512-BSPssGj54B/AABWXARIPOT/1ybFahM1ldlfmXy9gRmZi/afe9geWJGlFYCCt3PmqR+1Ny5XIjSfue+kMd//drQ==", "dependencies": { - "@smithy/service-error-classification": "^4.0.1", - "@smithy/types": "^4.1.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-stream": { - "version": "4.1.2", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/types": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.723.0.tgz", + "integrity": "sha512-LmK3kwiMZG1y5g3LGihT9mNkeNOmwEyPk6HGcJqh0wOSV4QpWoKu2epyKE4MLQNUUlz2kOVbVbOrwmI6ZcteuA==", "dependencies": { - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/types": "^4.1.0", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-uri-escape": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-endpoints": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.730.0.tgz", + "integrity": "sha512-1KTFuVnk+YtLgWr6TwDiggcDqtPpOY2Cszt3r2lkXfaEAX6kHyOZi1vdvxXjPU5LsOBJem8HZ7KlkmrEi+xowg==", "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-utf8": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.723.0.tgz", + "integrity": "sha512-Wh9I6j2jLhNFq6fmXydIpqD1WyQLyTfSxjW9B+PXSnPyk3jtQW8AKQur7p97rO8LAUzVI0bv8kb3ZzDEVbquIg==", "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.730.0.tgz", + "integrity": "sha512-yBvkOAjqsDEl1va4eHNOhnFBk0iCY/DBFNyhvtTMqPF4NO+MITWpFs3J9JtZKzJlQ6x0Yb9TLQ8NhDjEISz5Ug==", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.637.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/abort-controller": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", + "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", "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", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.620.1", - "license": "Apache-2.0", + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/config-resolver": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", + "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.637.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/core": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.5.3.tgz", + "integrity": "sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA==", "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", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.637.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.620.1", - "license": "Apache-2.0", + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", + "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", "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", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.621.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.4.tgz", + "integrity": "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==", "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.621.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/types": { - "version": "3.609.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/hash-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", + "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/invalid-dependency": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", + "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", "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", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-process/node_modules/@aws-sdk/core": { - "version": "3.693.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", "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": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.637.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-content-length": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", + "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", "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", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/types": { - "version": "3.609.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-endpoint": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.11.tgz", + "integrity": "sha512-zDogwtRLzKl58lVS8wPcARevFZNBOOqnmzWWxVe9XiaXU2CADFjvJ9XfNibgkOWs08sxLuSr81NrpY4mgp9OwQ==", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/core": "^3.5.3", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-middleware": "^4.0.4", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.758.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-retry": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.12.tgz", + "integrity": "sha512-wvIH70c4e91NtRxdaLZF+mbLZ/HcC6yg7ySKUiufL6ESp6zJUSnJucZ309AvG9nqCFHSRB5I6T3Ez1Q9wCh0Ww==", "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/nested-clients": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/property-provider": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.5", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "tslib": "^2.6.2", + "uuid": "^9.0.1" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/core": { - "version": "3.758.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-serde": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", + "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/core": "^3.1.5", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/property-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/signature-v4": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/util-middleware": "^4.0.1", - "fast-xml-parser": "4.4.1", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/types": { - "version": "3.734.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-stack": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", + "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/abort-controller": { - "version": "4.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/node-config-provider": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", + "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/core": { - "version": "3.1.5", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/node-http-handler": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", + "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", "dependencies": { - "@smithy/middleware-serde": "^4.0.2", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-stream": "^4.1.2", - "@smithy/util-utf8": "^4.0.0", + "@smithy/abort-controller": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/fetch-http-handler": { - "version": "5.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/property-provider": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", + "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", "dependencies": { - "@smithy/protocol-http": "^5.0.1", - "@smithy/querystring-builder": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-base64": "^4.0.0", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/is-array-buffer": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/protocol-http": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", + "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", "dependencies": { + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/middleware-endpoint": { - "version": "4.0.6", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/querystring-builder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", + "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", "dependencies": { - "@smithy/core": "^3.1.5", - "@smithy/middleware-serde": "^4.0.2", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", - "@smithy/util-middleware": "^4.0.1", + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/middleware-serde": { - "version": "4.0.2", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/querystring-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", + "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/middleware-stack": { - "version": "4.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/service-error-classification": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.5.tgz", + "integrity": "sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA==", "dependencies": { - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "@smithy/types": "^4.3.1" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/node-config-provider": { - "version": "4.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", + "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", "dependencies": { - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/node-http-handler": { - "version": "4.0.3", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/signature-v4": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", + "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", "dependencies": { - "@smithy/abort-controller": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/querystring-builder": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/property-provider": { - "version": "4.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/smithy-client": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.3.tgz", + "integrity": "sha512-xxzNYgA0HD6ETCe5QJubsxP0hQH3QK3kbpJz3QrosBCuIWyEXLR/CO5hFb2OeawEKUxMNhz3a1nuJNN2np2RMA==", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/core": "^3.5.3", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/protocol-http": { - "version": "5.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/types": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", + "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", "dependencies": { - "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/querystring-builder": { - "version": "4.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/url-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", + "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", "dependencies": { - "@smithy/types": "^4.1.0", - "@smithy/util-uri-escape": "^4.0.0", + "@smithy/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/querystring-parser": { - "version": "4.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/shared-ini-file-loader": { - "version": "4.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", "dependencies": { - "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/signature-v4": { - "version": "5.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-uri-escape": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/smithy-client": { - "version": "4.1.6", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", "dependencies": { - "@smithy/core": "^3.1.5", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/middleware-stack": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-stream": "^4.1.2", + "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/types": { - "version": "4.1.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", "dependencies": { "tslib": "^2.6.2" }, @@ -10850,86 +12712,95 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/url-parser": { - "version": "4.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.19.tgz", + "integrity": "sha512-mvLMh87xSmQrV5XqnUYEPoiFFeEGYeAKIDDKdhE2ahqitm8OHM3aSvhqL6rrK6wm1brIk90JhxDf5lf2hbrLbQ==", "dependencies": { - "@smithy/querystring-parser": "^4.0.1", - "@smithy/types": "^4.1.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-base64": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.19.tgz", + "integrity": "sha512-8tYnx+LUfj6m+zkUUIrIQJxPM1xVxfRBvoGHua7R/i6qAxOMjqR6CpEpDwKoIs1o0+hOjGvkKE23CafKL0vJ9w==", "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-body-length-browser": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-endpoints": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", + "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-buffer-from": { + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-hex-encoding": { "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-hex-encoding": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-middleware": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", + "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", "dependencies": { + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-middleware": { - "version": "4.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-retry": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.5.tgz", + "integrity": "sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg==", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/service-error-classification": "^4.0.5", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-stream": { - "version": "4.1.2", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-stream": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.2.tgz", + "integrity": "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==", "dependencies": { - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/types": "^4.1.0", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/types": "^4.3.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", @@ -10940,10 +12811,10 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-uri-escape": { + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-uri-escape": { "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", "dependencies": { "tslib": "^2.6.2" }, @@ -10951,10 +12822,10 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-utf8": { + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-utf8": { "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" @@ -27428,6 +29299,7 @@ "@aws-sdk/credential-provider-env": "<3.731.0", "@aws-sdk/credential-provider-process": "<3.731.0", "@aws-sdk/credential-provider-sso": "<3.731.0", + "@aws-sdk/credential-providers": "<3.731.0", "@aws-sdk/lib-storage": "<3.731.0", "@aws-sdk/property-provider": "<3.731.0", "@aws-sdk/protocol-http": "<3.731.0", diff --git a/packages/core/package.json b/packages/core/package.json index a9c7dfbd0f1..4209191fdf2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -516,6 +516,7 @@ "@amzn/amazon-q-developer-streaming-client": "file:../../src.gen/@amzn/amazon-q-developer-streaming-client", "@amzn/codewhisperer-streaming": "file:../../src.gen/@amzn/codewhisperer-streaming", "@amzn/sagemaker-client": "file:../../src.gen/@amzn/sagemaker-client/1.0.0.tgz", + "@aws-sdk/credential-providers": "<3.731.0", "@aws-sdk/client-api-gateway": "<3.731.0", "@aws-sdk/client-apprunner": "<3.731.0", "@aws-sdk/client-cloudcontrol": "<3.731.0", diff --git a/packages/core/resources/icons/fonts/aws-toolkit-icons.woff b/packages/core/resources/icons/fonts/aws-toolkit-icons.woff new file mode 100644 index 0000000000000000000000000000000000000000..f17f02c9974fe7cb176fef022dc24836e2a5fee8 GIT binary patch literal 59412 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 literal 0 HcmV?d00001 diff --git a/packages/core/resources/sagemaker_connect b/packages/core/resources/sagemaker_connect new file mode 100755 index 00000000000..b4a00f43cd5 --- /dev/null +++ b/packages/core/resources/sagemaker_connect @@ -0,0 +1,130 @@ +#!/bin/bash + +set -x + +_get_ssm_session_info() { + local credentials_type="$1" + local aws_resource_arn="$2" + local local_endpoint_port="$3" + + local url_to_get_session_info="http://localhost:${local_endpoint_port}/get_session?connection_identifier=${aws_resource_arn}&credentials_type=${credentials_type}" + + # Use curl with --write-out to capture HTTP status + response=$(curl -s -w "%{http_code}" -o /tmp/ssm_session_response.json "$url_to_get_session_info") + http_status="${response: -3}" + session_json=$(cat /tmp/ssm_session_response.json) + + if [[ "$http_status" -ne 200 ]]; then + echo "Error: Failed to get SSM session info. HTTP status: $http_status" + echo "Response: $session_json" + exit 1 + fi + + if [ -z "$session_json" ]; then + echo "Error: SSM connection info is empty." + exit 1 + fi + + export SSM_SESSION_JSON="$session_json" +} + +_get_ssm_session_info_async() { + local credentials_type="$1" + local aws_resource_arn="$2" + local local_endpoint_port="$3" + + local request_id=$(date +%s%3N) + local url_base="http://localhost:${local_endpoint_port}/get_session_async" + local url_to_get_session_info="${url_base}?connection_identifier=${aws_resource_arn}&credentials_type=${credentials_type}&request_id=${request_id}" + + local max_retries=60 + local retry_interval=5 + local attempt=1 + + while (( attempt <= max_retries )); do + response=$(curl -s -w "%{http_code}" -o /tmp/ssm_session_response.json "$url_to_get_session_info") + http_status="${response: -3}" + session_json=$(cat /tmp/ssm_session_response.json) + + if [[ "$http_status" -eq 200 ]]; then + export SSM_SESSION_JSON="$session_json" + return 0 + elif [[ "$http_status" -eq 202 || "$http_status" -eq 204 ]]; then + echo "Info: Session not ready (HTTP $http_status). Retrying in $retry_interval seconds... [$attempt/$max_retries]" + sleep $retry_interval + ((attempt++)) + else + echo "Error: Failed to get SSM session info. HTTP status: $http_status" + echo "Response: $session_json" + exit 1 + fi + done + + echo "Error: Timed out after $max_retries attempts waiting for session to be ready." + exit 1 +} + + +# Validate input +if [ "$#" -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +HOSTNAME="$1" + +# Parse creds_type and AWS resource ARN from HOSTNAME +if [[ "$HOSTNAME" =~ ^sm_([^_]+)_(arn_._aws.*)$ ]]; then + CREDS_TYPE="${BASH_REMATCH[1]}" + AWS_RESOURCE_ARN="${BASH_REMATCH[2]}" +else + echo "Hostname: $HOSTNAME" + echo "Invalid hostname format. Expected format: sm__" + exit 1 +fi + +# Workaround: Replace "__" with "/" in ARN +AWS_RESOURCE_ARN=$(echo "${AWS_RESOURCE_ARN}" | sed 's|__|/|g; s|_._|:|g; s|jupyterlab/default|JupyterLab/default|g') +REGION=$(echo "$AWS_RESOURCE_ARN" | cut -d: -f4) + +# Validate credentials type +if [[ "$CREDS_TYPE" != "lc" && "$CREDS_TYPE" != "dl" ]]; then + echo "Invalid creds_type. Must be 'lc' or 'dl'." + exit 1 +fi + +# Validate required env var and file +if [ -z "$SAGEMAKER_LOCAL_SERVER_FILE_PATH" ]; then + echo "[Error] SAGEMAKER_LOCAL_SERVER_FILE_PATH is not set" + exit 1 +fi + +if [ ! -f "$SAGEMAKER_LOCAL_SERVER_FILE_PATH" ]; then + echo "[Error] File not found at SAGEMAKER_LOCAL_SERVER_FILE_PATH: $SAGEMAKER_LOCAL_SERVER_FILE_PATH" + exit 1 +fi + +# Extract port from file +LOCAL_ENDPOINT_PORT=$(jq -r '.port' "$SAGEMAKER_LOCAL_SERVER_FILE_PATH") +if [ -z "$LOCAL_ENDPOINT_PORT" ] || [ "$LOCAL_ENDPOINT_PORT" == "null" ]; then + echo "[Error] 'port' field is missing or invalid in $SAGEMAKER_LOCAL_SERVER_FILE_PATH" + exit 1 +fi + +# Determine region from ARN +if [ "$CREDS_TYPE" == "lc" ]; then + credentials_type="local" + _get_ssm_session_info "$credentials_type" "$AWS_RESOURCE_ARN" "$LOCAL_ENDPOINT_PORT" +elif [ "$CREDS_TYPE" == "dl" ]; then + credentials_type="deeplink" + _get_ssm_session_info_async "$credentials_type" "$AWS_RESOURCE_ARN" "$LOCAL_ENDPOINT_PORT" +else + echo "[Error] Invalid creds_type. Must be 'lc' or 'dl'." + exit 1 +fi + +echo "Extracting AWS_SSM_CLI: $AWS_SSM_CLI" + +# Execute the session +AWS_SSM_CLI="${AWS_SSM_CLI:=session-manager-plugin}" +exec "${AWS_SSM_CLI}" "$SSM_SESSION_JSON" "$REGION" StartSession diff --git a/packages/core/resources/sagemaker_connect.ps1 b/packages/core/resources/sagemaker_connect.ps1 new file mode 100644 index 00000000000..c05dbfe3e53 --- /dev/null +++ b/packages/core/resources/sagemaker_connect.ps1 @@ -0,0 +1,128 @@ +param ( + [Parameter(Mandatory=$true)][string]$Hostname, +) + +Set-PSDebug -Trace 1 +function Get-SSMSessionInfo { + param ( + [string]$CredentialsType, + [string]$AwsResourceArn, + [int]$LocalEndpointPort + ) + + $url = "http://127.0.0.1:$LocalEndpointPort/get_session?connection_identifier=$AwsResourceArn&credentials_type=$CredentialsType" + + try { + $response = Invoke-WebRequest -Uri $url -UseBasicParsing -ErrorAction Stop + if ($response.StatusCode -ne 200) { + Write-Error "Error: Failed to get SSM session info. HTTP status: $($response.StatusCode)" + Write-Error "Response: $($response.Content)" + exit 1 + } + if (-not $response.Content) { + Write-Error "Error: SSM connection info is empty." + exit 1 + } + $script:SSM_SESSION_JSON = $response.Content + } catch { + Write-Error "Exception: $_" + exit 1 + } +} + +function Get-SSMSessionInfoAsync { + param ( + [string]$CredentialsType, + [string]$AwsResourceArn, + [int]$LocalEndpointPort + ) + + $requestId = [string][DateTimeOffset]::Now.ToUnixTimeMilliseconds() + $url = "http://localhost:$LocalEndpointPort/get_session_async?connection_identifier=$AwsResourceArn&credentials_type=$CredentialsType&request_id=$requestId" + + $maxRetries = 60 + $retryInterval = 5 + + for ($attempt = 1; $attempt -le $maxRetries; $attempt++) { + try { + $response = Invoke-WebRequest -Uri $url -UseBasicParsing -ErrorAction Stop + $statusCode = $response.StatusCode + if ($statusCode -eq 200) { + $script:SSM_SESSION_JSON = $response.Content + return + } elseif ($statusCode -eq 202 -or $statusCode -eq 204) { + Write-Host "Info: Session not ready (HTTP $statusCode). Retrying in $retryInterval seconds... [$attempt/$maxRetries]" + Start-Sleep -Seconds $retryInterval + } else { + Write-Error "Error: Failed to get SSM session info. HTTP status: $statusCode" + Write-Error "Response: $($response.Content)" + exit 1 + } + } catch { + Write-Error "Exception: $_" + exit 1 + } + } + + Write-Error "Error: Timed out after $maxRetries attempts waiting for session to be ready." + exit 1 +} + +Write-Host "HOSTName: $Hostname" + +# Parse creds_type and AWS resource ARN from HOSTNAME +if ($Hostname -match "^sm_([^_]+)_(arn_._aws.*)$") { + $CREDS_TYPE = $matches[1] + $AWS_RESOURCE_ARN = $matches[2] -replace '_._', ':' -replace '__', '/' +} else { + Write-Error "Invalid hostname format. Expected format: sm__ with ':' replaced by '--' and '/' replaced by '__'" + exit 1 +} + +$REGION = ($AWS_RESOURCE_ARN -split ':')[3] +Write-Host "Extracted CREDS_TYPE: $CREDS_TYPE" +Write-Host "Extracted AWS_RESOURCE_ARN: $AWS_RESOURCE_ARN" +Write-Host "REGION: $REGION" + +# Validate credentials type +if ($CREDS_TYPE -ne "lc" -and $CREDS_TYPE -ne "dl") { + Write-Error "Invalid creds_type. Must be 'lc' or 'dl'." + exit 1 +} + +try { + $jsonContent = Get-Content $env:SAGEMAKER_LOCAL_SERVER_FILE_PATH -Raw | ConvertFrom-Json + $LOCAL_ENDPOINT_PORT = $jsonContent.port +} catch { + Write-Error "[Error] Failed to read or parse JSON file at $env:SAGEMAKER_LOCAL_SERVER_FILE_PATH" + exit 1 +} + +if (-not $LOCAL_ENDPOINT_PORT -or $LOCAL_ENDPOINT_PORT -eq "null") { + Write-Error "[Error] 'port' field is missing or invalid in $env:SAGEMAKER_LOCAL_SERVER_FILE_PATH" + exit 1 +} + + +# Retrieve SSM session +if ($CREDS_TYPE -eq "lc") { + Get-SSMSessionInfo -CredentialsType "local" -AwsResourceArn $AWS_RESOURCE_ARN -LocalEndpointPort $LOCAL_ENDPOINT_PORT +} elseif ($CREDS_TYPE -eq "dl") { + Get-SSMSessionInfoAsync -CredentialsType "deeplink" -AwsResourceArn $AWS_RESOURCE_ARN -LocalEndpointPort $LOCAL_ENDPOINT_PORT +} + +# Execute the session +$sessionPlugin = if ($env:AWS_SSM_CLI) { $env:AWS_SSM_CLI } else { "session-manager-plugin" } + + +$jsonObj = $script:SSM_SESSION_JSON | ConvertFrom-Json + +$streamUrl = $jsonObj.StreamUrl +$tokenValue = $jsonObj.TokenValue +$sessionId = $jsonObj.SessionId + +Write-Host "Stream URL: $streamUrl" +Write-Host "Token Value: $tokenValue" +Write-Host "Session ID: $sessionId" + +& $sessionPlugin "{\`"streamUrl\`":\`"$streamUrl\`",\`"tokenValue\`":\`"$tokenValue\`",\`"sessionId\`":\`"$sessionId\`"}" "$Region" "StartSession" diff --git a/packages/core/src/auth/auth.ts b/packages/core/src/auth/auth.ts index 59b3a21f840..5cc9f810baa 100644 --- a/packages/core/src/auth/auth.ts +++ b/packages/core/src/auth/auth.ts @@ -182,6 +182,10 @@ export class Auth implements AuthService, ConnectionManager { return Object.values(this._declaredConnections) } + public getCurrentProfileId() { + return this.store.getCurrentProfileId() + } + @withTelemetryContext({ name: 'restorePreviousSession', class: authClassName }) public async restorePreviousSession(): Promise { const id = this.store.getCurrentProfileId() diff --git a/packages/core/src/auth/credentials/store.ts b/packages/core/src/auth/credentials/store.ts index 2fd2d29b18b..53cc5573858 100644 --- a/packages/core/src/auth/credentials/store.ts +++ b/packages/core/src/auth/credentials/store.ts @@ -18,7 +18,7 @@ export interface CachedCredentials { * Simple cache for credentials */ export class CredentialsStore { - private readonly credentialsCache: { [key: string]: CachedCredentials } + public readonly credentialsCache: { [key: string]: CachedCredentials } public constructor() { this.credentialsCache = {} diff --git a/packages/core/src/awsService/sagemaker/activation.ts b/packages/core/src/awsService/sagemaker/activation.ts index 4de3fb533db..5305a541885 100644 --- a/packages/core/src/awsService/sagemaker/activation.ts +++ b/packages/core/src/awsService/sagemaker/activation.ts @@ -4,11 +4,22 @@ */ import { Commands } from '../../shared/vscode/commands2' -import { ExtContext } from '../../shared/extensions' +import { SagemakerSpaceNode } from './explorer/sagemakerSpaceNode' import { SagemakerParentNode } from './explorer/sagemakerParentNode' -import { filterSpaceAppsByDomainUserProfiles } from './commands' +import * as uriHandlers from './uriHandlers' +import { getLogger } from '../../shared/logger/logger' +import { openRemoteConnect, filterSpaceAppsByDomainUserProfiles } from './commands' +import { ExtContext } from '../../shared/extensions' export async function activate(ctx: ExtContext): Promise { + ctx.extensionContext.subscriptions.push( + uriHandlers.register(ctx), + Commands.register('aws.sagemaker.openRemoteConnection', async (node: SagemakerSpaceNode) => { + getLogger().info('start openRemoteConnection') + await openRemoteConnect(node, ctx.extensionContext) + }) + ) + ctx.extensionContext.subscriptions.push( Commands.register('aws.sagemaker.filterSpaceApps', async (node: SagemakerParentNode) => { await filterSpaceAppsByDomainUserProfiles(node) diff --git a/packages/core/src/awsService/sagemaker/commands.ts b/packages/core/src/awsService/sagemaker/commands.ts index 19032190dfb..0c4b5400350 100644 --- a/packages/core/src/awsService/sagemaker/commands.ts +++ b/packages/core/src/awsService/sagemaker/commands.ts @@ -5,10 +5,16 @@ import * as vscode from 'vscode' import * as nls from 'vscode-nls' -import { getLogger } from '../../shared/logger/logger' import { SagemakerConstants } from './explorer/constants' import { SagemakerParentNode } from './explorer/sagemakerParentNode' import { DomainKeyDelimiter } from './utils' +import { startVscodeRemote } from '../../shared/extensions/ssh' +import { getLogger } from '../../shared/logger/logger' +import { SagemakerSpaceNode } from './explorer/sagemakerSpaceNode' +import { isRemoteWorkspace } from '../../shared/vscode/env' +import _ from 'lodash' +import { prepareDevEnvConnection } from './model' +import { ExtContext } from '../../shared/extensions' const localize = nls.loadMessageBundle() @@ -67,3 +73,70 @@ export async function filterSpaceAppsByDomainUserProfiles(parentNode: SagemakerP await vscode.commands.executeCommand('aws.refreshAwsExplorerNode', parentNode) } } + +export async function deeplinkConnect( + ctx: ExtContext, + connectionIdentifier: string, + session: string, + wsUrl: string, + token: string, + domain: string +) { + getLogger().debug( + `sm:deeplinkConnect: connectionIdentifier: ${connectionIdentifier} session: ${session} wsUrl: ${wsUrl} token: ${token}` + ) + + if (isRemoteWorkspace()) { + void vscode.window.showErrorMessage( + 'You are in a remote workspace, skipping deeplink connect. Please open from a local workspace.' + ) + return + } + + const remoteEnv = await prepareDevEnvConnection( + connectionIdentifier, + ctx.extensionContext, + 'sm_dl', + session, + wsUrl, + token, + domain + ) + + try { + await startVscodeRemote( + remoteEnv.SessionProcess, + remoteEnv.hostname, + '/home/sagemaker-user', + remoteEnv.vscPath, + 'sagemaker-user' + ) + } catch (err) { + getLogger().error( + `sm:OpenRemoteConnect: Unable to connect to target space with arn: ${connectionIdentifier} error: ${err}` + ) + } +} + +export async function openRemoteConnect(node: SagemakerSpaceNode, ctx: vscode.ExtensionContext) { + getLogger().debug( + `sm:openRemoteConnect: node: ${node.toString()} appArn: ${await node.getAppArn()} region: ${node.regionCode}` + ) + + const spaceArn = (await node.getSpaceArn()) as string + const remoteEnv = await prepareDevEnvConnection(spaceArn, ctx, 'sm_lc') + + try { + await startVscodeRemote( + remoteEnv.SessionProcess, + remoteEnv.hostname, + '/home/sagemaker-user', + remoteEnv.vscPath, + 'sagemaker-user' + ) + } catch (err) { + getLogger().error( + `sm:OpenRemoteConnect: Unable to connect to target space with arn: ${await node.getAppArn()} error: ${err}` + ) + } +} diff --git a/packages/core/src/awsService/sagemaker/credentialMapping.ts b/packages/core/src/awsService/sagemaker/credentialMapping.ts new file mode 100644 index 00000000000..205fc5fbad4 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/credentialMapping.ts @@ -0,0 +1,170 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as path from 'path' +import * as os from 'os' +import { fs } from '../../shared/fs/fs' +import globals from '../../shared/extensionGlobals' +import { ToolkitError } from '../../shared/errors' +import { DevSettings } from '../../shared/settings' +import { Auth } from '../../auth/auth' +import { parseRegionFromArn } from './utils' +import { SpaceMappings, SsmConnectionInfo } from './types' +import { getLogger } from '../../shared/logger/logger' + +const mappingFileName = '.sagemaker-space-profiles' +const mappingFilePath = path.join(os.homedir(), '.aws', mappingFileName) + +export async function loadMappings(): Promise { + try { + if (!(await fs.existsFile(mappingFilePath))) { + return {} + } + + const raw = await fs.readFileText(mappingFilePath) + return raw ? JSON.parse(raw) : {} + } catch (error) { + getLogger().error(`Failed to load space mappings from ${mappingFilePath}:`, error) + return {} + } +} + +export async function saveMappings(data: SpaceMappings): Promise { + try { + await fs.writeFile(mappingFilePath, JSON.stringify(data, undefined, 2), { + mode: 0o600, + atomic: true, + }) + } catch (error) { + getLogger().error(`Failed to save space mappings to ${mappingFilePath}:`, error) + } +} + +/** + * Persists the current profile to the appropriate space mapping based on connection type and profile format. + * @param appArn - The identifier for the SageMaker space. + */ +export async function persistLocalCredentials(appArn: string): Promise { + const currentProfileId = Auth.instance.getCurrentProfileId() + if (!currentProfileId) { + throw new ToolkitError('No current profile ID available for saving space credentials.') + } + + if (currentProfileId.startsWith('sso:')) { + const credentials = globals.loginManager.store.credentialsCache[currentProfileId] + await setSpaceSsoProfile( + appArn, + credentials.credentials.accessKeyId, + credentials.credentials.secretAccessKey, + credentials.credentials.sessionToken ?? '' + ) + } else { + await setSpaceIamProfile(appArn, currentProfileId) + } +} + +/** + * Persists deep link credentials for a SageMaker space using a derived refresh URL based on environment. + * + * @param appArn - ARN of the SageMaker space. + * @param domain - The domain ID associated with the space. + * @param session - SSM session ID. + * @param wsUrl - SSM WebSocket URL. + * @param token - Bearer token for the session. + */ +export async function persistSSMConnection( + appArn: string, + domain: string, + session?: string, + wsUrl?: string, + token?: string +): Promise { + const region = parseRegionFromArn(appArn) + const endpoint = DevSettings.instance.get('endpoints', {})['sagemaker'] ?? '' + + let envSubdomain: string + + if (endpoint.includes('beta')) { + envSubdomain = 'devo' + } else if (endpoint.includes('gamma')) { + envSubdomain = 'loadtest' + } else { + envSubdomain = 'studio' + } + + // Use the standard AWS domain for 'studio' (prod). + // For non-prod environments, use the obfuscated domain 'asfiovnxocqpcry.com'. + const baseDomain = + envSubdomain === 'studio' + ? `studio.${region}.sagemaker.aws` + : `${envSubdomain}.studio.${region}.asfiovnxocqpcry.com` + + const refreshUrl = `https://studio-${domain}.${baseDomain}/api/remoteaccess/token` + + await setSpaceCredentials(appArn, refreshUrl, { + sessionId: session ?? '-', + url: wsUrl ?? '-', + token: token ?? '-', + }) +} + +/** + * Sets or updates an IAM credential profile for a given space. + * @param spaceName - The name of the SageMaker space. + * @param profileName - The local AWS profile name to associate. + */ +export async function setSpaceIamProfile(spaceName: string, profileName: string): Promise { + const data = await loadMappings() + data.localCredential ??= {} + data.localCredential[spaceName] = { type: 'iam', profileName } + await saveMappings(data) +} + +/** + * Sets or updates an SSO credential profile for a given space. + * @param spaceName - The name of the SageMaker space. + * @param accessKey - Temporary access key from SSO. + * @param secret - Temporary secret key from SSO. + * @param token - Session token from SSO. + */ +export async function setSpaceSsoProfile( + spaceName: string, + accessKey: string, + secret: string, + token: string +): Promise { + const data = await loadMappings() + data.localCredential ??= {} + data.localCredential[spaceName] = { type: 'sso', accessKey, secret, token } + await saveMappings(data) +} + +/** + * Stores SSM connection information for a given space, typically from a deep link session. + * This initializes the request as 'fresh' and includes a refresh URL if provided. + * @param spaceName - The name of the SageMaker space. + * @param refreshUrl - URL to use for refreshing session tokens. + * @param credentials - The session information used to initiate the connection. + */ +export async function setSpaceCredentials( + spaceName: string, + refreshUrl: string, + credentials: SsmConnectionInfo +): Promise { + const data = await loadMappings() + data.deepLink ??= {} + + data.deepLink[spaceName] = { + refreshUrl, + requests: { + 'initial-connection': { + ...credentials, + status: 'fresh', + }, + }, + } + + await saveMappings(data) +} diff --git a/packages/core/src/awsService/sagemaker/detached-server/credentials.ts b/packages/core/src/awsService/sagemaker/detached-server/credentials.ts new file mode 100644 index 00000000000..5b2a7fdbc64 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/detached-server/credentials.ts @@ -0,0 +1,52 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { fromIni } from '@aws-sdk/credential-providers' +import { LocalCredentialProfile } from '../types' +import { readMapping } from './utils' + +/** + * Resolves AWS credentials for a given SageMaker space connection identifier + * using the 'lc' (local connection) credential mapping. + * + * Supported profile types: + * - 'iam': Looks up credentials from AWS config using profile name. + * - 'sso': Uses accessKey, secret, and sessionToken from the mapping file. + * + * @param connectionIdentifier - The ARN or space ID used to locate the profile in the mapping. + * @returns A Promise that resolves to AWS credentials compatible with AWS SDK v3. + * @throws If the profile is missing, malformed, or unsupported. + */ +export async function resolveCredentialsFor(connectionIdentifier: string): Promise { + const mapping = await readMapping() + const profile = mapping.localCredential?.[connectionIdentifier] as LocalCredentialProfile + + if (!profile) { + throw new Error(`No profile found for "${connectionIdentifier}"`) + } + + switch (profile.type) { + case 'iam': { + const name = profile.profileName?.split(':')[1] + if (!name) { + throw new Error(`Invalid IAM profile name for "${connectionIdentifier}"`) + } + return fromIni({ profile: name }) + } + case 'sso': { + const { accessKey, secret, token } = profile + if (!accessKey || !secret || !token) { + throw new Error(`Missing SSO credentials for "${connectionIdentifier}"`) + } + return { + accessKeyId: accessKey, + secretAccessKey: secret, + sessionToken: token, + } + } + default: + throw new Error(`Unsupported profile type "${profile}"`) + } +} diff --git a/packages/core/src/awsService/sagemaker/detached-server/routes/getSession.ts b/packages/core/src/awsService/sagemaker/detached-server/routes/getSession.ts new file mode 100644 index 00000000000..b8736ffe715 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/detached-server/routes/getSession.ts @@ -0,0 +1,50 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Disabled: detached server files cannot import vscode. +/* eslint-disable aws-toolkits/no-console-log */ +import { IncomingMessage, ServerResponse } from 'http' +import { startSagemakerSession, parseArn } from '../utils' +import { resolveCredentialsFor } from '../credentials' +import url from 'url' + +export async function handleGetSession(req: IncomingMessage, res: ServerResponse): Promise { + const parsedUrl = url.parse(req.url || '', true) + const connectionIdentifier = parsedUrl.query.connection_identifier as string + + if (!connectionIdentifier) { + res.writeHead(400, { 'Content-Type': 'text/plain' }) + res.end(`Missing required query parameter: "connection_identifier" (${connectionIdentifier})`) + return + } + + let credentials + try { + credentials = await resolveCredentialsFor(connectionIdentifier) + } catch (err) { + console.error('Failed to resolve credentials:', err) + res.writeHead(500, { 'Content-Type': 'text/plain' }) + res.end((err as Error).message) + } + + const { region } = parseArn(connectionIdentifier) + + try { + const session = await startSagemakerSession({ region, connectionIdentifier, credentials }) + res.writeHead(200, { 'Content-Type': 'application/json' }) + res.end( + JSON.stringify({ + SessionId: session.SessionId, + StreamUrl: session.StreamUrl, + TokenValue: session.TokenValue, + }) + ) + } catch (err) { + console.error(`Failed to start SageMaker session for ${connectionIdentifier}:`, err) + res.writeHead(500, { 'Content-Type': 'text/plain' }) + res.end('Failed to start SageMaker session') + return + } +} diff --git a/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts b/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts new file mode 100644 index 00000000000..e956f3d7019 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts @@ -0,0 +1,69 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Disabled: detached server files cannot import vscode. +/* eslint-disable aws-toolkits/no-console-log */ +import { IncomingMessage, ServerResponse } from 'http' +import url from 'url' +import { SessionStore } from '../sessionStore' +import { readServerInfo, open } from '../utils' + +export async function handleGetSessionAsync(req: IncomingMessage, res: ServerResponse): Promise { + const parsedUrl = url.parse(req.url || '', true) + const connectionIdentifier = parsedUrl.query.connection_identifier as string + const requestId = parsedUrl.query.request_id as string + + if (!connectionIdentifier || !requestId) { + res.writeHead(400, { 'Content-Type': 'text/plain' }) + res.end( + `Missing required query parameters: "connection_identifier" (${connectionIdentifier}), "request_id" (${requestId})` + ) + return + } + + const store = new SessionStore() + + try { + const freshEntry = await store.getFreshEntry(connectionIdentifier, requestId) + + if (freshEntry) { + res.writeHead(200, { 'Content-Type': 'application/json' }) + res.end( + JSON.stringify({ + SessionId: freshEntry.sessionId, + StreamUrl: freshEntry.url, + TokenValue: freshEntry.token, + }) + ) + return + } + + const status = await store.getStatus(connectionIdentifier, requestId) + if (status === 'pending') { + res.writeHead(204) + res.end() + return + } else if (status === 'not-started') { + const serverInfo = await readServerInfo() + const refreshUrl = await store.getRefreshUrl(connectionIdentifier) + + const url = `${refreshUrl}?connection_identifier=${encodeURIComponent( + connectionIdentifier + )}&request_id=${encodeURIComponent(requestId)}&call_back_url=${encodeURIComponent( + `http://localhost:${serverInfo.port}/refresh_token` + )}` + + await open(url) + res.writeHead(202, { 'Content-Type': 'text/plain' }) + res.end('Session is not ready yet. Please retry in a few seconds.') + await store.markPending(connectionIdentifier, requestId) + return + } + } catch (err) { + console.error('Error handling session async request:', err) + res.writeHead(500, { 'Content-Type': 'text/plain' }) + res.end('Unexpected error') + } +} diff --git a/packages/core/src/awsService/sagemaker/detached-server/routes/refreshToken.ts b/packages/core/src/awsService/sagemaker/detached-server/routes/refreshToken.ts new file mode 100644 index 00000000000..34152aa0423 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/detached-server/routes/refreshToken.ts @@ -0,0 +1,46 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Disabled: detached server files cannot import vscode. +/* eslint-disable aws-toolkits/no-console-log */ +import { IncomingMessage, ServerResponse } from 'http' +import url from 'url' +import { SessionStore } from '../sessionStore' + +export async function handleRefreshToken(req: IncomingMessage, res: ServerResponse): Promise { + const parsedUrl = url.parse(req.url || '', true) + const connectionIdentifier = parsedUrl.query.connection_identifier as string + const requestId = parsedUrl.query.request_id as string + const wsUrl = parsedUrl.query.ws_url as string + const token = parsedUrl.query.token as string + const sessionId = parsedUrl.query.session as string + + const store = new SessionStore() + + if (!connectionIdentifier || !requestId || !wsUrl || !token || !sessionId) { + res.writeHead(400, { 'Content-Type': 'text/plain' }) + res.end( + `Missing required parameters:\n` + + ` connection_identifier: ${connectionIdentifier ?? 'undefined'}\n` + + ` request_id: ${requestId ?? 'undefined'}\n` + + ` url: ${wsUrl ?? 'undefined'}\n` + + ` token: ${token ?? 'undefined'}\n` + + ` sessionId: ${sessionId ?? 'undefined'}` + ) + return + } + + try { + await store.setSession(connectionIdentifier, requestId, { sessionId, token, url: wsUrl }) + } catch (err) { + console.error('Failed to save session token:', err) + res.writeHead(500, { 'Content-Type': 'text/plain' }) + res.end('Failed to save session token') + return + } + + res.writeHead(200) + res.end('Session token refreshed successfully') +} diff --git a/packages/core/src/awsService/sagemaker/detached-server/server.ts b/packages/core/src/awsService/sagemaker/detached-server/server.ts new file mode 100644 index 00000000000..e785516146c --- /dev/null +++ b/packages/core/src/awsService/sagemaker/detached-server/server.ts @@ -0,0 +1,107 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Disabled: detached server files cannot import vscode. +/* eslint-disable aws-toolkits/no-console-log */ +/* eslint-disable no-restricted-imports */ +import http, { IncomingMessage, ServerResponse } from 'http' +import { handleGetSession } from './routes/getSession' +import { handleGetSessionAsync } from './routes/getSessionAsync' +import { handleRefreshToken } from './routes/refreshToken' +import url from 'url' +import * as os from 'os' +import fs from 'fs' +import { execFile } from 'child_process' + +const pollInterval = 30 * 60 * 100 // 30 minutes + +const server = http.createServer((req: IncomingMessage, res: ServerResponse) => { + const parsedUrl = url.parse(req.url || '', true) + + switch (parsedUrl.pathname) { + case '/get_session': + return handleGetSession(req, res) + case '/get_session_async': + return handleGetSessionAsync(req, res) + case '/refresh_token': + return handleRefreshToken(req, res) + default: + res.writeHead(404, { 'Content-Type': 'text/plain' }) + res.end(`Not Found: ${req.url}`) + } +}) + +server.listen(0, '127.0.0.1', async () => { + const address = server.address() + if (address && typeof address === 'object') { + const port = address.port + const pid = process.pid + + console.log(`Detached server listening on http://127.0.0.1:${port} (pid: ${pid})`) + + const filePath = process.env.SAGEMAKER_LOCAL_SERVER_FILE_PATH + if (!filePath) { + throw new Error('SAGEMAKER_LOCAL_SERVER_FILE_PATH environment variable is not set') + } + + const data = { pid, port } + console.log(`Writing local endpoint info to ${filePath}`) + + fs.writeFileSync(filePath, JSON.stringify(data, undefined, 2), 'utf-8') + } else { + console.error('Failed to retrieve assigned port') + process.exit(0) + } + await monitorVSCodeAndExit() +}) + +function checkVSCodeWindows(): Promise { + return new Promise((resolve) => { + const platform = os.platform() + + if (platform === 'win32') { + execFile('tasklist', ['/FI', 'IMAGENAME eq Code.exe'], (err, stdout) => { + if (err) { + resolve(false) + return + } + resolve(/Code\.exe/i.test(stdout)) + }) + } else if (platform === 'darwin') { + execFile('ps', ['aux'], (err, stdout) => { + if (err) { + resolve(false) + return + } + + const found = stdout + .split('\n') + .some((line) => /Visual Studio Code( - Insiders)?\.app\/Contents\/MacOS\/Electron/.test(line)) + resolve(found) + }) + } else { + execFile('ps', ['-A', '-o', 'comm'], (err, stdout) => { + if (err) { + resolve(false) + return + } + + const found = stdout.split('\n').some((line) => /^(code(-insiders)?|electron)$/i.test(line.trim())) + resolve(found) + }) + } + }) +} + +async function monitorVSCodeAndExit() { + while (true) { + const found = await checkVSCodeWindows() + if (!found) { + console.log('No VSCode windows found. Shutting down detached server.') + process.exit(0) + } + await new Promise((r) => setTimeout(r, pollInterval)) + } +} diff --git a/packages/core/src/awsService/sagemaker/detached-server/sessionStore.ts b/packages/core/src/awsService/sagemaker/detached-server/sessionStore.ts new file mode 100644 index 00000000000..312765de263 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/detached-server/sessionStore.ts @@ -0,0 +1,135 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SsmConnectionInfo } from '../types' +import { readMapping, writeMapping } from './utils' + +export type SessionStatus = 'pending' | 'fresh' | 'consumed' | 'not-started' + +export class SessionStore { + async getRefreshUrl(connectionId: string) { + const mapping = await readMapping() + + if (!mapping.deepLink) { + throw new Error('No deepLink mapping found') + } + + const entry = mapping.deepLink[connectionId] + if (!entry) { + throw new Error(`No mapping found for connectionId: "${connectionId}"`) + } + + if (!entry.refreshUrl) { + throw new Error(`No refreshUrl found for connectionId: "${connectionId}"`) + } + + return entry.refreshUrl + } + + async getFreshEntry(connectionId: string, requestId: string) { + const mapping = await readMapping() + + if (!mapping.deepLink) { + throw new Error('No deepLink mapping found') + } + + const entry = mapping.deepLink[connectionId] + if (!entry) { + throw new Error(`No mapping found for connectionId: "${connectionId}"`) + } + + const requests = entry.requests + const initialEntry = requests['initial-connection'] + if (initialEntry?.status === 'fresh') { + await this.markConsumed(connectionId, 'initial-connection') + return initialEntry + } + + const asyncEntry = requests[requestId] + if (asyncEntry?.status === 'fresh') { + await this.markConsumed(connectionId, requestId) + return asyncEntry + } + + return undefined + } + + async getStatus(connectionId: string, requestId: string) { + const mapping = await readMapping() + + if (!mapping.deepLink) { + throw new Error('No deepLink mapping found') + } + const entry = mapping.deepLink[connectionId] + if (!entry) { + throw new Error(`No mapping found for connectionId: "${connectionId}"`) + } + + const status = entry.requests?.[requestId]?.status + return status ?? 'not-started' + } + + async markConsumed(connectionId: string, requestId: string) { + const mapping = await readMapping() + + if (!mapping.deepLink) { + throw new Error('No deepLink mapping found') + } + const entry = mapping.deepLink[connectionId] + if (!entry) { + throw new Error(`No mapping found for connectionId: "${connectionId}"`) + } + + const requests = entry.requests + if (!requests[requestId]) { + throw new Error(`No request entry found for requestId: "${requestId}"`) + } + + requests[requestId].status = 'consumed' + await writeMapping(mapping) + } + + async markPending(connectionId: string, requestId: string) { + const mapping = await readMapping() + + if (!mapping.deepLink) { + throw new Error('No deepLink mapping found') + } + const entry = mapping.deepLink[connectionId] + if (!entry) { + throw new Error(`No mapping found for connectionId: "${connectionId}"`) + } + + entry.requests[requestId] = { + sessionId: '', + token: '', + url: '', + status: 'pending', + } + + await writeMapping(mapping) + } + + async setSession(connectionId: string, requestId: string, ssmConnectionInfo: SsmConnectionInfo) { + const mapping = await readMapping() + + if (!mapping.deepLink) { + throw new Error('No deepLink mapping found') + } + const entry = mapping.deepLink[connectionId] + if (!entry) { + throw new Error(`No mapping found for connectionId: "${connectionId}"`) + } + + entry.requests[requestId] = { + sessionId: ssmConnectionInfo.sessionId, + token: ssmConnectionInfo.token, + url: ssmConnectionInfo.url, + status: ssmConnectionInfo.status ?? 'fresh', + } + + await writeMapping(mapping) + } +} diff --git a/packages/core/src/awsService/sagemaker/detached-server/utils.ts b/packages/core/src/awsService/sagemaker/detached-server/utils.ts new file mode 100644 index 00000000000..50e80e536f3 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/detached-server/utils.ts @@ -0,0 +1,106 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Disabled: detached server files cannot import vscode. +/* eslint-disable aws-toolkits/no-console-log */ +/* eslint-disable no-restricted-imports */ +import { ServerInfo } from '../types' +import { promises as fs } from 'fs' +import { SageMakerClient, StartSessionCommand } from '@amzn/sagemaker-client' +import os from 'os' +import { join } from 'path' +import { SpaceMappings } from '../types' +import open from 'open' +export { open } + +export const mappingFilePath = join(os.homedir(), '.aws', '.sagemaker-space-profiles') +const tempFilePath = `${mappingFilePath}.tmp` + +/** + * Reads the local endpoint info file (default or via env) and returns pid & port. + * @throws Error if the file is missing, invalid JSON, or missing fields + */ +export async function readServerInfo(): Promise { + const filePath = process.env.SAGEMAKER_LOCAL_SERVER_FILE_PATH + if (!filePath) { + throw new Error('Environment variable SAGEMAKER_LOCAL_SERVER_FILE_PATH is not set') + } + + try { + const content = await fs.readFile(filePath, 'utf-8') + const data = JSON.parse(content) + if (typeof data.pid !== 'number' || typeof data.port !== 'number') { + throw new TypeError(`Invalid server info format in ${filePath}`) + } + return { pid: data.pid, port: data.port } + } catch (err: any) { + if (err.code === 'ENOENT') { + throw new Error(`Server info file not found at ${filePath}`) + } + throw new Error(`Failed to read server info: ${err.message ?? String(err)}`) + } +} + +/** + * Parses a SageMaker ARN to extract region and account ID. + * Supports formats like: + * arn:aws:sagemaker:::space/ + * or sm_lc_arn:aws:sagemaker:::space__d-xxxx__ + * + * If the input is prefixed with an identifier (e.g. "sagemaker-user@"), the function will strip it. + * + * @param arn - The full SageMaker ARN string + * @returns An object containing the region and accountId + * @throws If the ARN format is invalid + */ +export function parseArn(arn: string): { region: string; accountId: string } { + const cleanedArn = arn.includes('@') ? arn.split('@')[1] : arn + const regex = /^arn:aws:sagemaker:(?[^:]+):(?\d+):space[/:].+$/i + const match = cleanedArn.match(regex) + + if (!match?.groups) { + throw new Error(`Invalid SageMaker ARN format: "${arn}"`) + } + + return { + region: match.groups.region, + accountId: match.groups.account_id, + } +} + +export async function startSagemakerSession({ region, connectionIdentifier, credentials }: any) { + const endpoint = process.env.SAGEMAKER_ENDPOINT || `https://sagemaker.${region}.amazonaws.com` + const client = new SageMakerClient({ region, credentials, endpoint }) + const command = new StartSessionCommand({ ResourceIdentifier: connectionIdentifier }) + return client.send(command) +} + +/** + * Reads the mapping file and parses it as JSON. + * Throws if the file doesn't exist or is malformed. + */ +export async function readMapping() { + try { + const content = await fs.readFile(mappingFilePath, 'utf-8') + console.log(`Mapping file path: ${mappingFilePath}`) + console.log(`Conents: ${content}`) + return JSON.parse(content) + } catch (err) { + throw new Error(`Failed to read mapping file: ${err instanceof Error ? err.message : String(err)}`) + } +} + +/** + * Writes the mapping to a temp file and atomically renames it to the target path. + */ +export async function writeMapping(mapping: SpaceMappings) { + try { + const json = JSON.stringify(mapping, undefined, 2) + await fs.writeFile(tempFilePath, json) + await fs.rename(tempFilePath, mappingFilePath) + } catch (err) { + throw new Error(`Failed to write mapping file: ${err instanceof Error ? err.message : String(err)}`) + } +} diff --git a/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts b/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts index 902cd68fedc..a1916bc480d 100644 --- a/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts +++ b/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts @@ -37,7 +37,7 @@ export class SagemakerParentNode extends AWSTreeNodeBase { public override readonly regionCode: string, protected readonly sagemakerClient: SagemakerClient ) { - super('Sagemaker', vscode.TreeItemCollapsibleState.Collapsed) + super('SageMaker', vscode.TreeItemCollapsibleState.Collapsed) this.sagemakerSpaceNodes = new Map() this.stsClient = new DefaultStsClient(regionCode) } diff --git a/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts b/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts index e10ee03a83f..8970682af15 100644 --- a/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts +++ b/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts @@ -21,7 +21,7 @@ export class SagemakerSpaceNode extends AWSTreeNodeBase implements AWSResourceNo ) { super('') this.updateSpace(spaceApp) - this.contextValue = 'awsSagemakerSpaceRunningNode' + this.contextValue = this.getContext() } public updateSpace(spaceApp: SagemakerSpaceApp) { @@ -50,6 +50,15 @@ export class SagemakerSpaceNode extends AWSTreeNodeBase implements AWSResourceNo return appDetails.AppArn } + public async getSpaceArn() { + const appDetails = await this.client.describeSpace({ + DomainId: this.spaceApp.DomainId, + SpaceName: this.spaceApp.SpaceName, + }) + + return appDetails.SpaceArn + } + private buildLabel(): string { const status = generateSpaceStatus(this.spaceApp.Status, this.spaceApp.App?.Status) return `${this.name} (${status})` @@ -76,4 +85,12 @@ export class SagemakerSpaceNode extends AWSTreeNodeBase implements AWSResourceNo return getIcon('aws-sagemaker-jupyter-lab') } } + + private getContext() { + const status = generateSpaceStatus(this.spaceApp.Status, this.spaceApp.App?.Status) + if (status === 'Running' && this.spaceApp.SpaceSettingsSummary?.RemoteAccess === 'ENABLED') { + return 'awsSagemakerSpaceRunningRemoteEnabledNode' + } + return 'awsSagemakerSpaceNode' + } } diff --git a/packages/core/src/awsService/sagemaker/model.ts b/packages/core/src/awsService/sagemaker/model.ts new file mode 100644 index 00000000000..6f2fb1adbea --- /dev/null +++ b/packages/core/src/awsService/sagemaker/model.ts @@ -0,0 +1,194 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Disabled: detached server files cannot import vscode. +/* eslint-disable no-restricted-imports */ +import * as vscode from 'vscode' +import { sshAgentSocketVariable, startSshAgent } from '../../shared/extensions/ssh' +import { createBoundProcess, ensureDependencies } from '../../shared/remoteSession' +import { SshConfig } from '../../shared/sshConfig' +import * as path from 'path' +import { persistLocalCredentials, persistSSMConnection } from './credentialMapping' +import * as os from 'os' +import _ from 'lodash' +import { fs } from '../../shared/fs/fs' +import * as nodefs from 'fs' +import { getSmSsmEnv, spawnDetachedServer } from './utils' +import { getLogger } from '../../shared/logger/logger' +import { DevSettings } from '../../shared/settings' +import { ToolkitError } from '../../shared/errors' + +const logger = getLogger('sagemaker') + +export async function prepareDevEnvConnection( + appArn: string, + ctx: vscode.ExtensionContext, + connectionType: string, + session?: string, + wsUrl?: string, + token?: string, + domain?: string +) { + const remoteLogger = configureRemoteConnectionLogger() + const { ssm, vsc, ssh } = (await ensureDependencies()).unwrap() + + // Check timeout setting for remote SSH connections + const remoteSshConfig = vscode.workspace.getConfiguration('remote.SSH') + const current = remoteSshConfig.get('connectTimeout') + if (typeof current === 'number' && current < 300) { + await remoteSshConfig.update('connectTimeout', 300, vscode.ConfigurationTarget.Global) + void vscode.window.showInformationMessage( + 'Updated "remote.SSH.connectTimeout" to 300 seconds to improve stability.' + ) + } + + const hostnamePrefix = connectionType + const hostname = `${hostnamePrefix}_${appArn.replace(/\//g, '__').replace(/:/g, '_._')}` + + // save space credential mapping + if (connectionType === 'sm_lc') { + await persistLocalCredentials(appArn) + } else if (connectionType === 'sm_dl') { + await persistSSMConnection(appArn, domain ?? '', session, wsUrl, token) + } + + await startLocalServer(ctx) + await removeKnownHost(hostname) + + const sshConfig = new SshConfig(ssh, 'sm_', 'sagemaker_connect') + const config = await sshConfig.ensureValid() + if (config.isErr()) { + const err = config.err() + logger.error(`sagemaker: failed to add ssh config section: ${err.message}`) + throw err + } + + // set envirionment variables + const vars = getSmSsmEnv(ssm, path.join(ctx.globalStorageUri.fsPath, 'sagemaker-local-server-info.json')) + logger.info(`connect script logs at ${vars.LOG_FILE_LOCATION}`) + + const envProvider = async () => { + return { [sshAgentSocketVariable]: await startSshAgent(), ...vars } + } + const SessionProcess = createBoundProcess(envProvider).extend({ + onStdout: remoteLogger, + onStderr: remoteLogger, + rejectOnErrorCode: true, + }) + + return { + hostname, + envProvider, + sshPath: ssh, + vscPath: vsc, + SessionProcess, + } +} + +export function configureRemoteConnectionLogger() { + const logPrefix = 'sagemaker:' + const logger = (data: string) => getLogger().info(`${logPrefix}: ${data}`) + return logger +} + +export async function startLocalServer(ctx: vscode.ExtensionContext) { + const storagePath = ctx.globalStorageUri.fsPath + const serverPath = ctx.asAbsolutePath(path.join('dist/src/awsService/sagemaker/detached-server/', 'server.js')) + const outLog = path.join(storagePath, 'sagemaker-local-server.out.log') + const errLog = path.join(storagePath, 'sagemaker-local-server.err.log') + const infoFilePath = path.join(storagePath, 'sagemaker-local-server-info.json') + + logger.info(`local server logs at ${storagePath}/sagemaker-local-server.*.log`) + + const customEndpoint = DevSettings.instance.get('endpoints', {})['sagemaker'] + + await stopLocalServer(ctx) + + const child = spawnDetachedServer(process.execPath, [serverPath], { + cwd: path.dirname(serverPath), + detached: true, + stdio: ['ignore', nodefs.openSync(outLog, 'a'), nodefs.openSync(errLog, 'a')], + env: { + ...process.env, + SAGEMAKER_ENDPOINT: customEndpoint, + SAGEMAKER_LOCAL_SERVER_FILE_PATH: infoFilePath, + }, + }) + + child.unref() +} + +interface LocalServerInfo { + pid: number + port: string +} + +export async function stopLocalServer(ctx: vscode.ExtensionContext): Promise { + const infoFilePath = path.join(ctx.globalStorageUri.fsPath, 'sagemaker-local-server-info.json') + + if (!(await fs.existsFile(infoFilePath))) { + logger.debug('no server info file found. nothing to stop.') + return + } + + let pid: number | undefined + try { + const content = await fs.readFileText(infoFilePath) + const infoJson = JSON.parse(content) as LocalServerInfo + pid = infoJson.pid + } catch (err: any) { + throw ToolkitError.chain(err, 'failed to parse server info file') + } + + if (typeof pid === 'number' && !isNaN(pid)) { + try { + process.kill(pid) + logger.debug(`stopped local server with PID ${pid}`) + } catch (err: any) { + if (err.code === 'ESRCH') { + logger.warn(`no process found with PID ${pid}. It may have already exited.`) + } else { + throw ToolkitError.chain(err, 'failed to stop local server') + } + } + } else { + logger.warn('no valid PID found in info file.') + } + + try { + await fs.delete(infoFilePath) + logger.debug('removed server info file.') + } catch (err: any) { + logger.warn(`could not delete info file: ${err.message ?? err}`) + } +} + +export async function removeKnownHost(hostname: string): Promise { + const knownHostsPath = path.join(os.homedir(), '.ssh', 'known_hosts') + + if (!(await fs.existsFile(knownHostsPath))) { + logger.warn(`known_hosts not found at ${knownHostsPath}`) + return + } + + let lines: string[] + try { + const content = await fs.readFileText(knownHostsPath) + lines = content.split('\n') + } catch (err: any) { + throw ToolkitError.chain(err, 'Failed to read known_hosts file') + } + + const updatedLines = lines.filter((line) => !line.split(' ')[0].split(',').includes(hostname)) + + if (updatedLines.length !== lines.length) { + try { + await fs.writeFile(knownHostsPath, updatedLines.join('\n'), { atomic: true }) + logger.debug(`Removed '${hostname}' from known_hosts`) + } catch (err: any) { + throw ToolkitError.chain(err, 'Failed to write updated known_hosts file') + } + } +} diff --git a/packages/core/src/awsService/sagemaker/types.ts b/packages/core/src/awsService/sagemaker/types.ts new file mode 100644 index 00000000000..9b06058ef62 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/types.ts @@ -0,0 +1,30 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +export interface SpaceMappings { + localCredential?: { [spaceName: string]: LocalCredentialProfile } + deepLink?: { [spaceName: string]: DeeplinkSession } +} + +export type LocalCredentialProfile = + | { type: 'iam'; profileName: string } + | { type: 'sso'; accessKey: string; secret: string; token: string } + +export interface DeeplinkSession { + requests: Record + refreshUrl?: string +} + +export interface SsmConnectionInfo { + sessionId: string + url: string + token: string + status?: 'fresh' | 'consumed' | 'pending' +} + +export interface ServerInfo { + pid: number + port: number +} diff --git a/packages/core/src/awsService/sagemaker/uriHandlers.ts b/packages/core/src/awsService/sagemaker/uriHandlers.ts new file mode 100644 index 00000000000..8ee91c03d88 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/uriHandlers.ts @@ -0,0 +1,37 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { SearchParams } from '../../shared/vscode/uriHandler' +import { deeplinkConnect } from './commands' +import { ExtContext } from '../../shared/extensions' + +export function register(ctx: ExtContext) { + async function connectHandler(params: ReturnType) { + await deeplinkConnect( + ctx, + params.connection_identifier, + params.session, + `${params.ws_url}&cell-number=${params['cell-number']}`, + params.token, + params.domain + ) + } + + return vscode.Disposable.from(ctx.uriHandler.onPath('/connect/sagemaker', connectHandler, parseConnectParams)) +} + +export function parseConnectParams(query: SearchParams) { + const params = query.getFromKeysOrThrow( + 'connection_identifier', + 'domain', + 'user_profile', + 'session', + 'ws_url', + 'cell-number', + 'token' + ) + return params +} diff --git a/packages/core/src/awsService/sagemaker/utils.ts b/packages/core/src/awsService/sagemaker/utils.ts index c7cd00c8b8f..bc0f30a05fa 100644 --- a/packages/core/src/awsService/sagemaker/utils.ts +++ b/packages/core/src/awsService/sagemaker/utils.ts @@ -3,8 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +import * as cp from 'child_process' // eslint-disable-line no-restricted-imports import { AppStatus, SpaceStatus } from '@aws-sdk/client-sagemaker' import { SagemakerSpaceApp } from '../../shared/clients/sagemaker' +import { sshLogFileLocation } from '../../shared/sshConfig' export const DomainKeyDelimiter = '__' @@ -76,3 +78,27 @@ export function getSpaceAppsForUserProfile(spaceApps: SagemakerSpaceApp[], userP return result }, [] as string[]) } + +export function getSmSsmEnv(ssmPath: string, sagemakerLocalServerPath: string): NodeJS.ProcessEnv { + return Object.assign( + { + AWS_SSM_CLI: ssmPath, + SAGEMAKER_LOCAL_SERVER_FILE_PATH: sagemakerLocalServerPath, + LOF_FILE_LOCATION: sshLogFileLocation('sagemaker', 'blah'), + }, + process.env + ) +} + +export function spawnDetachedServer(...args: Parameters) { + return cp.spawn(...args) +} + +export function parseRegionFromArn(arn: string): string { + const parts = arn.split(':') + if (parts.length < 4) { + throw new Error(`Invalid ARN: "${arn}"`) + } + + return parts[3] // region is the 4th part +} diff --git a/packages/core/src/shared/clients/sagemaker.ts b/packages/core/src/shared/clients/sagemaker.ts index a4ad9083e8c..5d820d5b777 100644 --- a/packages/core/src/shared/clients/sagemaker.ts +++ b/packages/core/src/shared/clients/sagemaker.ts @@ -12,6 +12,9 @@ import { DescribeDomainCommandInput, DescribeDomainCommandOutput, DescribeDomainResponse, + DescribeSpaceCommand, + DescribeSpaceCommandInput, + DescribeSpaceCommandOutput, ListAppsCommandInput, ListSpacesCommandInput, SageMakerClient, @@ -51,6 +54,9 @@ export class SagemakerClient extends ClientWrapper { public describeDomain(request: DescribeDomainCommandInput): Promise { return this.makeRequest(DescribeDomainCommand, request) } + public describeSpace(request: DescribeSpaceCommandInput): Promise { + return this.makeRequest(DescribeSpaceCommand, request) + } public async fetchSpaceAppsAndDomains(): Promise< [Map, Map] @@ -59,6 +65,7 @@ export class SagemakerClient extends ClientWrapper { const appMap: Map = await this.listApps() .flatten() .filter((app) => !!app.DomainId && !!app.SpaceName) + .filter((app) => app.AppType === 'JupyterLab' || app.AppType === 'CodeEditor') .toMap((app) => getDomainSpaceKey(app.DomainId || '', app.SpaceName || '')) const spaceApps: Map = await this.listSpaces() diff --git a/packages/core/src/shared/logger/logger.ts b/packages/core/src/shared/logger/logger.ts index eb2602c30b9..95c4c7af769 100644 --- a/packages/core/src/shared/logger/logger.ts +++ b/packages/core/src/shared/logger/logger.ts @@ -22,6 +22,7 @@ export type LogTopic = | 'resourceCache' | 'telemetry' | 'proxyUtil' + | 'sagemaker' class ErrorLog { constructor( diff --git a/packages/core/src/shared/sshConfig.ts b/packages/core/src/shared/sshConfig.ts index 2c60b423ab3..db20c173393 100644 --- a/packages/core/src/shared/sshConfig.ts +++ b/packages/core/src/shared/sshConfig.ts @@ -192,8 +192,21 @@ Host ${this.configHostName} ` } + private getSageMakerSSHConfig(proxyCommand: string): string { + return ` +# Created by AWS Toolkit for VSCode. https://github.com/aws/aws-toolkit-vscode +Host ${this.configHostName} + ForwardAgent yes + AddKeysToAgent yes + StrictHostKeyChecking accept-new + ProxyCommand ${proxyCommand} + ` + } + protected createSSHConfigSection(proxyCommand: string): string { - if (this.keyPath) { + if (this.scriptPrefix === 'sagemaker_connect') { + return `${this.getSageMakerSSHConfig(proxyCommand)}User '%r'\n` + } else if (this.keyPath) { return `${this.getBaseSSHConfig(proxyCommand)}IdentityFile '${this.keyPath}'\n User '%r'\n` } return this.getBaseSSHConfig(proxyCommand) diff --git a/packages/core/src/shared/vscode/uriHandler.ts b/packages/core/src/shared/vscode/uriHandler.ts index 24be2b26321..c8beda72fc4 100644 --- a/packages/core/src/shared/vscode/uriHandler.ts +++ b/packages/core/src/shared/vscode/uriHandler.ts @@ -46,7 +46,8 @@ export class UriHandler implements vscode.UriHandler { const { handler, parser } = this.handlers.get(uri.path)! let parsedQuery: Parameters[0] - const url = new URL(uri.toString(true)) + // Ensure '+' is treated as a literal plus sign, not a space, by encoding it as '%2B' + const url = new URL(uri.toString(true).replace(/\+/g, '%2B')) const params = new SearchParams(url.searchParams) try { diff --git a/packages/core/src/test/awsService/sagemaker/credentialMapping.test.ts b/packages/core/src/test/awsService/sagemaker/credentialMapping.test.ts new file mode 100644 index 00000000000..1d17651a042 --- /dev/null +++ b/packages/core/src/test/awsService/sagemaker/credentialMapping.test.ts @@ -0,0 +1,154 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as sinon from 'sinon' +import * as assert from 'assert' +import { persistLocalCredentials, persistSSMConnection } from '../../../awsService/sagemaker/credentialMapping' +import { Auth } from '../../../auth' +import { DevSettings, fs } from '../../../shared' +import globals from '../../../shared/extensionGlobals' + +describe('credentialMapping', () => { + describe('persistLocalCredentials', () => { + const appArn = 'arn:aws:sagemaker:us-west-2:123456789012:app/domain/space' + + let sandbox: sinon.SinonSandbox + + beforeEach(() => { + sandbox = sinon.createSandbox() + }) + + afterEach(() => { + sandbox.restore() + }) + + it('writes IAM profile to mappings', async () => { + sandbox.stub(Auth.instance, 'getCurrentProfileId').returns('profile:my-iam-profile') + sandbox.stub(fs, 'existsFile').resolves(false) // simulate no existing mapping file + const writeStub = sandbox.stub(fs, 'writeFile').resolves() + + await persistLocalCredentials(appArn) + + assert.ok(writeStub.calledOnce) + const raw = writeStub.firstCall.args[1] + const data = JSON.parse(typeof raw === 'string' ? raw : raw.toString()) + + assert.deepStrictEqual(data.localCredential?.[appArn], { + type: 'iam', + profileName: 'profile:my-iam-profile', + }) + }) + + it('writes SSO credentials to mappings', async () => { + sandbox.stub(Auth.instance, 'getCurrentProfileId').returns('sso:my-sso-profile') + sandbox.stub(globals.loginManager.store, 'credentialsCache').value({ + 'sso:my-sso-profile': { + credentials: { + accessKeyId: 'AKIA123', + secretAccessKey: 'SECRET', + sessionToken: 'TOKEN', + }, + }, + }) + sandbox.stub(fs, 'existsFile').resolves(false) + const writeStub = sandbox.stub(fs, 'writeFile').resolves() + + await persistLocalCredentials(appArn) + + assert.ok(writeStub.calledOnce) + const raw = writeStub.firstCall.args[1] + const data = JSON.parse(typeof raw === 'string' ? raw : raw.toString()) + assert.deepStrictEqual(data.localCredential?.[appArn], { + type: 'sso', + accessKey: 'AKIA123', + secret: 'SECRET', + token: 'TOKEN', + }) + }) + + it('throws if no current profile ID is available', async () => { + sandbox.stub(Auth.instance, 'getCurrentProfileId').returns(undefined) + + await assert.rejects(() => persistLocalCredentials(appArn), { + message: 'No current profile ID available for saving space credentials.', + }) + }) + }) + + describe('persistSSMConnection', () => { + const appArn = 'arn:aws:sagemaker:us-west-2:123456789012:app/domain/space' + const domain = 'my-domain' + let sandbox: sinon.SinonSandbox + + beforeEach(() => { + sandbox = sinon.createSandbox() + }) + + afterEach(() => { + sandbox.restore() + }) + + function assertRefreshUrlMatches(writtenUrl: string, expectedSubdomain: string) { + assert.ok( + writtenUrl.startsWith(`https://studio-${domain}.${expectedSubdomain}`), + `Expected refresh URL to start with https://studio-${domain}.${expectedSubdomain}, got ${writtenUrl}` + ) + } + + it('uses default (studio) endpoint if no custom endpoint is set', async () => { + sandbox.stub(DevSettings.instance, 'get').returns({}) + sandbox.stub(fs, 'existsFile').resolves(false) + const writeStub = sandbox.stub(fs, 'writeFile').resolves() + + await persistSSMConnection(appArn, domain) + + const raw = writeStub.firstCall.args[1] + const data = JSON.parse(typeof raw === 'string' ? raw : raw.toString()) + + assertRefreshUrlMatches(data.deepLink?.[appArn]?.refreshUrl, 'studio.us-west-2.sagemaker.aws') + assert.deepStrictEqual(data.deepLink?.[appArn]?.requests['initial-connection'], { + sessionId: '-', + url: '-', + token: '-', + status: 'fresh', + }) + }) + + it('uses devo subdomain for beta endpoint', async () => { + sandbox.stub(DevSettings.instance, 'get').returns({ sagemaker: 'https://beta.whatever' }) + sandbox.stub(fs, 'existsFile').resolves(false) + const writeStub = sandbox.stub(fs, 'writeFile').resolves() + + await persistSSMConnection(appArn, domain, 'sess', 'wss://ws', 'token') + + const raw = writeStub.firstCall.args[1] + const data = JSON.parse(typeof raw === 'string' ? raw : raw.toString()) + + assertRefreshUrlMatches(data.deepLink?.[appArn]?.refreshUrl, 'devo.studio.us-west-2.asfiovnxocqpcry.com') + assert.deepStrictEqual(data.deepLink?.[appArn]?.requests['initial-connection'], { + sessionId: 'sess', + url: 'wss://ws', + token: 'token', + status: 'fresh', + }) + }) + + it('uses loadtest subdomain for gamma endpoint', async () => { + sandbox.stub(DevSettings.instance, 'get').returns({ sagemaker: 'https://gamma.example' }) + sandbox.stub(fs, 'existsFile').resolves(false) + const writeStub = sandbox.stub(fs, 'writeFile').resolves() + + await persistSSMConnection(appArn, domain) + + const raw = writeStub.firstCall.args[1] + const data = JSON.parse(typeof raw === 'string' ? raw : raw.toString()) + + assertRefreshUrlMatches( + data.deepLink?.[appArn]?.refreshUrl, + 'loadtest.studio.us-west-2.asfiovnxocqpcry.com' + ) + }) + }) +}) diff --git a/packages/core/src/test/awsService/sagemaker/detached-server/credentials.test.ts b/packages/core/src/test/awsService/sagemaker/detached-server/credentials.test.ts new file mode 100644 index 00000000000..a979c2186d3 --- /dev/null +++ b/packages/core/src/test/awsService/sagemaker/detached-server/credentials.test.ts @@ -0,0 +1,89 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import * as sinon from 'sinon' +import * as utils from '../../../../awsService/sagemaker/detached-server/utils' +import { resolveCredentialsFor } from '../../../../awsService/sagemaker/detached-server/credentials' + +const connectionId = 'arn:aws:sagemaker:region:acct:space/name' + +describe('resolveCredentialsFor', () => { + afterEach(() => sinon.restore()) + + it('throws if no profile is found', async () => { + sinon.stub(utils, 'readMapping').resolves({ localCredential: {} }) + + await assert.rejects(() => resolveCredentialsFor(connectionId), { + message: `No profile found for "${connectionId}"`, + }) + }) + + it('throws if IAM profile name is malformed', async () => { + sinon.stub(utils, 'readMapping').resolves({ + localCredential: { + [connectionId]: { + type: 'iam', + profileName: 'dev-profile', // no colon + }, + }, + }) + + await assert.rejects(() => resolveCredentialsFor(connectionId), { + message: `Invalid IAM profile name for "${connectionId}"`, + }) + }) + + it('resolves SSO credentials correctly', async () => { + sinon.stub(utils, 'readMapping').resolves({ + localCredential: { + [connectionId]: { + type: 'sso', + accessKey: 'key', + secret: 'sec', + token: 'tok', + }, + }, + }) + + const creds = await resolveCredentialsFor(connectionId) + assert.deepStrictEqual(creds, { + accessKeyId: 'key', + secretAccessKey: 'sec', + sessionToken: 'tok', + }) + }) + + it('throws if SSO credentials are incomplete', async () => { + sinon.stub(utils, 'readMapping').resolves({ + localCredential: { + [connectionId]: { + type: 'sso', + accessKey: 'key', + secret: 'sec', + token: '', // token is required but intentionally left empty for this test + }, + }, + }) + + await assert.rejects(() => resolveCredentialsFor(connectionId), { + message: `Missing SSO credentials for "${connectionId}"`, + }) + }) + + it('throws for unsupported profile types', async () => { + sinon.stub(utils, 'readMapping').resolves({ + localCredential: { + [connectionId]: { + type: 'unknown', + } as any, + }, + }) + + await assert.rejects(() => resolveCredentialsFor(connectionId), { + message: /Unsupported profile type/, // don't hard-code full value since object might be serialized + }) + }) +}) diff --git a/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSession.test.ts b/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSession.test.ts new file mode 100644 index 00000000000..fe0fcb5542b --- /dev/null +++ b/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSession.test.ts @@ -0,0 +1,97 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as http from 'http' +import * as sinon from 'sinon' +import assert from 'assert' +import { handleGetSession } from '../../../../../awsService/sagemaker/detached-server/routes/getSession' +import * as credentials from '../../../../../awsService/sagemaker/detached-server/credentials' +import * as utils from '../../../../../awsService/sagemaker/detached-server/utils' + +describe('handleGetSession', () => { + let req: Partial + let res: Partial + let resWriteHead: sinon.SinonSpy + let resEnd: sinon.SinonSpy + + beforeEach(() => { + resWriteHead = sinon.spy() + resEnd = sinon.spy() + + res = { + writeHead: resWriteHead, + end: resEnd, + } + }) + + it('responds with 400 if connection_identifier is missing', async () => { + req = { url: '/session' } + await handleGetSession(req as http.IncomingMessage, res as http.ServerResponse) + + assert(resWriteHead.calledWith(400)) + assert(resEnd.calledWithMatch(/Missing required query parameter/)) + }) + + it('responds with 500 if resolveCredentialsFor throws', async () => { + req = { url: '/session?connection_identifier=arn:aws:sagemaker:region:acc:space/domain/name' } + sinon.stub(credentials, 'resolveCredentialsFor').rejects(new Error('creds error')) + sinon.stub(utils, 'parseArn').returns({ + region: 'us-west-2', + accountId: '123456789012', + }) + + await handleGetSession(req as http.IncomingMessage, res as http.ServerResponse) + + assert(resWriteHead.calledWith(500)) + assert(resEnd.calledWith('creds error')) + }) + + it('responds with 500 if startSagemakerSession throws', async () => { + req = { url: '/session?connection_identifier=arn:aws:sagemaker:region:acc:space/domain/name' } + sinon.stub(credentials, 'resolveCredentialsFor').resolves({}) + sinon.stub(utils, 'parseArn').returns({ + region: 'us-west-2', + accountId: '123456789012', + }) + sinon.stub(utils, 'startSagemakerSession').rejects(new Error('session error')) + + await handleGetSession(req as http.IncomingMessage, res as http.ServerResponse) + + assert(resWriteHead.calledWith(500)) + assert(resEnd.calledWith('Failed to start SageMaker session')) + }) + + it('responds with 200 and session data on success', async () => { + req = { url: '/session?connection_identifier=arn:aws:sagemaker:region:acc:space/domain/name' } + sinon.stub(credentials, 'resolveCredentialsFor').resolves({}) + sinon.stub(utils, 'parseArn').returns({ + region: 'us-west-2', + accountId: '123456789012', + }) + sinon.stub(utils, 'startSagemakerSession').resolves({ + SessionId: 'abc123', + StreamUrl: 'https://stream', + TokenValue: 'token123', + $metadata: { httpStatusCode: 200 }, + }) + + await handleGetSession(req as http.IncomingMessage, res as http.ServerResponse) + + assert(resWriteHead.calledWith(200)) + assert( + resEnd.calledWithMatch( + JSON.stringify({ + SessionId: 'abc123', + StreamUrl: 'https://stream', + TokenValue: 'token123', + }) + ) + ) + }) + + afterEach(() => { + sinon.restore() + }) +}) diff --git a/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSessionAsync.test.ts b/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSessionAsync.test.ts new file mode 100644 index 00000000000..0de04217ef8 --- /dev/null +++ b/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSessionAsync.test.ts @@ -0,0 +1,96 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as http from 'http' +import * as sinon from 'sinon' +import assert from 'assert' +import { SessionStore } from '../../../../../awsService/sagemaker/detached-server/sessionStore' +import { handleGetSessionAsync } from '../../../../../awsService/sagemaker/detached-server/routes/getSessionAsync' +import * as utils from '../../../../../awsService/sagemaker/detached-server/utils' + +describe('handleGetSessionAsync', () => { + let req: Partial + let res: Partial + let resWriteHead: sinon.SinonSpy + let resEnd: sinon.SinonSpy + let storeStub: sinon.SinonStubbedInstance + + beforeEach(() => { + resWriteHead = sinon.spy() + resEnd = sinon.spy() + res = { writeHead: resWriteHead, end: resEnd } + + storeStub = sinon.createStubInstance(SessionStore) + sinon.stub(SessionStore.prototype, 'getFreshEntry').callsFake(storeStub.getFreshEntry) + sinon.stub(SessionStore.prototype, 'getStatus').callsFake(storeStub.getStatus) + sinon.stub(SessionStore.prototype, 'getRefreshUrl').callsFake(storeStub.getRefreshUrl) + sinon.stub(SessionStore.prototype, 'markPending').callsFake(storeStub.markPending) + }) + + it('responds with 400 if required query parameters are missing', async () => { + req = { url: '/session_async?connection_identifier=abc' } // missing request_id + await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) + + assert(resWriteHead.calledWith(400)) + assert(resEnd.calledWithMatch(/Missing required query parameters/)) + }) + + it('responds with 200 and session data if freshEntry exists', async () => { + req = { url: '/session_async?connection_identifier=abc&request_id=req123' } + storeStub.getFreshEntry.returns(Promise.resolve({ sessionId: 'sid', token: 'tok', url: 'wss://test' })) + + await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) + + assert(resWriteHead.calledWith(200)) + const actualJson = JSON.parse(resEnd.firstCall.args[0]) + assert.deepStrictEqual(actualJson, { + SessionId: 'sid', + TokenValue: 'tok', + StreamUrl: 'wss://test', + }) + }) + + it('responds with 204 if session is pending', async () => { + req = { url: '/session_async?connection_identifier=abc&request_id=req123' } + storeStub.getFreshEntry.returns(Promise.resolve(undefined)) + storeStub.getStatus.returns(Promise.resolve('pending')) + + await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) + + assert(resWriteHead.calledWith(204)) + assert(resEnd.calledOnce) + }) + + it('responds with 202 if status is not-started and opens browser', async () => { + req = { url: '/session_async?connection_identifier=abc&request_id=req123' } + + storeStub.getFreshEntry.returns(Promise.resolve(undefined)) + storeStub.getStatus.returns(Promise.resolve('not-started')) + storeStub.getRefreshUrl.returns(Promise.resolve('https://example.com/refresh')) + storeStub.markPending.returns(Promise.resolve()) + + sinon.stub(utils, 'readServerInfo').resolves({ pid: 1234, port: 4567 }) + sinon.stub(utils, 'open').resolves() + await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) + + assert(resWriteHead.calledWith(202)) + assert(resEnd.calledWithMatch(/Session is not ready yet/)) + assert(storeStub.markPending.calledWith('abc', 'req123')) + }) + + it('responds with 500 if unexpected error occurs', async () => { + req = { url: '/session_async?connection_identifier=abc&request_id=req123' } + storeStub.getFreshEntry.throws(new Error('fail')) + + await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) + + assert(resWriteHead.calledWith(500)) + assert(resEnd.calledWith('Unexpected error')) + }) + + afterEach(() => { + sinon.restore() + }) +}) diff --git a/packages/core/src/test/awsService/sagemaker/detached-server/routes/refreshToken.test.ts b/packages/core/src/test/awsService/sagemaker/detached-server/routes/refreshToken.test.ts new file mode 100644 index 00000000000..2fe6b3c648d --- /dev/null +++ b/packages/core/src/test/awsService/sagemaker/detached-server/routes/refreshToken.test.ts @@ -0,0 +1,74 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as http from 'http' +import * as sinon from 'sinon' +import assert from 'assert' +import { SessionStore } from '../../../../../awsService/sagemaker/detached-server/sessionStore' +import { handleRefreshToken } from '../../../../../awsService/sagemaker/detached-server/routes/refreshToken' + +describe('handleRefreshToken', () => { + let req: Partial + let res: Partial + let resWriteHead: sinon.SinonSpy + let resEnd: sinon.SinonSpy + let storeStub: sinon.SinonStubbedInstance + + beforeEach(() => { + resWriteHead = sinon.spy() + resEnd = sinon.spy() + + res = { + writeHead: resWriteHead, + end: resEnd, + } + + storeStub = sinon.createStubInstance(SessionStore) + sinon.stub(SessionStore.prototype, 'setSession').callsFake(storeStub.setSession) + }) + + it('responds with 400 if any required query parameter is missing', async () => { + req = { url: '/refresh?connection_identifier=abc&request_id=req123' } // missing others + + await handleRefreshToken(req as http.IncomingMessage, res as http.ServerResponse) + + assert(resWriteHead.calledWith(400)) + assert(resEnd.calledWithMatch(/Missing required parameters/)) + }) + + it('responds with 500 if setSession throws', async () => { + req = { + url: '/refresh?connection_identifier=abc&request_id=req123&ws_url=wss://abc&token=tok123&session=sess123', + } + storeStub.setSession.throws(new Error('store error')) + + await handleRefreshToken(req as http.IncomingMessage, res as http.ServerResponse) + + assert(resWriteHead.calledWith(500)) + assert(resEnd.calledWith('Failed to save session token')) + }) + + it('responds with 200 if session is saved successfully', async () => { + req = { + url: '/refresh?connection_identifier=abc&request_id=req123&ws_url=wss://abc&token=tok123&session=sess123', + } + + await handleRefreshToken(req as http.IncomingMessage, res as http.ServerResponse) + + assert(resWriteHead.calledWith(200)) + assert(resEnd.calledWith('Session token refreshed successfully')) + assert( + storeStub.setSession.calledWith('abc', 'req123', { + sessionId: 'sess123', + token: 'tok123', + url: 'wss://abc', + }) + ) + }) + + afterEach(() => { + sinon.restore() + }) +}) diff --git a/packages/core/src/test/awsService/sagemaker/detached-server/sessionStore.test.ts b/packages/core/src/test/awsService/sagemaker/detached-server/sessionStore.test.ts new file mode 100644 index 00000000000..0bb46b7d24b --- /dev/null +++ b/packages/core/src/test/awsService/sagemaker/detached-server/sessionStore.test.ts @@ -0,0 +1,141 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as sinon from 'sinon' +import assert from 'assert' +import * as utils from '../../../../awsService/sagemaker/detached-server/utils' +import { SessionStore } from '../../../../awsService/sagemaker/detached-server/sessionStore' +import { SsmConnectionInfo } from '../../../../awsService/sagemaker/types' + +describe('SessionStore', () => { + let readMappingStub: sinon.SinonStub + let writeMappingStub: sinon.SinonStub + const connectionId = 'abc' + const requestId = 'req123' + + const baseMapping = { + deepLink: { + [connectionId]: { + refreshUrl: 'https://refresh.url', + requests: { + [requestId]: { sessionId: 's1', token: 't1', url: 'u1', status: 'fresh' }, + 'initial-connection': { sessionId: 's0', token: 't0', url: 'u0', status: 'fresh' }, + }, + }, + }, + } + + beforeEach(() => { + readMappingStub = sinon.stub(utils, 'readMapping').returns(JSON.parse(JSON.stringify(baseMapping))) + writeMappingStub = sinon.stub(utils, 'writeMapping') + }) + + afterEach(() => sinon.restore()) + + it('gets refreshUrl', async () => { + const store = new SessionStore() + const result = await store.getRefreshUrl(connectionId) + assert.strictEqual(result, 'https://refresh.url') + }) + + it('throws if no mapping exists for connectionId', async () => { + const store = new SessionStore() + readMappingStub.returns({ deepLink: {} }) + + await assert.rejects(() => store.getRefreshUrl('missing'), /No mapping found/) + }) + + it('returns fresh entry and marks consumed', async () => { + const store = new SessionStore() + const result = await store.getFreshEntry(connectionId, requestId) + assert.deepStrictEqual(result, { + sessionId: 's0', + token: 't0', + url: 'u0', + status: 'consumed', + }) + assert(writeMappingStub.calledOnce) + }) + + it('returns async fresh entry and marks consumed', async () => { + const store = new SessionStore() + // Disable initial-connection freshness + readMappingStub.returns({ + deepLink: { + [connectionId]: { + refreshUrl: 'url', + requests: { + 'initial-connection': { status: 'consumed' }, + [requestId]: { sessionId: 'a', token: 'b', url: 'c', status: 'fresh' }, + }, + }, + }, + }) + const result = await store.getFreshEntry(connectionId, requestId) + assert.ok(result, 'Expected result to be defined') + assert.strictEqual(result.sessionId, 'a') + assert(writeMappingStub.calledOnce) + }) + + it('returns undefined if no fresh entries exist', async () => { + const store = new SessionStore() + readMappingStub.returns({ + deepLink: { + [connectionId]: { + refreshUrl: 'url', + requests: { + 'initial-connection': { status: 'consumed' }, + [requestId]: { status: 'pending' }, + }, + }, + }, + }) + const result = await store.getFreshEntry(connectionId, requestId) + assert.strictEqual(result, undefined) + }) + + it('gets status of known entry', async () => { + const store = new SessionStore() + const result = await store.getStatus(connectionId, requestId) + assert.strictEqual(result, 'fresh') + }) + + it('returns not-started if request not found', async () => { + const store = new SessionStore() + const result = await store.getStatus(connectionId, 'unknown') + assert.strictEqual(result, 'not-started') + }) + + it('marks entry as consumed', async () => { + const store = new SessionStore() + await store.markConsumed(connectionId, requestId) + const updated = writeMappingStub.firstCall.args[0] + assert.strictEqual(updated.deepLink[connectionId].requests[requestId].status, 'consumed') + }) + + it('marks request as pending', async () => { + const store = new SessionStore() + await store.markPending(connectionId, 'newReq') + const updated = writeMappingStub.firstCall.args[0] + assert.strictEqual(updated.deepLink[connectionId].requests['newReq'].status, 'pending') + }) + + it('sets session entry with default fresh status', async () => { + const store = new SessionStore() + const info: SsmConnectionInfo = { + sessionId: 's99', + token: 't99', + url: 'u99', + } + await store.setSession(connectionId, 'r99', info) + const written = writeMappingStub.firstCall.args[0] + assert.deepStrictEqual(written.deepLink[connectionId].requests['r99'], { + sessionId: 's99', + token: 't99', + url: 'u99', + status: 'fresh', + }) + }) +}) diff --git a/packages/core/src/test/awsService/sagemaker/detached-server/utils.test.ts b/packages/core/src/test/awsService/sagemaker/detached-server/utils.test.ts new file mode 100644 index 00000000000..66a47747bf9 --- /dev/null +++ b/packages/core/src/test/awsService/sagemaker/detached-server/utils.test.ts @@ -0,0 +1,46 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as assert from 'assert' +import { parseArn } from '../../../../awsService/sagemaker/detached-server/utils' + +describe('parseArn', () => { + it('parses a standard SageMaker ARN with forward slash', () => { + const arn = 'arn:aws:sagemaker:us-west-2:123456789012:space/my-space-name' + const result = parseArn(arn) + assert.deepStrictEqual(result, { + region: 'us-west-2', + accountId: '123456789012', + }) + }) + + it('parses a standard SageMaker ARN with colon', () => { + const arn = 'arn:aws:sagemaker:eu-central-1:123456789012:space:space-name' + const result = parseArn(arn) + assert.deepStrictEqual(result, { + region: 'eu-central-1', + accountId: '123456789012', + }) + }) + + it('parses an ARN prefixed with sagemaker-user@', () => { + const arn = 'sagemaker-user@arn:aws:sagemaker:ap-southeast-1:123456789012:space/foo' + const result = parseArn(arn) + assert.deepStrictEqual(result, { + region: 'ap-southeast-1', + accountId: '123456789012', + }) + }) + + it('throws on malformed ARN', () => { + const invalidArn = 'arn:aws:invalid:format' + assert.throws(() => parseArn(invalidArn), /Invalid SageMaker ARN format/) + }) + + it('throws when missing region/account', () => { + const invalidArn = 'arn:aws:sagemaker:::space/xyz' + assert.throws(() => parseArn(invalidArn), /Invalid SageMaker ARN format/) + }) +}) diff --git a/packages/core/src/test/awsService/sagemaker/model.test.ts b/packages/core/src/test/awsService/sagemaker/model.test.ts new file mode 100644 index 00000000000..09b287ad964 --- /dev/null +++ b/packages/core/src/test/awsService/sagemaker/model.test.ts @@ -0,0 +1,205 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as sinon from 'sinon' +import * as os from 'os' +import * as path from 'path' +import { DevSettings, fs, ToolkitError } from '../../../shared' +import { removeKnownHost, startLocalServer, stopLocalServer } from '../../../awsService/sagemaker/model' +import { assertLogsContain } from '../../globalSetup.test' +import assert from 'assert' + +describe('SageMaker Model', () => { + describe('startLocalServer', function () { + const ctx = { + globalStorageUri: vscode.Uri.file(path.join(os.tmpdir(), 'test-storage')), + extensionPath: path.join(os.tmpdir(), 'extension'), + asAbsolutePath: (relPath: string) => path.join(path.join(os.tmpdir(), 'extension'), relPath), + } as vscode.ExtensionContext + + let sandbox: sinon.SinonSandbox + + beforeEach(() => { + sandbox = sinon.createSandbox() + }) + + afterEach(() => { + sandbox.restore() + }) + + it('calls stopLocalServer and spawns child process with correct args', async function () { + const storagePath = ctx.globalStorageUri.fsPath + const serverPath = path.join( + ctx.extensionPath, + 'dist', + 'src', + 'awsService', + 'sagemaker', + 'detached-server', + 'server.js' + ) + const infoFilePath = path.join(storagePath, 'sagemaker-local-server-info.json') + + // Stubs + const stopLocalServerStub = sandbox.stub().resolves() + sandbox.replace(require('../../../awsService/sagemaker/model'), 'stopLocalServer', stopLocalServerStub) + sandbox.stub(DevSettings.instance, 'get').returns({ sagemaker: 'https://endpoint' }) + sandbox.stub(require('fs'), 'openSync').returns(42) + + const unrefStub = sandbox.stub() + const spawnStub = sandbox.stub().returns({ unref: unrefStub }) + sandbox.replace(require('../../../awsService/sagemaker/utils'), 'spawnDetachedServer', spawnStub) + + await startLocalServer(ctx) + + // Validate spawn args + sinon.assert.calledOnce(spawnStub) + const spawnArgs = spawnStub.firstCall.args + const expectedEnv = spawnArgs[2].env + + assert.strictEqual(spawnArgs[0], process.execPath) + assert.deepStrictEqual(spawnArgs[1], [serverPath]) + assert.strictEqual(spawnArgs[2].cwd, path.dirname(serverPath)) + assert.strictEqual(spawnArgs[2].detached, true) + assert.strictEqual(expectedEnv.SAGEMAKER_ENDPOINT, 'https://endpoint') + assert.strictEqual(expectedEnv.SAGEMAKER_LOCAL_SERVER_FILE_PATH, infoFilePath) + + sinon.assert.calledOnce(unrefStub) + assertLogsContain('local server logs at', false, 'info') + }) + }) + + describe('stopLocalServer', function () { + const ctx = { + globalStorageUri: vscode.Uri.file(path.join(os.tmpdir(), 'test-storage')), + } as vscode.ExtensionContext + + const infoFilePath = path.join(ctx.globalStorageUri.fsPath, 'sagemaker-local-server-info.json') + const validPid = 12345 + const validJson = JSON.stringify({ pid: validPid }) + let sandbox: sinon.SinonSandbox + + beforeEach(() => { + sandbox = sinon.createSandbox() + }) + + afterEach(() => { + sandbox.restore() + }) + + it('logs debug when successfully stops server and deletes file', async function () { + sandbox.stub(fs, 'existsFile').resolves(true) + sandbox.stub(fs, 'readFileText').resolves(validJson) + const killStub = sandbox.stub(process, 'kill').returns(true) + const deleteStub = sandbox.stub(fs, 'delete').resolves() + + await stopLocalServer(ctx) + + sinon.assert.calledWith(killStub, validPid) + sinon.assert.calledWith(deleteStub, infoFilePath) + assertLogsContain(`stopped local server with PID ${validPid}`, false, 'debug') + assertLogsContain('removed server info file.', false, 'debug') + }) + + it('throws ToolkitError when info file is invalid JSON', async function () { + sandbox.stub(fs, 'existsFile').resolves(true) + sandbox.stub(fs, 'readFileText').resolves('invalid json') + + try { + await stopLocalServer(ctx) + assert.ok(false, 'Expected error not thrown') + } catch (err) { + assert.ok(err instanceof ToolkitError) + assert.strictEqual(err.message, 'failed to parse server info file') + } + }) + + it('throws ToolkitError when killing process fails for another reason', async function () { + sandbox.stub(fs, 'existsFile').resolves(true) + sandbox.stub(fs, 'readFileText').resolves(validJson) + sandbox.stub(fs, 'delete').resolves() + sandbox.stub(process, 'kill').throws({ code: 'EPERM', message: 'permission denied' }) + + try { + await stopLocalServer(ctx) + assert.ok(false) + } catch (err) { + assert.ok(err instanceof ToolkitError) + assert.strictEqual(err.message, 'failed to stop local server') + } + }) + }) + + describe('removeKnownHost', function () { + const knownHostsPath = path.join(os.homedir(), '.ssh', 'known_hosts') + const hostname = 'test.host.com' + let sandbox: sinon.SinonSandbox + + beforeEach(function () { + sandbox = sinon.createSandbox() + }) + + afterEach(function () { + sandbox.restore() + }) + + it('removes line with hostname and writes updated file', async function () { + sandbox.stub(fs, 'existsFile').resolves(true) + + const inputContent = `${hostname} ssh-rsa AAAA\nsome.other.com ssh-rsa BBBB` + const expectedOutput = `some.other.com ssh-rsa BBBB` + + sandbox.stub(fs, 'readFileText').resolves(inputContent) + + const writeStub = sandbox.stub(fs, 'writeFile').resolves() + await removeKnownHost(hostname) + + sinon.assert.calledWith( + writeStub, + knownHostsPath, + sinon.match((value: string) => value.trim() === expectedOutput), + { atomic: true } + ) + }) + + it('logs warning when known_hosts does not exist', async function () { + sandbox.stub(fs, 'existsFile').resolves(false) + + await removeKnownHost('test.host.com') + + assertLogsContain(`known_hosts not found at`, false, 'warn') + }) + + it('throws ToolkitError when reading known_hosts fails', async function () { + sandbox.stub(fs, 'existsFile').resolves(true) + sandbox.stub(fs, 'readFileText').rejects(new Error('read failed')) + + try { + await removeKnownHost(hostname) + assert.ok(false, 'Expected error was not thrown') + } catch (err) { + assert.ok(err instanceof ToolkitError) + assert.strictEqual(err.message, 'Failed to read known_hosts file') + assert.strictEqual((err as ToolkitError).cause?.message, 'read failed') + } + }) + + it('throws ToolkitError when writing known_hosts fails', async function () { + sandbox.stub(fs, 'existsFile').resolves(true) + sandbox.stub(fs, 'readFileText').resolves(`${hostname} ssh-rsa key\nsomehost ssh-rsa key`) + sandbox.stub(fs, 'writeFile').rejects(new Error('write failed')) + + try { + await removeKnownHost(hostname) + assert.ok(false, 'Expected error was not thrown') + } catch (err) { + assert.ok(err instanceof ToolkitError) + assert.strictEqual(err.message, 'Failed to write updated known_hosts file') + assert.strictEqual((err as ToolkitError).cause?.message, 'write failed') + } + }) + }) +}) diff --git a/packages/core/src/test/awsService/sagemaker/uriHandlers.test.ts b/packages/core/src/test/awsService/sagemaker/uriHandlers.test.ts new file mode 100644 index 00000000000..9ff24b2a3f9 --- /dev/null +++ b/packages/core/src/test/awsService/sagemaker/uriHandlers.test.ts @@ -0,0 +1,59 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as sinon from 'sinon' +import * as vscode from 'vscode' +import assert from 'assert' +import { UriHandler } from '../../../shared/vscode/uriHandler' +import { VSCODE_EXTENSION_ID } from '../../../shared/extensions' +import { register } from '../../../awsService/sagemaker/uriHandlers' + +function createConnectUri(params: { [key: string]: string }): vscode.Uri { + const query = Object.entries(params) + .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`) + .join('&') + return vscode.Uri.parse(`vscode://${VSCODE_EXTENSION_ID.awstoolkit}/connect/sagemaker?${query}`) +} + +describe('SageMaker URI handler', function () { + let handler: UriHandler + let deeplinkConnectStub: sinon.SinonStub + + beforeEach(function () { + handler = new UriHandler() + deeplinkConnectStub = sinon.stub().resolves() + sinon.replace(require('../../../awsService/sagemaker/commands'), 'deeplinkConnect', deeplinkConnectStub) + + register({ + uriHandler: handler, + } as any) + }) + + afterEach(function () { + sinon.restore() + }) + + it('calls deeplinkConnect with all expected params', async function () { + const params = { + connection_identifier: 'abc123', + domain: 'my-domain', + user_profile: 'me', + session: 'sess-xyz', + ws_url: 'wss://example.com', + 'cell-number': '4', + token: 'my-token', + } + + const uri = createConnectUri(params) + await handler.handleUri(uri) + + assert.ok(deeplinkConnectStub.calledOnce) + assert.deepStrictEqual(deeplinkConnectStub.firstCall.args[1], 'abc123') + assert.deepStrictEqual(deeplinkConnectStub.firstCall.args[2], 'sess-xyz') + assert.deepStrictEqual(deeplinkConnectStub.firstCall.args[3], 'wss://example.com&cell-number=4') + assert.deepStrictEqual(deeplinkConnectStub.firstCall.args[4], 'my-token') + assert.deepStrictEqual(deeplinkConnectStub.firstCall.args[5], 'my-domain') + }) +}) diff --git a/packages/core/src/test/shared/clients/sagemakerClient.test.ts b/packages/core/src/test/shared/clients/sagemakerClient.test.ts index 678345d006a..e95fae15989 100644 --- a/packages/core/src/test/shared/clients/sagemakerClient.test.ts +++ b/packages/core/src/test/shared/clients/sagemakerClient.test.ts @@ -16,9 +16,9 @@ describe('SagemakerClient.fetchSpaceAppsAndDomains', function () { let listAppsStub: sinon.SinonStub const appDetails: AppDetails[] = [ - { AppName: 'app1', DomainId: 'domain1', SpaceName: 'space1' }, - { AppName: 'app2', DomainId: 'domain2', SpaceName: 'space2' }, - { AppName: 'app3', DomainId: 'domain2', SpaceName: 'space3' }, + { AppName: 'app1', DomainId: 'domain1', SpaceName: 'space1', AppType: 'CodeEditor' }, + { AppName: 'app2', DomainId: 'domain2', SpaceName: 'space2', AppType: 'CodeEditor' }, + { AppName: 'app3', DomainId: 'domain2', SpaceName: 'space3', AppType: 'JupyterLab' }, ] const spaceDetails: SpaceDetails[] = [ diff --git a/packages/core/src/testLint/gitSecrets.test.ts b/packages/core/src/testLint/gitSecrets.test.ts index d57d0763e2d..49091665ea1 100644 --- a/packages/core/src/testLint/gitSecrets.test.ts +++ b/packages/core/src/testLint/gitSecrets.test.ts @@ -23,7 +23,12 @@ describe('git-secrets', function () { /** git-secrets patterns that will not cause a failure during the scan */ function setAllowListPatterns(gitSecrets: string) { - const allowListPatterns: string[] = ['"accountId": "123456789012"', "Account: '123456789012'"] + const allowListPatterns: string[] = [ + '"accountId": "123456789012"', + "'accountId': '123456789012'", + "Account: '123456789012'", + "accountId: '123456789012'", + ] for (const pattern of allowListPatterns) { // Returns non-zero exit code if pattern already exists diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 3baba69a575..702a86ee8e6 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -7,5 +7,6 @@ "declaration": true, "declarationMap": true }, - "exclude": ["node_modules", ".vscode-test", "src/testFixtures", "dist"] + "exclude": ["node_modules", ".vscode-test", "src/testFixtures", "dist"], + "noEmitOnError": false // allow emitting even with type errors } diff --git a/packages/core/webpack.config.js b/packages/core/webpack.config.js index fba19d133b2..b58c990704a 100644 --- a/packages/core/webpack.config.js +++ b/packages/core/webpack.config.js @@ -23,6 +23,7 @@ module.exports = (env, argv) => { ...baseConfig, entry: { 'src/stepFunctions/asl/aslServer': './src/stepFunctions/asl/aslServer.ts', + 'src/awsService/sagemaker/detached-server/server': './src/awsService/sagemaker/detached-server/server.ts', }, } diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index 537cb901bb4..093d7ce6c73 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -255,13 +255,13 @@ "type": "boolean", "default": false }, - "amazonqChatLSP": { - "type": "boolean", - "default": true - }, "amazonqLSPInlineChat": { "type": "boolean", "default": false + }, + "amazonqChatLSP": { + "type": "boolean", + "default": true } }, "additionalProperties": false @@ -1455,6 +1455,11 @@ } ], "view/item/context": [ + { + "command": "aws.sagemaker.openRemoteConnection", + "group": "inline@0", + "when": "viewItem =~ /^(awsSagemakerSpaceRunningRemoteEnabledNode)$/" + }, { "command": "_aws.toolkit.notifications.dismiss", "when": "viewItem == toolkitNotificationStartUp", @@ -2592,6 +2597,18 @@ } } }, + { + "command": "aws.sagemaker.openRemoteConnection", + "title": "Connect to Sagemaker Space", + "icon": "$(remote-explorer)", + "category": "%AWS.title%", + "enablement": "isCloud9 || !aws.isWebExtHost", + "cloud9": { + "cn": { + "category": "%AWS.title.cn%" + } + } + }, { "command": "aws.ec2.startInstance", "title": "%AWS.command.ec2.startInstance%", diff --git a/packages/toolkit/scripts/build/copyFiles.ts b/packages/toolkit/scripts/build/copyFiles.ts index e081a2eb9b4..782c16ddb50 100644 --- a/packages/toolkit/scripts/build/copyFiles.ts +++ b/packages/toolkit/scripts/build/copyFiles.ts @@ -29,7 +29,6 @@ const tasks: CopyTask[] = [ ...['LICENSE', 'NOTICE'].map((f) => { return { target: path.join('../../', f), destination: path.join(projectRoot, f) } }), - { target: path.join('../core', 'resources'), destination: path.join('..', 'resources') }, { target: path.join('../core/', 'package.nls.json'), @@ -69,6 +68,21 @@ const tasks: CopyTask[] = [ destination: path.join('src', 'stepFunctions', 'asl', 'aslServer.js'), }, + // Sagemaker local server + { + target: path.join( + '../../node_modules', + 'aws-core-vscode', + 'dist', + 'src', + 'awsService', + 'sagemaker', + 'detached-server', + 'server.js' + ), + destination: path.join('src', 'awsService', 'sagemaker', 'detached-server', 'server.js'), + }, + // Serverless Land { target: path.join( diff --git a/packages/toolkit/tsconfig.json b/packages/toolkit/tsconfig.json index 2ec1c0534c1..0aef63efe5a 100644 --- a/packages/toolkit/tsconfig.json +++ b/packages/toolkit/tsconfig.json @@ -5,5 +5,6 @@ "baseUrl": ".", "rootDir": "." }, - "exclude": ["node_modules", ".vscode-test", "src/testFixtures", "dist"] + "exclude": ["node_modules", ".vscode-test", "src/testFixtures", "dist"], + "noEmitOnError": false // allow emitting even with type errors } diff --git a/tsconfig.json b/tsconfig.json index b5676bad46b..c1c1b0ee221 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,7 @@ "jsx": "preserve", "esModuleInterop": true, "incremental": true, - "noEmitOnError": true, + "noEmitOnError": false, "skipLibCheck": true } } From ffd326026d9a5f7ad997c0693c93c86df6b419f2 Mon Sep 17 00:00:00 2001 From: aws-asolidu Date: Mon, 30 Jun 2025 09:38:04 -0700 Subject: [PATCH 04/12] feat(sagemaker-connect): Add start/stop space and error handling (#2136) ## Problem - Users should be able to start and stop SageMaker Spaces directly from the Toolkit extension, without relying on the Studio UI. - When actions like start, stop, or connect fail, users currently receive no feedback, making troubleshooting difficult. ## Solution - Added buttons to start and stop a space - Implemented error notifications for failures when viewing, starting, or stopping spaces. - For errors during startSession, display a webpage hosted by the local SageMaker server, since remote VS Code windows cannot display standard error messages from the local server. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Co-authored-by: Newton Der Co-authored-by: Edward Sun --- package-lock.json | 960 ++++++++++++++++++ packages/core/resources/sagemaker_connect | 3 + packages/core/resources/sagemaker_connect.ps1 | 66 +- .../src/awsService/sagemaker/activation.ts | 23 +- .../core/src/awsService/sagemaker/commands.ts | 66 +- .../sagemaker/detached-server/errorPage.ts | 136 +++ .../detached-server/routes/getSession.ts | 7 + .../sagemaker/explorer/sagemakerParentNode.ts | 41 +- .../sagemaker/explorer/sagemakerSpaceNode.ts | 84 +- .../core/src/awsService/sagemaker/model.ts | 42 +- packages/core/src/shared/clients/sagemaker.ts | 162 ++- .../src/shared/telemetry/vscodeTelemetry.json | 27 + .../detached-server/routes/getSession.test.ts | 2 + .../explorer/sagemakerParentNode.test.ts | 59 +- .../explorer/sagemakerSpaceNode.test.ts | 19 +- .../test/awsService/sagemaker/model.test.ts | 51 +- .../shared/clients/sagemakerClient.test.ts | 104 ++ packages/toolkit/package.json | 23 +- 18 files changed, 1777 insertions(+), 98 deletions(-) create mode 100644 packages/core/src/awsService/sagemaker/detached-server/errorPage.ts diff --git a/package-lock.json b/package-lock.json index ea7c0842597..94d495577cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,6 +68,966 @@ "resolved": "src.gen/@amzn/codewhisperer-streaming", "link": true }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client": { + "version": "1.0.0", + "resolved": "file:../../Downloads/@amzn-rhinestone-monarch-sagemaker-client-1.0.0.tgz", + "integrity": "sha512-RrI07M0PcPKpZjui5nLpFdsrHvsPs2t6AB8ojJDt3epN2iu4Zi89056k5BJ12gexKeEcdsMDiNu3CQleXNy2EQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.363.0", + "@aws-sdk/credential-provider-node": "3.363.0", + "@aws-sdk/middleware-host-header": "3.363.0", + "@aws-sdk/middleware-logger": "3.363.0", + "@aws-sdk/middleware-recursion-detection": "3.363.0", + "@aws-sdk/middleware-signing": "3.363.0", + "@aws-sdk/middleware-user-agent": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.363.0", + "@aws-sdk/util-user-agent-node": "3.363.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-retry": "^1.0.3", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.3", + "@smithy/util-utf8": "^1.0.1", + "@smithy/util-waiter": "^1.0.1", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/client-sso": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.363.0.tgz", + "integrity": "sha512-PZ+HfKSgS4hlMnJzG+Ev8/mgHd/b/ETlJWPSWjC/f2NwVoBQkBnqHjdyEx7QjF6nksJozcVh5Q+kkYLKc/QwBQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.363.0", + "@aws-sdk/middleware-logger": "3.363.0", + "@aws-sdk/middleware-recursion-detection": "3.363.0", + "@aws-sdk/middleware-user-agent": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.363.0", + "@aws-sdk/util-user-agent-node": "3.363.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.1", + "@smithy/middleware-retry": "^1.0.2", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.0.1", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.0.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.2", + "@smithy/util-utf8": "^1.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.363.0.tgz", + "integrity": "sha512-V3Ebiq/zNtDS/O92HUWGBa7MY59RYSsqWd+E0XrXv6VYTA00RlMTbNcseivNgp2UghOgB9a20Nkz6EqAeIN+RQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.363.0", + "@aws-sdk/middleware-logger": "3.363.0", + "@aws-sdk/middleware-recursion-detection": "3.363.0", + "@aws-sdk/middleware-user-agent": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.363.0", + "@aws-sdk/util-user-agent-node": "3.363.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.1", + "@smithy/middleware-retry": "^1.0.2", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.0.1", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.0.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.2", + "@smithy/util-utf8": "^1.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/client-sts": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.363.0.tgz", + "integrity": "sha512-0jj14WvBPJQ8xr72cL0mhlmQ90tF0O0wqXwSbtog6PsC8+KDE6Yf+WsxsumyI8E5O8u3eYijBL+KdqG07F/y/w==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/credential-provider-node": "3.363.0", + "@aws-sdk/middleware-host-header": "3.363.0", + "@aws-sdk/middleware-logger": "3.363.0", + "@aws-sdk/middleware-recursion-detection": "3.363.0", + "@aws-sdk/middleware-sdk-sts": "3.363.0", + "@aws-sdk/middleware-signing": "3.363.0", + "@aws-sdk/middleware-user-agent": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.363.0", + "@aws-sdk/util-user-agent-node": "3.363.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.1", + "@smithy/middleware-retry": "^1.0.1", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.1", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.2", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.1", + "@smithy/util-utf8": "^1.0.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.363.0.tgz", + "integrity": "sha512-VAQ3zITT2Q0acht0HezouYnMFKZ2vIOa20X4zQA3WI0HfaP4D6ga6KaenbDcb/4VFiqfqiRHfdyXHP0ThcDRMA==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.363.0.tgz", + "integrity": "sha512-ZYN+INoqyX5FVC3rqUxB6O8nOWkr0gHRRBm1suoOlmuFJ/WSlW/uUGthRBY5x1AQQnBF8cpdlxZzGHd41lFVNw==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.363.0", + "@aws-sdk/credential-provider-process": "3.363.0", + "@aws-sdk/credential-provider-sso": "3.363.0", + "@aws-sdk/credential-provider-web-identity": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/credential-provider-imds": "^1.0.1", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.363.0.tgz", + "integrity": "sha512-C1qXFIN2yMxD6pGgug0vR1UhScOki6VqdzuBHzXZAGu7MOjvgHNdscEcb3CpWnITHaPL2ztkiw75T1sZ7oIgQg==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.363.0", + "@aws-sdk/credential-provider-ini": "3.363.0", + "@aws-sdk/credential-provider-process": "3.363.0", + "@aws-sdk/credential-provider-sso": "3.363.0", + "@aws-sdk/credential-provider-web-identity": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/credential-provider-imds": "^1.0.1", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.363.0.tgz", + "integrity": "sha512-fOKAINU7Rtj2T8pP13GdCt+u0Ml3gYynp8ki+1jMZIQ+Ju/MdDOqZpKMFKicMn3Z1ttUOgqr+grUdus6z8ceBQ==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.363.0.tgz", + "integrity": "sha512-5RUZ5oM0lwZSo3EehT0dXggOjgtxFogpT3cZvoLGtIwrPBvm8jOQPXQUlaqCj10ThF1sYltEyukz/ovtDwYGew==", + "dependencies": { + "@aws-sdk/client-sso": "3.363.0", + "@aws-sdk/token-providers": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.363.0.tgz", + "integrity": "sha512-Z6w7fjgy79pAax580wdixbStQw10xfyZ+hOYLcPudoYFKjoNx0NQBejg5SwBzCF/HQL23Ksm9kDfbXDX9fkPhA==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.363.0.tgz", + "integrity": "sha512-FobpclDCf5Y1ueyJDmb9MqguAdPssNMlnqWQpujhYVABq69KHu73fSCWSauFPUrw7YOpV8kG1uagDF0POSxHzA==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/middleware-logger": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.363.0.tgz", + "integrity": "sha512-SSGgthScYnFGTOw8EzbkvquqweFmvn7uJihkpFekbtBNGC/jGOGO+8ziHjTQ8t/iI/YKubEwv+LMi0f77HKSEg==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.363.0.tgz", + "integrity": "sha512-MWD/57QgI/N7fG8rtzDTUdSqNpYohQfgj9XCFAoVeI/bU4usrkOrew43L4smJG4XrDxlNT8lSJlDtd64tuiUZA==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.363.0.tgz", + "integrity": "sha512-ri8YaQvXP6odteVTMfxPqFR26Q0h9ejtqhUDv47P34FaKXedEM4nC6ix6o+5FEYj6l8syGyktftZ5O70NoEhug==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/token-providers": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.363.0.tgz", + "integrity": "sha512-6+0aJ1zugNgsMmhTtW2LBWxOVSaXCUk2q3xyTchSXkNzallYaRiZMRkieW+pKNntnu0g5H1T0zyfCO0tbXwxEA==", + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/types": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.357.0.tgz", + "integrity": "sha512-/riCRaXg3p71BeWnShrai0y0QTdXcouPSM0Cn1olZbzTf7s71aLEewrc96qFrL70XhY4XvnxMpqQh+r43XIL3g==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/util-endpoints": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.357.0.tgz", + "integrity": "sha512-XHKyS5JClT9su9hDif715jpZiWHQF9gKZXER8tW0gOizU3R9cyWc9EsJ2BRhFNhi7nt/JF/CLUEc5qDx3ETbUw==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.363.0.tgz", + "integrity": "sha512-fk9ymBUIYbxiGm99Cn+kAAXmvMCWTf/cHAcB79oCXV4ELXdPa9lN5xQhZRFNxLUeXG4OAMEuCAUUuZEj8Fnc1Q==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/types": "^1.1.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.363.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.363.0.tgz", + "integrity": "sha512-Fli/dvgGA9hdnQUrYb1//wNSFlK2jAfdJcfNXA6SeBYzSeH5pVGYF4kXF0FCdnMA3Fef+Zn1zAP/hw9v8VJHWQ==", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-5imgGUlZL4dW4YWdMYAKLmal9ny/tlenM81QZY7xYyb76z9Z/QOg7oM5Ak9HQl8QfFTlGVWwcMXl+54jroRgEQ==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/config-resolver": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-1.1.0.tgz", + "integrity": "sha512-7WD9eZHp46BxAjNGHJLmxhhyeiNWkBdVStd7SUJPUZqQGeIO/REtIrcIfKUfdiHTQ9jyu2SYoqvzqqaFc6987w==", + "dependencies": { + "@smithy/types": "^1.2.0", + "@smithy/util-config-provider": "^1.1.0", + "@smithy/util-middleware": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/credential-provider-imds": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-1.1.0.tgz", + "integrity": "sha512-kUMOdEu3RP6ozH0Ga8OeMP8gSkBsK1UqZZKyPLFnpZHrtZuHSSt7M7gsHYB/bYQBZAo3o7qrGmRty3BubYtYxQ==", + "dependencies": { + "@smithy/node-config-provider": "^1.1.0", + "@smithy/property-provider": "^1.2.0", + "@smithy/types": "^1.2.0", + "@smithy/url-parser": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/fetch-http-handler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-1.1.0.tgz", + "integrity": "sha512-N22C9R44u5WGlcY+Wuv8EXmCAq62wWwriRAuoczMEwAIjPbvHSthyPSLqI4S7kAST1j6niWg8kwpeJ3ReAv3xg==", + "dependencies": { + "@smithy/protocol-http": "^1.2.0", + "@smithy/querystring-builder": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-base64": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/hash-node": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-1.1.0.tgz", + "integrity": "sha512-yiNKDGMzrQjnpnbLfkYKo+HwIxmBAsv0AI++QIJwvhfkLpUTBylelkv6oo78/YqZZS6h+bGfl0gILJsKE2wAKQ==", + "dependencies": { + "@smithy/types": "^1.2.0", + "@smithy/util-buffer-from": "^1.1.0", + "@smithy/util-utf8": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/invalid-dependency": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-1.1.0.tgz", + "integrity": "sha512-h2rXn68ClTwzPXYzEUNkz+0B/A0Hz8YdFNTiEwlxkwzkETGKMxmsrQGFXwYm3jd736R5vkXcClXz1ddKrsaBEQ==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/is-array-buffer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-1.1.0.tgz", + "integrity": "sha512-twpQ/n+3OWZJ7Z+xu43MJErmhB/WO/mMTnqR6PwWQShvSJ/emx5d1N59LQZk6ZpTAeuRWrc+eHhkzTp9NFjNRQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/middleware-content-length": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-1.1.0.tgz", + "integrity": "sha512-iNxwhZ7Xc5+LjeDElEOi/Nh8fFsc9Dw9+5w7h7/GLFIU0RgAwBJuJtcP1vNTOwzW4B3hG+gRu8sQLqA9OEaTwA==", + "dependencies": { + "@smithy/protocol-http": "^1.2.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/middleware-endpoint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-1.1.0.tgz", + "integrity": "sha512-PvpazNjVpxX2ICrzoFYCpFnjB39DKCpZds8lRpAB3p6HGrx6QHBaNvOzVhJGBf0jcAbfCdc5/W0n9z8VWaSSww==", + "dependencies": { + "@smithy/middleware-serde": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/url-parser": "^1.1.0", + "@smithy/util-middleware": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/middleware-retry": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-1.1.0.tgz", + "integrity": "sha512-lINKYxIvT+W20YFOtHBKeGm7npuJg0/YCoShttU7fVpsmU+a2rdb9zrJn1MHqWfUL6DhTAWGa0tH2O7l4XrDcw==", + "dependencies": { + "@smithy/protocol-http": "^1.2.0", + "@smithy/service-error-classification": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-middleware": "^1.1.0", + "@smithy/util-retry": "^1.1.0", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/middleware-serde": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-1.1.0.tgz", + "integrity": "sha512-RiBMxhxuO9VTjHsjJvhzViyceoLhU6gtrnJGpAXY43wE49IstXIGEQz8MT50/hOq5EumX16FCpup0r5DVyfqNQ==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/middleware-stack": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-1.1.0.tgz", + "integrity": "sha512-XynYiIvXNea2BbLcppvpNK0zu8o2woJqgnmxqYTn4FWagH/Hr2QIk8LOsUz7BIJ4tooFhmx8urHKCdlPbbPDCA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/node-config-provider": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-1.1.0.tgz", + "integrity": "sha512-2G4TlzUnmTrUY26VKTonQqydwb+gtM/mcl+TqDP8CnWtJKVL8ElPpKgLGScP04bPIRY9x2/10lDdoaRXDqPuCw==", + "dependencies": { + "@smithy/property-provider": "^1.2.0", + "@smithy/shared-ini-file-loader": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/node-http-handler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-1.1.0.tgz", + "integrity": "sha512-d3kRriEgaIiGXLziAM8bjnaLn1fthCJeTLZIwEIpzQqe6yPX0a+yQoLCTyjb2fvdLwkMoG4p7THIIB5cj5lkbg==", + "dependencies": { + "@smithy/abort-controller": "^1.1.0", + "@smithy/protocol-http": "^1.2.0", + "@smithy/querystring-builder": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/property-provider": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-1.2.0.tgz", + "integrity": "sha512-qlJd9gT751i4T0t/hJAyNGfESfi08Fek8QiLcysoKPgR05qHhG0OYhlaCJHhpXy4ECW0lHyjvFM1smrCLIXVfw==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/protocol-http": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.2.0.tgz", + "integrity": "sha512-GfGfruksi3nXdFok5RhgtOnWe5f6BndzYfmEXISD+5gAGdayFGpjWu5pIqIweTudMtse20bGbc+7MFZXT1Tb8Q==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/querystring-builder": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-1.1.0.tgz", + "integrity": "sha512-gDEi4LxIGLbdfjrjiY45QNbuDmpkwh9DX4xzrR2AzjjXpxwGyfSpbJaYhXARw9p17VH0h9UewnNQXNwaQyYMDA==", + "dependencies": { + "@smithy/types": "^1.2.0", + "@smithy/util-uri-escape": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/querystring-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-1.1.0.tgz", + "integrity": "sha512-Lm/FZu2qW3XX+kZ4WPwr+7aAeHf1Lm84UjNkKyBu16XbmEV7ukfhXni2aIwS2rcVf8Yv5E7wchGGpOFldj9V4Q==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/service-error-classification": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-1.1.0.tgz", + "integrity": "sha512-OCTEeJ1igatd5kFrS2VDlYbainNNpf7Lj1siFOxnRWqYOP9oNvC5HOJBd3t+Z8MbrmehBtuDJ2QqeBsfeiNkww==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/shared-ini-file-loader": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-1.1.0.tgz", + "integrity": "sha512-S/v33zvCWzFyGZGlsEF0XsZtNNR281UhR7byk3nRfsgw5lGpg51rK/zjMgulM+h6NSuXaFILaYrw1I1v4kMcuA==", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/smithy-client": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-1.1.0.tgz", + "integrity": "sha512-j32SGgVhv2G9nBTmel9u3OXux8KG20ssxuFakJrEeDug3kqbl1qrGzVLCe+Eib402UDtA0Sp1a4NZ2SEXDBxag==", + "dependencies": { + "@smithy/middleware-stack": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-stream": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", + "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/url-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-1.1.0.tgz", + "integrity": "sha512-tpvi761kzboiLNGEWczuybMPCJh6WHB3cz9gWAG95mSyaKXmmX8ZcMxoV+irZfxDqLwZVJ22XTumu32S7Ow8aQ==", + "dependencies": { + "@smithy/querystring-parser": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-base64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-1.1.0.tgz", + "integrity": "sha512-FpYmDmVbOXAxqvoVCwqehUN0zXS+lN8V7VS9O7I8MKeVHdSTsZzlwiMEvGoyTNOXWn8luF4CTDYgNHnZViR30g==", + "dependencies": { + "@smithy/util-buffer-from": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-body-length-browser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-1.1.0.tgz", + "integrity": "sha512-cep3ioRxzRZ2Jbp3Kly7gy6iNVefYXiT6ETt8W01RQr3uwi1YMkrbU1p3lMR4KhX/91Nrk6UOgX1RH+oIt48RQ==", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-body-length-node": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-1.1.0.tgz", + "integrity": "sha512-fRHRjkUuT5em4HZoshySXmB1n3HAU7IS232s+qU4TicexhyGJpXMK/2+c56ePOIa1FOK2tV1Q3J/7Mae35QVSw==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-buffer-from": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-1.1.0.tgz", + "integrity": "sha512-9m6NXE0ww+ra5HKHCHig20T+FAwxBAm7DIdwc/767uGWbRcY720ybgPacQNB96JMOI7xVr/CDa3oMzKmW4a+kw==", + "dependencies": { + "@smithy/is-array-buffer": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-config-provider": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-1.1.0.tgz", + "integrity": "sha512-rQ47YpNmF6Is4I9GiE3T3+0xQ+r7RKRKbmHYyGSbyep/0cSf9kteKcI0ssJTvveJ1K4QvwrxXj1tEFp/G2UqxQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-defaults-mode-browser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-1.1.0.tgz", + "integrity": "sha512-0bWhs1e412bfC5gwPCMe8Zbz0J8UoZ/meEQdo6MYj8Ne+c+QZ+KxVjx0a1dFYOclvM33SslL9dP0odn8kfblkg==", + "dependencies": { + "@smithy/property-provider": "^1.2.0", + "@smithy/types": "^1.2.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-defaults-mode-node": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-1.1.0.tgz", + "integrity": "sha512-440e25TUH2b+TeK5CwsjYFrI9ShVOgA31CoxCKiv4ncSK4ZM68XW5opYxQmzMbRWARGEMu2XEUeBmOgMU2RLsw==", + "dependencies": { + "@smithy/config-resolver": "^1.1.0", + "@smithy/credential-provider-imds": "^1.1.0", + "@smithy/node-config-provider": "^1.1.0", + "@smithy/property-provider": "^1.2.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-hex-encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-1.1.0.tgz", + "integrity": "sha512-7UtIE9eH0u41zpB60Jzr0oNCQ3hMJUabMcKRUVjmyHTXiWDE4vjSqN6qlih7rCNeKGbioS7f/y2Jgym4QZcKFg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-middleware": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-1.1.0.tgz", + "integrity": "sha512-6hhckcBqVgjWAqLy2vqlPZ3rfxLDhFWEmM7oLh2POGvsi7j0tHkbN7w4DFhuBExVJAbJ/qqxqZdRY6Fu7/OezQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-retry": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-1.1.0.tgz", + "integrity": "sha512-ygQW5HBqYXpR3ua09UciS0sL7UGJzGiktrKkOuEJwARoUuzz40yaEGU6xd9Gs7KBmAaFC8gMfnghHtwZ2nyBCQ==", + "dependencies": { + "@smithy/service-error-classification": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-1.1.0.tgz", + "integrity": "sha512-w3lsdGsntaLQIrwDWJkIFKrFscgZXwU/oxsse09aSTNv5TckPhDeYea3LhsDrU5MGAG3vprhVZAKr33S45coVA==", + "dependencies": { + "@smithy/fetch-http-handler": "^1.1.0", + "@smithy/node-http-handler": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-base64": "^1.1.0", + "@smithy/util-buffer-from": "^1.1.0", + "@smithy/util-hex-encoding": "^1.1.0", + "@smithy/util-utf8": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-uri-escape": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-1.1.0.tgz", + "integrity": "sha512-/jL/V1xdVRt5XppwiaEU8Etp5WHZj609n0xMTuehmCqdoOFbId1M+aEeDWZsQ+8JbEB/BJ6ynY2SlYmOaKtt8w==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-1.1.0.tgz", + "integrity": "sha512-p/MYV+JmqmPyjdgyN2UxAeYDj9cBqCjp0C/NsTWnnjoZUVqoeZ6IrW915L9CAKWVECgv9lVQGc4u/yz26/bI1A==", + "dependencies": { + "@smithy/util-buffer-from": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-waiter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-1.1.0.tgz", + "integrity": "sha512-S6FNIB3UJT+5Efd/0DeziO5Rs82QAMODHW4v2V3oNRrwaBigY/7Yx3SiLudZuF9WpVsV08Ih3BjIH34nzZiinQ==", + "dependencies": { + "@smithy/abort-controller": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@amzn/sagemaker-client": { "version": "1.0.0", "resolved": "file:src.gen/@amzn/sagemaker-client/1.0.0.tgz", diff --git a/packages/core/resources/sagemaker_connect b/packages/core/resources/sagemaker_connect index b4a00f43cd5..a1b7c9c0db0 100755 --- a/packages/core/resources/sagemaker_connect +++ b/packages/core/resources/sagemaker_connect @@ -13,6 +13,9 @@ _get_ssm_session_info() { response=$(curl -s -w "%{http_code}" -o /tmp/ssm_session_response.json "$url_to_get_session_info") http_status="${response: -3}" session_json=$(cat /tmp/ssm_session_response.json) + + # Clean up temporary file + rm -f /tmp/ssm_session_response.json if [[ "$http_status" -ne 200 ]]; then echo "Error: Failed to get SSM session info. HTTP status: $http_status" diff --git a/packages/core/resources/sagemaker_connect.ps1 b/packages/core/resources/sagemaker_connect.ps1 index c05dbfe3e53..034f9f09754 100644 --- a/packages/core/resources/sagemaker_connect.ps1 +++ b/packages/core/resources/sagemaker_connect.ps1 @@ -1,8 +1,13 @@ param ( - [Parameter(Mandatory=$true)][string]$Hostname, + [Parameter(Mandatory = $true)][string]$Hostname ) +Write-Host "`n--- Script Start ---" +Write-Host "Start Time: $(Get-Date -Format o)" +Write-Host "Hostname argument received: $Hostname" + Set-PSDebug -Trace 1 + function Get-SSMSessionInfo { param ( [string]$CredentialsType, @@ -10,22 +15,30 @@ function Get-SSMSessionInfo { [int]$LocalEndpointPort ) + Write-Host "Calling Get-SSMSessionInfo with credsType=${CredentialsType}, arn=${AwsResourceArn}, port=${LocalEndpointPort}" + $url = "http://127.0.0.1:$LocalEndpointPort/get_session?connection_identifier=$AwsResourceArn&credentials_type=$CredentialsType" + Write-Host "Request URL: $url" try { $response = Invoke-WebRequest -Uri $url -UseBasicParsing -ErrorAction Stop + Write-Host "Received response with status: $($response.StatusCode)" + if ($response.StatusCode -ne 200) { - Write-Error "Error: Failed to get SSM session info. HTTP status: $($response.StatusCode)" + Write-Error "Failed to get SSM session info. HTTP status: $($response.StatusCode)" Write-Error "Response: $($response.Content)" exit 1 } + if (-not $response.Content) { - Write-Error "Error: SSM connection info is empty." + Write-Error "SSM connection info is empty." exit 1 } + $script:SSM_SESSION_JSON = $response.Content + Write-Host "Session JSON successfully retrieved" } catch { - Write-Error "Exception: $_" + Write-Error "Exception in Get-SSMSessionInfo: $_" exit 1 } } @@ -39,6 +52,7 @@ function Get-SSMSessionInfoAsync { $requestId = [string][DateTimeOffset]::Now.ToUnixTimeMilliseconds() $url = "http://localhost:$LocalEndpointPort/get_session_async?connection_identifier=$AwsResourceArn&credentials_type=$CredentialsType&request_id=$requestId" + Write-Host "Calling Get-SSMSessionInfoAsync with URL: $url" $maxRetries = 60 $retryInterval = 5 @@ -47,42 +61,45 @@ function Get-SSMSessionInfoAsync { try { $response = Invoke-WebRequest -Uri $url -UseBasicParsing -ErrorAction Stop $statusCode = $response.StatusCode + Write-Host "Attempt ${attempt}: HTTP ${statusCode}" + if ($statusCode -eq 200) { $script:SSM_SESSION_JSON = $response.Content + Write-Host "Session JSON successfully retrieved" return } elseif ($statusCode -eq 202 -or $statusCode -eq 204) { - Write-Host "Info: Session not ready (HTTP $statusCode). Retrying in $retryInterval seconds... [$attempt/$maxRetries]" + Write-Host "Session not ready. Retrying in ${retryInterval} seconds... [${attempt}/${maxRetries}]" Start-Sleep -Seconds $retryInterval } else { - Write-Error "Error: Failed to get SSM session info. HTTP status: $statusCode" + Write-Error "Failed to get SSM session info. HTTP status: ${statusCode}" Write-Error "Response: $($response.Content)" exit 1 } } catch { - Write-Error "Exception: $_" + Write-Error "Exception in Get-SSMSessionInfoAsync: $_" exit 1 } } - Write-Error "Error: Timed out after $maxRetries attempts waiting for session to be ready." + Write-Error "Timed out after ${maxRetries} attempts waiting for session to be ready." exit 1 } -Write-Host "HOSTName: $Hostname" - # Parse creds_type and AWS resource ARN from HOSTNAME +Write-Host "`nParsing hostname..." if ($Hostname -match "^sm_([^_]+)_(arn_._aws.*)$") { $CREDS_TYPE = $matches[1] $AWS_RESOURCE_ARN = $matches[2] -replace '_._', ':' -replace '__', '/' } else { - Write-Error "Invalid hostname format. Expected format: sm__ with ':' replaced by '--' and '/' replaced by '__'" + Write-Error "Invalid hostname format. Expected format: sm__" exit 1 } $REGION = ($AWS_RESOURCE_ARN -split ':')[3] -Write-Host "Extracted CREDS_TYPE: $CREDS_TYPE" -Write-Host "Extracted AWS_RESOURCE_ARN: $AWS_RESOURCE_ARN" -Write-Host "REGION: $REGION" +Write-Host "Parsed values:" +Write-Host " CREDS_TYPE: ${CREDS_TYPE}" +Write-Host " AWS_RESOURCE_ARN: ${AWS_RESOURCE_ARN}" +Write-Host " REGION: ${REGION}" # Validate credentials type if ($CREDS_TYPE -ne "lc" -and $CREDS_TYPE -ne "dl") { @@ -90,21 +107,24 @@ if ($CREDS_TYPE -ne "lc" -and $CREDS_TYPE -ne "dl") { exit 1 } +# Read port from local info JSON +Write-Host "`nReading SAGEMAKER_LOCAL_SERVER_FILE_PATH: $env:SAGEMAKER_LOCAL_SERVER_FILE_PATH" try { $jsonContent = Get-Content $env:SAGEMAKER_LOCAL_SERVER_FILE_PATH -Raw | ConvertFrom-Json $LOCAL_ENDPOINT_PORT = $jsonContent.port + Write-Host "Extracted port: $LOCAL_ENDPOINT_PORT" } catch { - Write-Error "[Error] Failed to read or parse JSON file at $env:SAGEMAKER_LOCAL_SERVER_FILE_PATH" + Write-Error "Failed to read or parse JSON file at $env:SAGEMAKER_LOCAL_SERVER_FILE_PATH" exit 1 } if (-not $LOCAL_ENDPOINT_PORT -or $LOCAL_ENDPOINT_PORT -eq "null") { - Write-Error "[Error] 'port' field is missing or invalid in $env:SAGEMAKER_LOCAL_SERVER_FILE_PATH" + Write-Error "'port' field is missing or invalid in $env:SAGEMAKER_LOCAL_SERVER_FILE_PATH" exit 1 } - # Retrieve SSM session +Write-Host "`nStarting session retrieval..." if ($CREDS_TYPE -eq "lc") { Get-SSMSessionInfo -CredentialsType "local" -AwsResourceArn $AWS_RESOURCE_ARN -LocalEndpointPort $LOCAL_ENDPOINT_PORT } elseif ($CREDS_TYPE -eq "dl") { @@ -112,17 +132,17 @@ if ($CREDS_TYPE -eq "lc") { } # Execute the session +Write-Host "`nLaunching session-manager-plugin..." $sessionPlugin = if ($env:AWS_SSM_CLI) { $env:AWS_SSM_CLI } else { "session-manager-plugin" } - $jsonObj = $script:SSM_SESSION_JSON | ConvertFrom-Json - $streamUrl = $jsonObj.StreamUrl $tokenValue = $jsonObj.TokenValue $sessionId = $jsonObj.SessionId -Write-Host "Stream URL: $streamUrl" -Write-Host "Token Value: $tokenValue" -Write-Host "Session ID: $sessionId" +Write-Host "Session Values:" +Write-Host " Stream URL: ${streamUrl}" +Write-Host " Token Value: ${tokenValue}" +Write-Host " Session ID: ${sessionId}" -& $sessionPlugin "{\`"streamUrl\`":\`"$streamUrl\`",\`"tokenValue\`":\`"$tokenValue\`",\`"sessionId\`":\`"$sessionId\`"}" "$Region" "StartSession" +& $sessionPlugin "{\`"streamUrl\`":\`"${streamUrl}\`",\`"tokenValue\`":\`"${tokenValue}\`",\`"sessionId\`":\`"${sessionId}\`"}" "$REGION" "StartSession" \ No newline at end of file diff --git a/packages/core/src/awsService/sagemaker/activation.ts b/packages/core/src/awsService/sagemaker/activation.ts index 5305a541885..49a0244c48e 100644 --- a/packages/core/src/awsService/sagemaker/activation.ts +++ b/packages/core/src/awsService/sagemaker/activation.ts @@ -7,22 +7,29 @@ import { Commands } from '../../shared/vscode/commands2' import { SagemakerSpaceNode } from './explorer/sagemakerSpaceNode' import { SagemakerParentNode } from './explorer/sagemakerParentNode' import * as uriHandlers from './uriHandlers' -import { getLogger } from '../../shared/logger/logger' -import { openRemoteConnect, filterSpaceAppsByDomainUserProfiles } from './commands' +import { openRemoteConnect, filterSpaceAppsByDomainUserProfiles, stopSpace } from './commands' import { ExtContext } from '../../shared/extensions' +import { telemetry } from '../../shared/telemetry/telemetry' export async function activate(ctx: ExtContext): Promise { ctx.extensionContext.subscriptions.push( uriHandlers.register(ctx), Commands.register('aws.sagemaker.openRemoteConnection', async (node: SagemakerSpaceNode) => { - getLogger().info('start openRemoteConnection') - await openRemoteConnect(node, ctx.extensionContext) - }) - ) + await telemetry.sagemaker_openRemoteConnection.run(async () => { + await openRemoteConnect(node, ctx.extensionContext) + }) + }), - ctx.extensionContext.subscriptions.push( Commands.register('aws.sagemaker.filterSpaceApps', async (node: SagemakerParentNode) => { - await filterSpaceAppsByDomainUserProfiles(node) + await telemetry.sagemaker_filterSpaces.run(async () => { + await filterSpaceAppsByDomainUserProfiles(node) + }) + }), + + Commands.register('aws.sagemaker.stopSpace', async (node: SagemakerSpaceNode) => { + await telemetry.sagemaker_stopSpace.run(async () => { + await stopSpace(node, ctx.extensionContext) + }) }) ) } diff --git a/packages/core/src/awsService/sagemaker/commands.ts b/packages/core/src/awsService/sagemaker/commands.ts index 0c4b5400350..22a00a25219 100644 --- a/packages/core/src/awsService/sagemaker/commands.ts +++ b/packages/core/src/awsService/sagemaker/commands.ts @@ -10,11 +10,14 @@ import { SagemakerParentNode } from './explorer/sagemakerParentNode' import { DomainKeyDelimiter } from './utils' import { startVscodeRemote } from '../../shared/extensions/ssh' import { getLogger } from '../../shared/logger/logger' -import { SagemakerSpaceNode } from './explorer/sagemakerSpaceNode' +import { SagemakerSpaceNode, tryRefreshNode } from './explorer/sagemakerSpaceNode' import { isRemoteWorkspace } from '../../shared/vscode/env' import _ from 'lodash' -import { prepareDevEnvConnection } from './model' +import { prepareDevEnvConnection, tryRemoteConnection } from './model' import { ExtContext } from '../../shared/extensions' +import { SagemakerClient } from '../../shared/clients/sagemaker' +import { ToolkitError } from '../../shared/errors' +import { showConfirmationMessage } from '../../shared/utilities/messages' const localize = nls.loadMessageBundle() @@ -118,25 +121,52 @@ export async function deeplinkConnect( } } -export async function openRemoteConnect(node: SagemakerSpaceNode, ctx: vscode.ExtensionContext) { - getLogger().debug( - `sm:openRemoteConnect: node: ${node.toString()} appArn: ${await node.getAppArn()} region: ${node.regionCode}` - ) +export async function stopSpace(node: SagemakerSpaceNode, ctx: vscode.ExtensionContext) { + const spaceName = node.spaceApp.SpaceName! + const confirmed = await showConfirmationMessage({ + prompt: `You are about to stop this space. Any active resource will also be stopped. Are you sure you want to stop the space?`, + confirm: 'Stop Space', + cancel: 'Cancel', + type: 'warning', + }) - const spaceArn = (await node.getSpaceArn()) as string - const remoteEnv = await prepareDevEnvConnection(spaceArn, ctx, 'sm_lc') + if (!confirmed) { + return + } + const client = new SagemakerClient(node.regionCode) try { - await startVscodeRemote( - remoteEnv.SessionProcess, - remoteEnv.hostname, - '/home/sagemaker-user', - remoteEnv.vscPath, - 'sagemaker-user' - ) + await client.deleteApp({ + DomainId: node.spaceApp.DomainId!, + SpaceName: spaceName, + AppType: node.spaceApp.App!.AppType!, + AppName: node.spaceApp.App?.AppName, + }) } catch (err) { - getLogger().error( - `sm:OpenRemoteConnect: Unable to connect to target space with arn: ${await node.getAppArn()} error: ${err}` - ) + const error = err as Error + if (error.name === 'AccessDeniedException') { + throw new ToolkitError('You do not have permission to stop spaces. Please contact your administrator', { + cause: error, + }) + } else { + throw err + } + } + await tryRefreshNode(node) +} + +export async function openRemoteConnect(node: SagemakerSpaceNode, ctx: vscode.ExtensionContext) { + if (node.getStatus() === 'Stopped') { + const client = new SagemakerClient(node.regionCode) + await client.startSpace(node.spaceApp.SpaceName!, node.spaceApp.DomainId!) + await tryRefreshNode(node) + const appType = node.spaceApp.SpaceSettingsSummary?.AppType + if (!appType) { + throw new ToolkitError('AppType is undefined for the selected space. Cannot start remote connection.') + } + await client.waitForAppInService(node.spaceApp.DomainId!, node.spaceApp.SpaceName!, appType) + await tryRemoteConnection(node, ctx) + } else if (node.getStatus() === 'Running') { + await tryRemoteConnection(node, ctx) } } diff --git a/packages/core/src/awsService/sagemaker/detached-server/errorPage.ts b/packages/core/src/awsService/sagemaker/detached-server/errorPage.ts new file mode 100644 index 00000000000..b297c8e7083 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/detached-server/errorPage.ts @@ -0,0 +1,136 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Disabled: detached server files cannot import vscode. +/* eslint-disable no-restricted-imports */ +import { randomUUID } from 'crypto' +import { join } from 'path' +import { promises as fs } from 'fs' +import os from 'os' +import { SageMakerServiceException } from '@amzn/sagemaker-client' +import { open } from './utils' + +export enum ExceptionType { + ACCESS_DENIED = 'AccessDeniedException', + DEFAULT = 'Default', + INTERNAL_FAILURE = 'InternalFailure', + RESOURCE_LIMIT_EXCEEDED = 'ResourceLimitExceeded', + THROTTLING = 'ThrottlingException', + VALIDATION = 'ValidationException', +} + +export const getVSCodeErrorTitle = (error: SageMakerServiceException): string => { + const exceptionType = error.name as ExceptionType + + if (exceptionType in ErrorText.StartSession) { + return ErrorText.StartSession[exceptionType].Title + } + + return ErrorText.StartSession[ExceptionType.DEFAULT].Title +} + +export const getVSCodeErrorText = (error: SageMakerServiceException): string => { + const exceptionType = error.name as ExceptionType + + switch (exceptionType) { + case ExceptionType.ACCESS_DENIED: + case ExceptionType.VALIDATION: + return ErrorText.StartSession[exceptionType].Text.replace('{message}', error.message) + case ExceptionType.INTERNAL_FAILURE: + case ExceptionType.RESOURCE_LIMIT_EXCEEDED: + case ExceptionType.THROTTLING: + return ErrorText.StartSession[exceptionType].Text + default: + return ErrorText.StartSession[ExceptionType.DEFAULT].Text.replace('{exceptionType}', exceptionType) + } +} + +export const ErrorText = { + StartSession: { + [ExceptionType.ACCESS_DENIED]: { + Title: 'Remote access denied', + Text: 'Unable to connect because: [{message}]', + }, + [ExceptionType.DEFAULT]: { + Title: 'Unexpected system error', + Text: 'We encountered an unexpected error: [{exceptionType}]. Please contact your administrator and provide them with this error so they can investigate the issue.', + }, + [ExceptionType.INTERNAL_FAILURE]: { + Title: 'Failed to connect remotely to VSCode', + Text: 'Unable to establish remote connection to VSCode. This could be due to several factors. Please try again by clicking the VSCode button. If the problem persists, please contact your admin.', + }, + [ExceptionType.RESOURCE_LIMIT_EXCEEDED]: { + Title: 'Connection limit reached', + Text: 'You have 10 active remote connections to this space. Stop an existing connection to start a new one.', + }, + [ExceptionType.THROTTLING]: { + Title: 'Too many connection attempts', + Text: "You're connecting too quickly. Wait a moment and try again.", + }, + [ExceptionType.VALIDATION]: { + Title: 'Configuration error', + Text: 'The operation cannot be completed due to: [{message}]', + }, + }, +} + +export async function openErrorPage(title: string, message: string) { + const html = ` + + + + ${title} + + + +

+ +` + + const filePath = join(os.tmpdir(), `sagemaker-error-${randomUUID()}.html`) + await fs.writeFile(filePath, html, 'utf8') + await open(filePath) +} diff --git a/packages/core/src/awsService/sagemaker/detached-server/routes/getSession.ts b/packages/core/src/awsService/sagemaker/detached-server/routes/getSession.ts index b8736ffe715..a39b4c1c812 100644 --- a/packages/core/src/awsService/sagemaker/detached-server/routes/getSession.ts +++ b/packages/core/src/awsService/sagemaker/detached-server/routes/getSession.ts @@ -9,6 +9,8 @@ import { IncomingMessage, ServerResponse } from 'http' import { startSagemakerSession, parseArn } from '../utils' import { resolveCredentialsFor } from '../credentials' import url from 'url' +import { SageMakerServiceException } from '@amzn/sagemaker-client' +import { getVSCodeErrorText, getVSCodeErrorTitle, openErrorPage } from '../errorPage' export async function handleGetSession(req: IncomingMessage, res: ServerResponse): Promise { const parsedUrl = url.parse(req.url || '', true) @@ -27,6 +29,7 @@ export async function handleGetSession(req: IncomingMessage, res: ServerResponse console.error('Failed to resolve credentials:', err) res.writeHead(500, { 'Content-Type': 'text/plain' }) res.end((err as Error).message) + return } const { region } = parseArn(connectionIdentifier) @@ -42,7 +45,11 @@ export async function handleGetSession(req: IncomingMessage, res: ServerResponse }) ) } catch (err) { + const error = err as SageMakerServiceException console.error(`Failed to start SageMaker session for ${connectionIdentifier}:`, err) + const errorTitle = getVSCodeErrorTitle(error) + const errorText = getVSCodeErrorText(error) + await openErrorPage(errorTitle, errorText) res.writeHead(500, { 'Content-Type': 'text/plain' }) res.end('Failed to start SageMaker session') return diff --git a/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts b/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts index a1916bc480d..ced97be0565 100644 --- a/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts +++ b/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts @@ -17,6 +17,7 @@ import { isRemoteWorkspace } from '../../../shared/vscode/env' import { SagemakerConstants } from './constants' import { SagemakerSpaceNode } from './sagemakerSpaceNode' import { getDomainSpaceKey, getDomainUserProfileKey, getRemoteAppMetadata, getSpaceAppsForUserProfile } from '../utils' +import { PollingSet } from '../../../shared/utilities/pollingSet' export const parentContextValue = 'awsSagemakerParentNode' @@ -32,12 +33,13 @@ export class SagemakerParentNode extends AWSTreeNodeBase { domainUserProfiles: Map = new Map() spaceApps: Map = new Map() callerIdentity: GetCallerIdentityResponse = {} + public readonly pollingSet: PollingSet = new PollingSet(5000, this.updatePendingNodes.bind(this)) public constructor( public override readonly regionCode: string, protected readonly sagemakerClient: SagemakerClient ) { - super('SageMaker', vscode.TreeItemCollapsibleState.Collapsed) + super('SageMaker AI', vscode.TreeItemCollapsibleState.Collapsed) this.sagemakerSpaceNodes = new Map() this.stsClient = new DefaultStsClient(regionCode) } @@ -55,6 +57,34 @@ export class SagemakerParentNode extends AWSTreeNodeBase { return result } + public trackPendingNode(domainSpaceKey: string) { + this.pollingSet.add(domainSpaceKey) + } + + private async updatePendingNodes() { + for (const spaceKey of this.pollingSet.values()) { + const childNode = this.getSpaceNodes(spaceKey) + await this.updatePendingSpaceNode(childNode) + } + } + + private async updatePendingSpaceNode(node: SagemakerSpaceNode) { + await node.updateSpaceAppStatus() + if (!node.isPending()) { + this.pollingSet.delete(node.DomainSpaceKey) + await node.refreshNode() + } + } + + public getSpaceNodes(spaceKey: string): SagemakerSpaceNode { + const childNode = this.sagemakerSpaceNodes.get(spaceKey) + if (childNode) { + return childNode + } else { + throw new Error(`Node with id ${spaceKey} from polling set not found`) + } + } + public async getLocalSelectedDomainUsers(): Promise { /** * By default, filter userProfileNames that match the detected IAM user, IAM assumed role @@ -161,4 +191,13 @@ export class SagemakerParentNode extends AWSTreeNodeBase { (key) => new SagemakerSpaceNode(this, this.sagemakerClient, this.regionCode, spaceApps.get(key)!) ) } + + public async clearChildren() { + this.sagemakerSpaceNodes = new Map() + } + + public async refreshNode(): Promise { + await this.clearChildren() + await vscode.commands.executeCommand('aws.refreshAwsExplorerNode', this) + } } diff --git a/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts b/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts index 8970682af15..16fd00d95cb 100644 --- a/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts +++ b/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts @@ -11,6 +11,7 @@ import { AWSTreeNodeBase } from '../../../shared/treeview/nodes/awsTreeNodeBase' import { SagemakerParentNode } from './sagemakerParentNode' import { generateSpaceStatus } from '../utils' import { getIcon } from '../../../shared/icons' +import { getLogger } from '../../../shared/logger/logger' export class SagemakerSpaceNode extends AWSTreeNodeBase implements AWSResourceNode { public constructor( @@ -25,10 +26,41 @@ export class SagemakerSpaceNode extends AWSTreeNodeBase implements AWSResourceNo } public updateSpace(spaceApp: SagemakerSpaceApp) { + this.setSpaceStatus(spaceApp.Status ?? '', spaceApp.App?.Status ?? '') this.label = this.buildLabel() this.description = this.buildDescription() this.tooltip = new vscode.MarkdownString(this.buildTooltip()) this.iconPath = this.getAppIcon() + + if (this.isPending()) { + this.parent.trackPendingNode(this.DomainSpaceKey) + } + } + + public setSpaceStatus(spaceStatus: string, appStatus: string) { + this.spaceApp.Status = spaceStatus + if (this.spaceApp.App) { + this.spaceApp.App.Status = appStatus + } + } + + public isPending(): boolean { + return this.getStatus() !== 'Running' && this.getStatus() !== 'Stopped' + } + + public getStatus(): string { + return generateSpaceStatus(this.spaceApp.Status, this.spaceApp.App?.Status) + } + + public async getAppStatus() { + const app = await this.client.describeApp({ + DomainId: this.spaceApp.DomainId, + AppName: this.spaceApp.App?.AppName, + AppType: this.spaceApp.SpaceSettingsSummary?.AppType, + SpaceName: this.spaceApp.SpaceName, + }) + + return app.Status ?? 'Unknown' } public get name(): string { @@ -59,6 +91,26 @@ export class SagemakerSpaceNode extends AWSTreeNodeBase implements AWSResourceNo return appDetails.SpaceArn } + public async updateSpaceAppStatus() { + const space = await this.client.describeSpace({ + DomainId: this.spaceApp.DomainId, + SpaceName: this.spaceApp.SpaceName, + }) + + const app = await this.client.describeApp({ + DomainId: this.spaceApp.DomainId, + AppName: this.spaceApp.App?.AppName, + AppType: this.spaceApp.SpaceSettingsSummary?.AppType, + SpaceName: this.spaceApp.SpaceName, + }) + + this.updateSpace({ + ...space, + App: app, + DomainSpaceKey: this.spaceApp.DomainSpaceKey, + }) + } + private buildLabel(): string { const status = generateSpaceStatus(this.spaceApp.Status, this.spaceApp.App?.Status) return `${this.name} (${status})` @@ -87,10 +139,40 @@ export class SagemakerSpaceNode extends AWSTreeNodeBase implements AWSResourceNo } private getContext() { - const status = generateSpaceStatus(this.spaceApp.Status, this.spaceApp.App?.Status) + const status = this.getStatus() if (status === 'Running' && this.spaceApp.SpaceSettingsSummary?.RemoteAccess === 'ENABLED') { return 'awsSagemakerSpaceRunningRemoteEnabledNode' + } else if (status === 'Running' && this.spaceApp.SpaceSettingsSummary?.RemoteAccess === 'DISABLED') { + return 'awsSagemakerSpaceRunningRemoteDisabledNode' + } else if (status === 'Stopped' && this.spaceApp.SpaceSettingsSummary?.RemoteAccess === 'ENABLED') { + return 'awsSagemakerSpaceStoppedRemoteEnabledNode' + } else if ( + status === 'Stopped' && + (!this.spaceApp.SpaceSettingsSummary?.RemoteAccess || + this.spaceApp.SpaceSettingsSummary?.RemoteAccess === 'DISABLED') + ) { + return 'awsSagemakerSpaceStoppedRemoteDisabledNode' } return 'awsSagemakerSpaceNode' } + + public get DomainSpaceKey(): string { + return this.spaceApp.DomainSpaceKey! + } + + public async refreshNode(): Promise { + await this.updateSpaceAppStatus() + await tryRefreshNode(this) + } +} + +export async function tryRefreshNode(node?: SagemakerSpaceNode) { + if (node) { + const n = node instanceof SagemakerSpaceNode ? node.parent : node + try { + await n.refreshNode() + } catch (e) { + getLogger().error('refreshNode failed: %s', (e as Error).message) + } + } } diff --git a/packages/core/src/awsService/sagemaker/model.ts b/packages/core/src/awsService/sagemaker/model.ts index 6f2fb1adbea..9acf481f2f0 100644 --- a/packages/core/src/awsService/sagemaker/model.ts +++ b/packages/core/src/awsService/sagemaker/model.ts @@ -6,7 +6,7 @@ // Disabled: detached server files cannot import vscode. /* eslint-disable no-restricted-imports */ import * as vscode from 'vscode' -import { sshAgentSocketVariable, startSshAgent } from '../../shared/extensions/ssh' +import { sshAgentSocketVariable, startSshAgent, startVscodeRemote } from '../../shared/extensions/ssh' import { createBoundProcess, ensureDependencies } from '../../shared/remoteSession' import { SshConfig } from '../../shared/sshConfig' import * as path from 'path' @@ -19,9 +19,30 @@ import { getSmSsmEnv, spawnDetachedServer } from './utils' import { getLogger } from '../../shared/logger/logger' import { DevSettings } from '../../shared/settings' import { ToolkitError } from '../../shared/errors' +import { SagemakerSpaceNode } from './explorer/sagemakerSpaceNode' +import { sleep } from '../../shared/utilities/timeoutUtils' const logger = getLogger('sagemaker') +export async function tryRemoteConnection(node: SagemakerSpaceNode, ctx: vscode.ExtensionContext) { + const spaceArn = (await node.getSpaceArn()) as string + const remoteEnv = await prepareDevEnvConnection(spaceArn, ctx, 'sm_lc') + + try { + await startVscodeRemote( + remoteEnv.SessionProcess, + remoteEnv.hostname, + '/home/sagemaker-user', + remoteEnv.vscPath, + 'sagemaker-user' + ) + } catch (err) { + getLogger().info( + `sm:OpenRemoteConnect: Unable to connect to target space with arn: ${await node.getAppArn()} error: ${err}` + ) + } +} + export async function prepareDevEnvConnection( appArn: string, ctx: vscode.ExtensionContext, @@ -37,10 +58,10 @@ export async function prepareDevEnvConnection( // Check timeout setting for remote SSH connections const remoteSshConfig = vscode.workspace.getConfiguration('remote.SSH') const current = remoteSshConfig.get('connectTimeout') - if (typeof current === 'number' && current < 300) { - await remoteSshConfig.update('connectTimeout', 300, vscode.ConfigurationTarget.Global) + if (typeof current === 'number' && current < 120) { + await remoteSshConfig.update('connectTimeout', 120, vscode.ConfigurationTarget.Global) void vscode.window.showInformationMessage( - 'Updated "remote.SSH.connectTimeout" to 300 seconds to improve stability.' + 'Updated "remote.SSH.connectTimeout" to 120 seconds to improve stability.' ) } @@ -118,6 +139,19 @@ export async function startLocalServer(ctx: vscode.ExtensionContext) { }) child.unref() + + // Wait for the info file to appear (timeout after 10 seconds) + const maxRetries = 20 + const delayMs = 500 + for (let i = 0; i < maxRetries; i++) { + if (await fs.existsFile(infoFilePath)) { + logger.debug('Detected server info file.') + return + } + await sleep(delayMs) + } + + throw new ToolkitError(`Timed out waiting for local server info file: ${infoFilePath}`) } interface LocalServerInfo { diff --git a/packages/core/src/shared/clients/sagemaker.ts b/packages/core/src/shared/clients/sagemaker.ts index 5d820d5b777..d24a0f74869 100644 --- a/packages/core/src/shared/clients/sagemaker.ts +++ b/packages/core/src/shared/clients/sagemaker.ts @@ -3,8 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ +import * as vscode from 'vscode' import { AppDetails, + CreateAppCommand, + CreateAppCommandInput, + CreateAppCommandOutput, + DeleteAppCommand, + DeleteAppCommandInput, + DeleteAppCommandOutput, DescribeAppCommand, DescribeAppCommandInput, DescribeAppCommandOutput, @@ -17,8 +24,12 @@ import { DescribeSpaceCommandOutput, ListAppsCommandInput, ListSpacesCommandInput, + ResourceSpec, SageMakerClient, SpaceDetails, + UpdateSpaceCommand, + UpdateSpaceCommandInput, + UpdateSpaceCommandOutput, paginateListApps, paginateListSpaces, } from '@amzn/sagemaker-client' @@ -28,9 +39,11 @@ import { ClientWrapper } from './clientWrapper' import { AsyncCollection } from '../utilities/asyncCollection' import { getDomainSpaceKey } from '../../awsService/sagemaker/utils' import { getLogger } from '../logger/logger' +import { ToolkitError } from '../errors' export interface SagemakerSpaceApp extends SpaceDetails { App?: AppDetails + DomainSpaceKey: string } export class SagemakerClient extends ClientWrapper { public constructor(public override readonly regionCode: string) { @@ -54,10 +67,87 @@ export class SagemakerClient extends ClientWrapper { public describeDomain(request: DescribeDomainCommandInput): Promise { return this.makeRequest(DescribeDomainCommand, request) } + public describeSpace(request: DescribeSpaceCommandInput): Promise { return this.makeRequest(DescribeSpaceCommand, request) } + public updateSpace(request: UpdateSpaceCommandInput): Promise { + return this.makeRequest(UpdateSpaceCommand, request) + } + + public createApp(request: CreateAppCommandInput): Promise { + return this.makeRequest(CreateAppCommand, request) + } + + public deleteApp(request: DeleteAppCommandInput): Promise { + return this.makeRequest(DeleteAppCommand, request) + } + + public async startSpace(spaceName: string, domainId: string) { + let spaceDetails + try { + spaceDetails = await this.describeSpace({ + DomainId: domainId, + SpaceName: spaceName, + }) + } catch (err) { + throw this.handleStartSpaceError(err) + } + + if (!spaceDetails.SpaceSettings?.RemoteAccess || spaceDetails.SpaceSettings?.RemoteAccess === 'DISABLED') { + try { + await this.updateSpace({ + DomainId: domainId, + SpaceName: spaceName, + SpaceSettings: { + RemoteAccess: 'ENABLED', + }, + }) + await this.waitForSpaceInService(spaceName, domainId) + } catch (err) { + throw this.handleStartSpaceError(err) + } + } + + const appType = spaceDetails.SpaceSettings?.AppType + if (appType !== 'JupyterLab' && appType !== 'CodeEditor') { + throw new ToolkitError(`Unsupported AppType "${appType}" for space "${spaceName}"`) + } + + const requestedResourceSpec = + appType === 'JupyterLab' + ? spaceDetails.SpaceSettings?.JupyterLabAppSettings?.DefaultResourceSpec + : spaceDetails.SpaceSettings?.CodeEditorAppSettings?.DefaultResourceSpec + + const fallbackResourceSpec: ResourceSpec = { + InstanceType: 'ml.t3.medium', + SageMakerImageArn: 'arn:aws:sagemaker:us-west-2:542918446943:image/sagemaker-distribution-cpu', + SageMakerImageVersionAlias: '3.2.0', + } + + const resourceSpec = requestedResourceSpec?.InstanceType ? requestedResourceSpec : fallbackResourceSpec + + const cleanedResourceSpec = + resourceSpec && 'EnvironmentArn' in resourceSpec + ? { ...resourceSpec, EnvironmentArn: undefined, EnvironmentVersionArn: undefined } + : resourceSpec + + const createAppRequest: CreateAppCommandInput = { + DomainId: domainId, + SpaceName: spaceName, + AppType: appType, + AppName: 'default', + ResourceSpec: cleanedResourceSpec, + } + + try { + await this.createApp(createAppRequest) + } catch (err) { + throw this.handleStartSpaceError(err) + } + } + public async fetchSpaceAppsAndDomains(): Promise< [Map, Map] > { @@ -73,7 +163,7 @@ export class SagemakerClient extends ClientWrapper { .filter((space) => !!space.DomainId && !!space.SpaceName) .map((space) => { const key = getDomainSpaceKey(space.DomainId || '', space.SpaceName || '') - return { ...space, App: appMap.get(key) } + return { ...space, App: appMap.get(key), DomainSpaceKey: key } }) .toMap((space) => getDomainSpaceKey(space.DomainId || '', space.SpaceName || '')) @@ -100,8 +190,76 @@ export class SagemakerClient extends ClientWrapper { ) return [filteredSpaceApps, domainsMap] - } catch (err: any) { + } catch (err) { + const error = err as Error getLogger().error('Failed to fetch space apps: %s', err) + if (error.name === 'AccessDeniedException') { + void vscode.window.showErrorMessage( + 'AccessDeniedException: You do not have permission to view spaces. Please contact your administrator', + { modal: false, detail: 'AWS Toolkit' } + ) + } + throw err + } + } + + private async waitForSpaceInService( + spaceName: string, + domainId: string, + maxRetries = 30, + intervalMs = 5000 + ): Promise { + for (let attempt = 0; attempt < maxRetries; attempt++) { + const result = await this.describeSpace({ SpaceName: spaceName, DomainId: domainId }) + + if (result.Status === 'InService') { + return + } + + await sleep(intervalMs) + } + + throw new ToolkitError( + `Timed out waiting for space "${spaceName}" in domain "${domainId}" to reach "InService" status.` + ) + } + + public async waitForAppInService( + domainId: string, + spaceName: string, + appType: string, + maxRetries = 30, + intervalMs = 5000 + ): Promise { + for (let attempt = 0; attempt < maxRetries; attempt++) { + const { Status } = await this.describeApp({ + DomainId: domainId, + SpaceName: spaceName, + AppType: appType as any, + AppName: 'default', + }) + + if (Status === 'InService') { + return + } + + if (['Failed', 'DeleteFailed'].includes(Status ?? '')) { + throw new ToolkitError(`App failed to start. Status: ${Status}`) + } + + await sleep(intervalMs) + } + + throw new ToolkitError(`Timed out waiting for app "${spaceName}" to reach "InService" status.`) + } + + private handleStartSpaceError(err: unknown) { + const error = err as Error + if (error.name === 'AccessDeniedException') { + throw new ToolkitError('You do not have permission to start spaces. Please contact your administrator', { + cause: error, + }) + } else { throw err } } diff --git a/packages/core/src/shared/telemetry/vscodeTelemetry.json b/packages/core/src/shared/telemetry/vscodeTelemetry.json index b28aeec4847..d075afa8e81 100644 --- a/packages/core/src/shared/telemetry/vscodeTelemetry.json +++ b/packages/core/src/shared/telemetry/vscodeTelemetry.json @@ -241,6 +241,33 @@ } ], "metrics": [ + { + "name": "sagemaker_openRemoteConnection", + "description": "Perform a connection to a SageMaker Space", + "metadata": [ + { + "type": "result" + } + ] + }, + { + "name": "sagemaker_stopSpace", + "description": "Stop a SageMaker Space", + "metadata": [ + { + "type": "result" + } + ] + }, + { + "name": "sagemaker_filterSpaces", + "description": "Filter SageMaker Spaces", + "metadata": [ + { + "type": "result" + } + ] + }, { "name": "amazonq_didSelectProfile", "description": "Emitted after the user's Q Profile has been set, whether the user was prompted with a dialog, or a profile was automatically assigned after signing in.", diff --git a/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSession.test.ts b/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSession.test.ts index fe0fcb5542b..1e09fdbc8da 100644 --- a/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSession.test.ts +++ b/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSession.test.ts @@ -9,6 +9,7 @@ import assert from 'assert' import { handleGetSession } from '../../../../../awsService/sagemaker/detached-server/routes/getSession' import * as credentials from '../../../../../awsService/sagemaker/detached-server/credentials' import * as utils from '../../../../../awsService/sagemaker/detached-server/utils' +import * as errorPage from '../../../../../awsService/sagemaker/detached-server/errorPage' describe('handleGetSession', () => { let req: Partial @@ -24,6 +25,7 @@ describe('handleGetSession', () => { writeHead: resWriteHead, end: resEnd, } + sinon.stub(errorPage, 'openErrorPage') }) it('responds with 400 if connection_identifier is missing', async () => { diff --git a/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts b/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts index d412b5b9b4c..a0a0f807b73 100644 --- a/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts +++ b/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts @@ -46,10 +46,8 @@ describe('sagemakerParentNode', function () { afterEach(function () { fetchSpaceAppsAndDomainsStub.restore() getCallerIdentityStub.restore() - sinon.restore() - }) - - after(function () { + testNode.pollingSet.clear() + testNode.pollingSet.clearTimer() sinon.restore() }) @@ -78,6 +76,7 @@ describe('sagemakerParentNode', function () { DomainId: 'domain1', OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, Status: 'InService', + DomainSpaceKey: 'domain1__name1', }, ], [ @@ -87,6 +86,7 @@ describe('sagemakerParentNode', function () { DomainId: 'domain2', OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, Status: 'InService', + DomainSpaceKey: 'domain2__name2', }, ], ]) @@ -109,6 +109,50 @@ describe('sagemakerParentNode', function () { assert.strictEqual(childNodes[1].label, 'name2 (Stopped)', 'Unexpected node label') }) + it('adds pending nodes to polling nodes set', async function () { + const spaceAppsMap: Map = new Map([ + [ + 'domain1__name3', + { + SpaceName: 'name3', + DomainId: 'domain1', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, + Status: 'InService', + DomainSpaceKey: 'domain1__name3', + App: { + Status: 'InService', + }, + }, + ], + [ + 'domain2__name4', + { + SpaceName: 'name4', + DomainId: 'domain2', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, + Status: 'InService', + DomainSpaceKey: 'domain2__name4', + App: { + Status: 'Pending', + }, + }, + ], + ]) + + fetchSpaceAppsAndDomainsStub.returns(Promise.resolve([spaceAppsMap, domainsMap])) + getCallerIdentityStub.returns( + Promise.resolve({ + UserId: 'test-userId', + Account: '123456789012', + Arn: 'arn:aws:iam::123456789012:user/test-user', + }) + ) + + await testNode.updateChildren() + assert.strictEqual(testNode.pollingSet.size, 1) + fetchSpaceAppsAndDomainsStub.restore() + }) + it('filters spaces owned by user profiles that match the IAM user', async function () { const spaceAppsMap: Map = new Map([ [ @@ -118,6 +162,7 @@ describe('sagemakerParentNode', function () { DomainId: 'domain1', OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, Status: 'InService', + DomainSpaceKey: 'domain1__name1', }, ], [ @@ -127,6 +172,7 @@ describe('sagemakerParentNode', function () { DomainId: 'domain2', OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, Status: 'InService', + DomainSpaceKey: 'domain2__name2', }, ], ]) @@ -157,6 +203,7 @@ describe('sagemakerParentNode', function () { DomainId: 'domain1', OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, Status: 'InService', + DomainSpaceKey: 'domain1__name1', }, ], [ @@ -166,6 +213,7 @@ describe('sagemakerParentNode', function () { DomainId: 'domain2', OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, Status: 'InService', + DomainSpaceKey: 'domain2__name2', }, ], ]) @@ -196,6 +244,7 @@ describe('sagemakerParentNode', function () { DomainId: 'domain1', OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, Status: 'InService', + DomainSpaceKey: 'domain1__name1', }, ], [ @@ -205,6 +254,7 @@ describe('sagemakerParentNode', function () { DomainId: 'domain2', OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, Status: 'InService', + DomainSpaceKey: 'domain2__name2', }, ], ]) @@ -234,6 +284,7 @@ describe('sagemakerParentNode', function () { OwnershipSettingsSummary: { OwnerUserProfileName: ownerName, }, + DomainSpaceKey: 'domain1__name1', }) beforeEach(function () { diff --git a/packages/core/src/test/awsService/sagemaker/explorer/sagemakerSpaceNode.test.ts b/packages/core/src/test/awsService/sagemaker/explorer/sagemakerSpaceNode.test.ts index 37e48f0c1f4..57b4d7a80c6 100644 --- a/packages/core/src/test/awsService/sagemaker/explorer/sagemakerSpaceNode.test.ts +++ b/packages/core/src/test/awsService/sagemaker/explorer/sagemakerSpaceNode.test.ts @@ -10,6 +10,7 @@ import { AppType } from '@aws-sdk/client-sagemaker' import { SagemakerClient, SagemakerSpaceApp } from '../../../../shared/clients/sagemaker' import { SagemakerSpaceNode } from '../../../../awsService/sagemaker/explorer/sagemakerSpaceNode' import { SagemakerParentNode } from '../../../../awsService/sagemaker/explorer/sagemakerParentNode' +import { PollingSet } from '../../../../shared/utilities/pollingSet' describe('SagemakerSpaceNode', function () { const testRegion = 'testRegion' @@ -17,11 +18,9 @@ describe('SagemakerSpaceNode', function () { let testParent: SagemakerParentNode let testSpaceApp: SagemakerSpaceApp let describeAppStub: sinon.SinonStub + let testSpaceAppNode: SagemakerSpaceNode beforeEach(function () { - client = new SagemakerClient(testRegion) - testParent = new SagemakerParentNode(testRegion, client) - testSpaceApp = { SpaceName: 'TestSpace', DomainId: 'd-12345', @@ -30,9 +29,15 @@ describe('SagemakerSpaceNode', function () { OwnershipSettingsSummary: { OwnerUserProfileName: 'test-user' }, SpaceSharingSettingsSummary: { SharingType: 'Private' }, Status: 'InService', + DomainSpaceKey: '123', } + sinon.stub(PollingSet.prototype, 'add') + client = new SagemakerClient(testRegion) + testParent = new SagemakerParentNode(testRegion, client) + describeAppStub = sinon.stub(SagemakerClient.prototype, 'describeApp') + testSpaceAppNode = new SagemakerSpaceNode(testParent, client, testRegion, testSpaceApp) }) afterEach(function () { @@ -53,6 +58,7 @@ describe('SagemakerSpaceNode', function () { SpaceName: undefined, DomainId: 'domainId', Status: 'Failed', + DomainSpaceKey: '123', } const node = new SagemakerSpaceNode(testParent, client, testRegion, partialApp) @@ -77,4 +83,11 @@ describe('SagemakerSpaceNode', function () { SpaceName: 'TestSpace', }) }) + + it('updates status with new spaceApp', async function () { + const newStatus = 'Starting' + const newSpaceApp = { ...testSpaceApp, App: { AppName: 'TestApp', Status: 'Pending' } } as SagemakerSpaceApp + testSpaceAppNode.updateSpace(newSpaceApp) + assert.strictEqual(testSpaceAppNode.getStatus(), newStatus) + }) }) diff --git a/packages/core/src/test/awsService/sagemaker/model.test.ts b/packages/core/src/test/awsService/sagemaker/model.test.ts index 09b287ad964..892baf2f77b 100644 --- a/packages/core/src/test/awsService/sagemaker/model.test.ts +++ b/packages/core/src/test/awsService/sagemaker/model.test.ts @@ -30,45 +30,34 @@ describe('SageMaker Model', () => { sandbox.restore() }) - it('calls stopLocalServer and spawns child process with correct args', async function () { - const storagePath = ctx.globalStorageUri.fsPath - const serverPath = path.join( - ctx.extensionPath, - 'dist', - 'src', - 'awsService', - 'sagemaker', - 'detached-server', - 'server.js' - ) - const infoFilePath = path.join(storagePath, 'sagemaker-local-server-info.json') + it('waits for info file and starts server', async function () { + // Simulate the file doesn't exist initially, then appears on 3rd check + const existsStub = sandbox.stub(fs, 'existsFile') + existsStub.onCall(0).resolves(false) + existsStub.onCall(1).resolves(false) + existsStub.onCall(2).resolves(true) - // Stubs - const stopLocalServerStub = sandbox.stub().resolves() - sandbox.replace(require('../../../awsService/sagemaker/model'), 'stopLocalServer', stopLocalServerStub) - sandbox.stub(DevSettings.instance, 'get').returns({ sagemaker: 'https://endpoint' }) sandbox.stub(require('fs'), 'openSync').returns(42) - const unrefStub = sandbox.stub() - const spawnStub = sandbox.stub().returns({ unref: unrefStub }) + const stopStub = sandbox.stub().resolves() + sandbox.replace(require('../../../awsService/sagemaker/model'), 'stopLocalServer', stopStub) + + const spawnStub = sandbox.stub().returns({ unref: sandbox.stub() }) sandbox.replace(require('../../../awsService/sagemaker/utils'), 'spawnDetachedServer', spawnStub) - await startLocalServer(ctx) + sandbox.stub(DevSettings.instance, 'get').returns({ sagemaker: 'https://fake-endpoint' }) - // Validate spawn args - sinon.assert.calledOnce(spawnStub) - const spawnArgs = spawnStub.firstCall.args - const expectedEnv = spawnArgs[2].env + await startLocalServer(ctx) - assert.strictEqual(spawnArgs[0], process.execPath) - assert.deepStrictEqual(spawnArgs[1], [serverPath]) - assert.strictEqual(spawnArgs[2].cwd, path.dirname(serverPath)) - assert.strictEqual(spawnArgs[2].detached, true) - assert.strictEqual(expectedEnv.SAGEMAKER_ENDPOINT, 'https://endpoint') - assert.strictEqual(expectedEnv.SAGEMAKER_LOCAL_SERVER_FILE_PATH, infoFilePath) + sinon.assert.called(spawnStub) + sinon.assert.calledWith( + spawnStub, + process.execPath, + [ctx.asAbsolutePath('dist/src/awsService/sagemaker/detached-server/server.js')], + sinon.match.any + ) - sinon.assert.calledOnce(unrefStub) - assertLogsContain('local server logs at', false, 'info') + assert.ok(existsStub.callCount >= 3, 'should have retried for file existence') }) }) diff --git a/packages/core/src/test/shared/clients/sagemakerClient.test.ts b/packages/core/src/test/shared/clients/sagemakerClient.test.ts index e95fae15989..94a07dd32eb 100644 --- a/packages/core/src/test/shared/clients/sagemakerClient.test.ts +++ b/packages/core/src/test/shared/clients/sagemakerClient.test.ts @@ -103,4 +103,108 @@ describe('SagemakerClient.fetchSpaceAppsAndDomains', function () { assert.strictEqual(spaceApps.get(spaceAppKey2)?.App, undefined) assert.strictEqual(spaceApps.get(spaceAppKey3)?.App, undefined) }) + + describe('SagemakerClient.startSpace', function () { + const region = 'test-region' + let client: SagemakerClient + let describeSpaceStub: sinon.SinonStub + let updateSpaceStub: sinon.SinonStub + let waitForSpaceStub: sinon.SinonStub + let createAppStub: sinon.SinonStub + + beforeEach(function () { + client = new SagemakerClient(region) + describeSpaceStub = sinon.stub(client, 'describeSpace') + updateSpaceStub = sinon.stub(client, 'updateSpace') + waitForSpaceStub = sinon.stub(client as any, 'waitForSpaceInService') + createAppStub = sinon.stub(client, 'createApp') + }) + + afterEach(function () { + sinon.restore() + }) + + it('enables remote access and starts the app', async function () { + describeSpaceStub.resolves({ + SpaceSettings: { + RemoteAccess: 'DISABLED', + AppType: 'CodeEditor', + CodeEditorAppSettings: { + DefaultResourceSpec: { + InstanceType: 'ml.t3.medium', + SageMakerImageArn: 'arn:aws:sagemaker:us-west-2:img', + SageMakerImageVersionAlias: '1.0.0', + }, + }, + }, + }) + + updateSpaceStub.resolves({}) + waitForSpaceStub.resolves() + createAppStub.resolves({}) + + await client.startSpace('my-space', 'my-domain') + + sinon.assert.calledOnce(updateSpaceStub) + sinon.assert.calledOnce(waitForSpaceStub) + sinon.assert.calledOnce(createAppStub) + }) + + it('skips enabling remote access if already enabled', async function () { + describeSpaceStub.resolves({ + SpaceSettings: { + RemoteAccess: 'ENABLED', + AppType: 'CodeEditor', + CodeEditorAppSettings: {}, + }, + }) + + createAppStub.resolves({}) + + await client.startSpace('my-space', 'my-domain') + + sinon.assert.notCalled(updateSpaceStub) + sinon.assert.notCalled(waitForSpaceStub) + sinon.assert.calledOnce(createAppStub) + }) + + it('throws error on unsupported app type', async function () { + describeSpaceStub.resolves({ + SpaceSettings: { + RemoteAccess: 'ENABLED', + AppType: 'Studio', + }, + }) + + await assert.rejects(client.startSpace('my-space', 'my-domain'), /Unsupported AppType "Studio"/) + }) + + it('uses fallback resource spec when none provided', async function () { + describeSpaceStub.resolves({ + SpaceSettings: { + RemoteAccess: 'ENABLED', + AppType: 'JupyterLab', + JupyterLabAppSettings: {}, + }, + }) + + createAppStub.resolves({}) + + await client.startSpace('my-space', 'my-domain') + + sinon.assert.calledOnceWithExactly( + createAppStub, + sinon.match.hasNested('ResourceSpec.InstanceType', 'ml.t3.medium') + ) + }) + + it('handles AccessDeniedException gracefully', async function () { + describeSpaceStub.rejects({ name: 'AccessDeniedException', message: 'no access' }) + + await assert.rejects( + client.startSpace('my-space', 'my-domain'), + /You do not have permission to start spaces/ + ) + }) + }) }) diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index 093d7ce6c73..b507f0093cd 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -1456,9 +1456,14 @@ ], "view/item/context": [ { - "command": "aws.sagemaker.openRemoteConnection", + "command": "aws.sagemaker.stopSpace", "group": "inline@0", - "when": "viewItem =~ /^(awsSagemakerSpaceRunningRemoteEnabledNode)$/" + "when": "viewItem =~ /^(awsSagemakerSpaceRunningRemoteEnabledNode|awsSagemakerSpaceRunningRemoteDisabledNode)$/" + }, + { + "command": "aws.sagemaker.openRemoteConnection", + "group": "inline@1", + "when": "viewItem =~ /^(awsSagemakerSpaceRunningRemoteEnabledNode|awsSagemakerSpaceStoppedRemoteEnabledNode|awsSagemakerSpaceStoppedRemoteDisabledNode)$/" }, { "command": "_aws.toolkit.notifications.dismiss", @@ -2599,7 +2604,7 @@ }, { "command": "aws.sagemaker.openRemoteConnection", - "title": "Connect to Sagemaker Space", + "title": "Connect to SageMaker Space", "icon": "$(remote-explorer)", "category": "%AWS.title%", "enablement": "isCloud9 || !aws.isWebExtHost", @@ -2609,6 +2614,18 @@ } } }, + { + "command": "aws.sagemaker.stopSpace", + "title": "Stop SageMaker Space", + "icon": "$(debug-stop)", + "category": "%AWS.title%", + "enablement": "isCloud9 || !aws.isWebExtHost", + "cloud9": { + "cn": { + "category": "%AWS.title.cn%" + } + } + }, { "command": "aws.ec2.startInstance", "title": "%AWS.command.ec2.startInstance%", From d6c50b33ab16caaef00da673bd2e8b81ce1ca3e4 Mon Sep 17 00:00:00 2001 From: aws-asolidu Date: Tue, 1 Jul 2025 12:51:41 -0700 Subject: [PATCH 05/12] feat(sagemaker-connect): Add remote upfront filtering (#2140) ## Problem - When the Toolkit connects to a remote space, it should automatically apply the space filter based on the space profile owner. ## Solution 1. Read the metadata file to extract the space name and domain ID. 2. Use the describeSpace API to retrieve the profile name. 3. Apply filtering using the domain ID and profile name as keys. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- .../sagemaker/explorer/sagemakerParentNode.ts | 24 +++--- .../src/awsService/sagemaker/remoteUtils.ts | 47 ++++++++++++ .../core/src/awsService/sagemaker/utils.ts | 16 ++-- .../awsService/sagemaker/remoteUtils.test.ts | 74 +++++++++++++++++++ 4 files changed, 143 insertions(+), 18 deletions(-) create mode 100644 packages/core/src/awsService/sagemaker/remoteUtils.ts create mode 100644 packages/core/src/test/awsService/sagemaker/remoteUtils.test.ts diff --git a/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts b/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts index ced97be0565..193a11cf972 100644 --- a/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts +++ b/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts @@ -16,8 +16,9 @@ import { updateInPlace } from '../../../shared/utilities/collectionUtils' import { isRemoteWorkspace } from '../../../shared/vscode/env' import { SagemakerConstants } from './constants' import { SagemakerSpaceNode } from './sagemakerSpaceNode' -import { getDomainSpaceKey, getDomainUserProfileKey, getRemoteAppMetadata, getSpaceAppsForUserProfile } from '../utils' +import { getDomainSpaceKey, getDomainUserProfileKey, getSpaceAppsForUserProfile } from '../utils' import { PollingSet } from '../../../shared/utilities/pollingSet' +import { getRemoteAppMetadata } from '../remoteUtils' export const parentContextValue = 'awsSagemakerParentNode' @@ -115,10 +116,12 @@ export class SagemakerParentNode extends AWSTreeNodeBase { } public async getRemoteSelectedDomainUsers(): Promise { - const remoteAppMetadata = getRemoteAppMetadata() - const userProfilePrefix = `${remoteAppMetadata.UserProfileName}-` - - return getSpaceAppsForUserProfile([...this.spaceApps.values()], userProfilePrefix) + const remoteAppMetadata = await getRemoteAppMetadata() + return getSpaceAppsForUserProfile( + [...this.spaceApps.values()], + remoteAppMetadata.UserProfileName, + remoteAppMetadata.DomainId + ) } public async getDefaultSelectedDomainUsers(): Promise { @@ -136,12 +139,13 @@ export class SagemakerParentNode extends AWSTreeNodeBase { const defaultSelectedDomainUsers = await this.getDefaultSelectedDomainUsers() - // Get selectedDomainUsers from globalState. If it doesn't exist, then default to defaultSelectedDomainUsers - const selectedDomainUsers = new Set( - selectedDomainUsersMap?.get(this.callerIdentity?.Arn || '') || defaultSelectedDomainUsers - ) + const cachedDomainUsers = selectedDomainUsersMap.get(this.callerIdentity.Arn || '') - return selectedDomainUsers + if (cachedDomainUsers && cachedDomainUsers.length > 0) { + return new Set(cachedDomainUsers) + } else { + return new Set(defaultSelectedDomainUsers) + } } public saveSelectedDomainUsers(selectedDomainUsers: string[]) { diff --git a/packages/core/src/awsService/sagemaker/remoteUtils.ts b/packages/core/src/awsService/sagemaker/remoteUtils.ts new file mode 100644 index 00000000000..ffd7210eea1 --- /dev/null +++ b/packages/core/src/awsService/sagemaker/remoteUtils.ts @@ -0,0 +1,47 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { fs } from '../../shared/fs/fs' +import { SagemakerClient } from '../../shared/clients/sagemaker' +import { parseRegionFromArn, RemoteAppMetadata } from './utils' +import { getLogger } from '../../shared/logger/logger' + +export async function getRemoteAppMetadata(): Promise { + try { + const metadataPath = '/opt/ml/metadata/resource-metadata.json' + const metadataContent = await fs.readFileText(metadataPath) + const metadata = JSON.parse(metadataContent) + + const domainId = metadata.DomainId + const spaceName = metadata.SpaceName + + if (!domainId || !spaceName) { + throw new Error('DomainId or SpaceName not found in metadata file') + } + + const region = parseRegionFromArn(metadata.ResourceArn) + + const client = new SagemakerClient(region) + const spaceDetails = await client.describeSpace({ DomainId: domainId, SpaceName: spaceName }) + + const userProfileName = spaceDetails.OwnershipSettings?.OwnerUserProfileName + + if (!userProfileName) { + throw new Error('OwnerUserProfileName not found in space details') + } + + return { + DomainId: domainId, + UserProfileName: userProfileName, + } + } catch (error) { + const logger = getLogger() + logger.error(`getRemoteAppMetadata: Failed to read metadata file, using fallback values: ${error}`) + return { + DomainId: '', + UserProfileName: '', + } + } +} diff --git a/packages/core/src/awsService/sagemaker/utils.ts b/packages/core/src/awsService/sagemaker/utils.ts index bc0f30a05fa..602cb17f6ed 100644 --- a/packages/core/src/awsService/sagemaker/utils.ts +++ b/packages/core/src/awsService/sagemaker/utils.ts @@ -60,16 +60,16 @@ export interface RemoteAppMetadata { UserProfileName: string } -export function getRemoteAppMetadata(): RemoteAppMetadata { - return { - DomainId: 'd-abcdefg123456', - UserProfileName: 'dernewtz-jorus', - } -} - -export function getSpaceAppsForUserProfile(spaceApps: SagemakerSpaceApp[], userProfilePrefix: string): string[] { +export function getSpaceAppsForUserProfile( + spaceApps: SagemakerSpaceApp[], + userProfilePrefix: string, + domainId?: string +): string[] { return spaceApps.reduce((result: string[], app: SagemakerSpaceApp) => { if (app.OwnershipSettingsSummary?.OwnerUserProfileName?.startsWith(userProfilePrefix)) { + if (domainId && app.DomainId !== domainId) { + return result + } result.push( getDomainUserProfileKey(app.DomainId || '', app.OwnershipSettingsSummary?.OwnerUserProfileName || '') ) diff --git a/packages/core/src/test/awsService/sagemaker/remoteUtils.test.ts b/packages/core/src/test/awsService/sagemaker/remoteUtils.test.ts new file mode 100644 index 00000000000..b2e1071e0db --- /dev/null +++ b/packages/core/src/test/awsService/sagemaker/remoteUtils.test.ts @@ -0,0 +1,74 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as sinon from 'sinon' +import * as assert from 'assert' +import { getRemoteAppMetadata } from '../../../awsService/sagemaker/remoteUtils' +import { fs } from '../../../shared/fs/fs' +import { SagemakerClient } from '../../../shared/clients/sagemaker' + +describe('getRemoteAppMetadata', function () { + let sandbox: sinon.SinonSandbox + let fsStub: sinon.SinonStub + let parseRegionStub: sinon.SinonStub + let describeSpaceStub: sinon.SinonStub + let loggerStub: sinon.SinonStub + + const mockMetadata = { + AppType: 'JupyterLab', + DomainId: 'd-f0lwireyzpjp', + SpaceName: 'test-ae-3', + ExecutionRoleArn: 'arn:aws:iam::177118115371:role/service-role/AmazonSageMaker-ExecutionRole-20250415T091941', + ResourceArn: 'arn:aws:sagemaker:us-west-2:177118115371:app/d-f0lwireyzpjp/test-ae-3/JupyterLab/default', + ResourceName: 'default', + AppImageVersion: '', + ResourceArnCaseSensitive: + 'arn:aws:sagemaker:us-west-2:177118115371:app/d-f0lwireyzpjp/test-ae-3/JupyterLab/default', + IpAddressType: 'ipv4', + } + + const mockSpaceDetails = { + OwnershipSettings: { + OwnerUserProfileName: 'test-user-profile', + }, + } + + beforeEach(() => { + sandbox = sinon.createSandbox() + fsStub = sandbox.stub(fs, 'readFileText') + parseRegionStub = sandbox.stub().returns('us-west-2') + sandbox.replace(require('../../../awsService/sagemaker/utils'), 'parseRegionFromArn', parseRegionStub) + + describeSpaceStub = sandbox.stub().resolves(mockSpaceDetails) + sandbox.stub(SagemakerClient.prototype, 'describeSpace').callsFake(describeSpaceStub) + + loggerStub = sandbox.stub().returns({ + error: sandbox.stub(), + }) + sandbox.replace(require('../../../shared/logger/logger'), 'getLogger', loggerStub) + }) + + afterEach(() => { + sandbox.restore() + }) + + it('successfully reads metadata file and returns remote app metadata', async function () { + fsStub.resolves(JSON.stringify(mockMetadata)) + + const result = await getRemoteAppMetadata() + + assert.deepStrictEqual(result, { + DomainId: 'd-f0lwireyzpjp', + UserProfileName: 'test-user-profile', + }) + + sinon.assert.calledWith(fsStub, '/opt/ml/metadata/resource-metadata.json') + sinon.assert.calledWith(parseRegionStub, mockMetadata.ResourceArn) + sinon.assert.calledWith(describeSpaceStub, { + DomainId: 'd-f0lwireyzpjp', + SpaceName: 'test-ae-3', + }) + }) +}) From 3cff4e74472da15fb61d49dd7b8413cf435de1a5 Mon Sep 17 00:00:00 2001 From: Alvin Solidum Date: Tue, 1 Jul 2025 23:28:35 -0700 Subject: [PATCH 06/12] rebuild package-lock.json --- package-lock.json | 3422 ++++++++++++++++++++------------------------- 1 file changed, 1494 insertions(+), 1928 deletions(-) diff --git a/package-lock.json b/package-lock.json index 84d9dc28487..93699267e33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,10 +70,10 @@ "resolved": "src.gen/@amzn/codewhisperer-streaming", "link": true }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client": { + "node_modules/@amzn/sagemaker-client": { "version": "1.0.0", - "resolved": "file:../../Downloads/@amzn-rhinestone-monarch-sagemaker-client-1.0.0.tgz", - "integrity": "sha512-RrI07M0PcPKpZjui5nLpFdsrHvsPs2t6AB8ojJDt3epN2iu4Zi89056k5BJ12gexKeEcdsMDiNu3CQleXNy2EQ==", + "resolved": "file:src.gen/@amzn/sagemaker-client/1.0.0.tgz", + "integrity": "sha512-rNMUzeACaCiIqR8aQo3G99xR+Qy6zhbGi9+6XRG5proUKetO3584dclmSnIUvDvzLWosFcl4GyP8tFqiahc6Jg==", "dependencies": { "@aws-crypto/sha256-browser": "3.0.0", "@aws-crypto/sha256-js": "3.0.0", @@ -116,7 +116,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/sha256-browser": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-browser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", @@ -131,12 +131,12 @@ "tslib": "^1.11.1" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/sha256-js": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-js": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", @@ -146,12 +146,12 @@ "tslib": "^1.11.1" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-js/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/supports-web-crypto": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/supports-web-crypto": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", @@ -159,12 +159,12 @@ "tslib": "^1.11.1" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/util": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/util": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", @@ -174,12 +174,12 @@ "tslib": "^1.11.1" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-crypto/util/node_modules/tslib": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/util/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/client-sso": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/client-sso": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.363.0.tgz", "integrity": "sha512-PZ+HfKSgS4hlMnJzG+Ev8/mgHd/b/ETlJWPSWjC/f2NwVoBQkBnqHjdyEx7QjF6nksJozcVh5Q+kkYLKc/QwBQ==", @@ -222,7 +222,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/client-sso-oidc": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/client-sso-oidc": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.363.0.tgz", "integrity": "sha512-V3Ebiq/zNtDS/O92HUWGBa7MY59RYSsqWd+E0XrXv6VYTA00RlMTbNcseivNgp2UghOgB9a20Nkz6EqAeIN+RQ==", @@ -265,7 +265,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/client-sts": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/client-sts": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.363.0.tgz", "integrity": "sha512-0jj14WvBPJQ8xr72cL0mhlmQ90tF0O0wqXwSbtog6PsC8+KDE6Yf+WsxsumyI8E5O8u3eYijBL+KdqG07F/y/w==", @@ -312,7 +312,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/credential-provider-env": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-env": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.363.0.tgz", "integrity": "sha512-VAQ3zITT2Q0acht0HezouYnMFKZ2vIOa20X4zQA3WI0HfaP4D6ga6KaenbDcb/4VFiqfqiRHfdyXHP0ThcDRMA==", @@ -326,7 +326,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/credential-provider-ini": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-ini": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.363.0.tgz", "integrity": "sha512-ZYN+INoqyX5FVC3rqUxB6O8nOWkr0gHRRBm1suoOlmuFJ/WSlW/uUGthRBY5x1AQQnBF8cpdlxZzGHd41lFVNw==", @@ -346,7 +346,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/credential-provider-node": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-node": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.363.0.tgz", "integrity": "sha512-C1qXFIN2yMxD6pGgug0vR1UhScOki6VqdzuBHzXZAGu7MOjvgHNdscEcb3CpWnITHaPL2ztkiw75T1sZ7oIgQg==", @@ -367,7 +367,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/credential-provider-process": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-process": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.363.0.tgz", "integrity": "sha512-fOKAINU7Rtj2T8pP13GdCt+u0Ml3gYynp8ki+1jMZIQ+Ju/MdDOqZpKMFKicMn3Z1ttUOgqr+grUdus6z8ceBQ==", @@ -382,7 +382,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/credential-provider-sso": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-sso": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.363.0.tgz", "integrity": "sha512-5RUZ5oM0lwZSo3EehT0dXggOjgtxFogpT3cZvoLGtIwrPBvm8jOQPXQUlaqCj10ThF1sYltEyukz/ovtDwYGew==", @@ -399,7 +399,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/credential-provider-web-identity": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.363.0.tgz", "integrity": "sha512-Z6w7fjgy79pAax580wdixbStQw10xfyZ+hOYLcPudoYFKjoNx0NQBejg5SwBzCF/HQL23Ksm9kDfbXDX9fkPhA==", @@ -413,7 +413,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/middleware-host-header": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-host-header": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.363.0.tgz", "integrity": "sha512-FobpclDCf5Y1ueyJDmb9MqguAdPssNMlnqWQpujhYVABq69KHu73fSCWSauFPUrw7YOpV8kG1uagDF0POSxHzA==", @@ -427,7 +427,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/middleware-logger": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-logger": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.363.0.tgz", "integrity": "sha512-SSGgthScYnFGTOw8EzbkvquqweFmvn7uJihkpFekbtBNGC/jGOGO+8ziHjTQ8t/iI/YKubEwv+LMi0f77HKSEg==", @@ -440,7 +440,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/middleware-recursion-detection": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.363.0.tgz", "integrity": "sha512-MWD/57QgI/N7fG8rtzDTUdSqNpYohQfgj9XCFAoVeI/bU4usrkOrew43L4smJG4XrDxlNT8lSJlDtd64tuiUZA==", @@ -454,7 +454,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/middleware-user-agent": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-user-agent": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.363.0.tgz", "integrity": "sha512-ri8YaQvXP6odteVTMfxPqFR26Q0h9ejtqhUDv47P34FaKXedEM4nC6ix6o+5FEYj6l8syGyktftZ5O70NoEhug==", @@ -469,7 +469,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/token-providers": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/token-providers": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.363.0.tgz", "integrity": "sha512-6+0aJ1zugNgsMmhTtW2LBWxOVSaXCUk2q3xyTchSXkNzallYaRiZMRkieW+pKNntnu0g5H1T0zyfCO0tbXwxEA==", @@ -485,7 +485,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/types": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/types": { "version": "3.357.0", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.357.0.tgz", "integrity": "sha512-/riCRaXg3p71BeWnShrai0y0QTdXcouPSM0Cn1olZbzTf7s71aLEewrc96qFrL70XhY4XvnxMpqQh+r43XIL3g==", @@ -496,7 +496,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/util-endpoints": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/util-endpoints": { "version": "3.357.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.357.0.tgz", "integrity": "sha512-XHKyS5JClT9su9hDif715jpZiWHQF9gKZXER8tW0gOizU3R9cyWc9EsJ2BRhFNhi7nt/JF/CLUEc5qDx3ETbUw==", @@ -508,7 +508,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/util-user-agent-browser": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.363.0.tgz", "integrity": "sha512-fk9ymBUIYbxiGm99Cn+kAAXmvMCWTf/cHAcB79oCXV4ELXdPa9lN5xQhZRFNxLUeXG4OAMEuCAUUuZEj8Fnc1Q==", @@ -519,7 +519,7 @@ "tslib": "^2.5.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@aws-sdk/util-user-agent-node": { + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/util-user-agent-node": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.363.0.tgz", "integrity": "sha512-Fli/dvgGA9hdnQUrYb1//wNSFlK2jAfdJcfNXA6SeBYzSeH5pVGYF4kXF0FCdnMA3Fef+Zn1zAP/hw9v8VJHWQ==", @@ -541,7 +541,7 @@ } } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/abort-controller": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/abort-controller": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-1.1.0.tgz", "integrity": "sha512-5imgGUlZL4dW4YWdMYAKLmal9ny/tlenM81QZY7xYyb76z9Z/QOg7oM5Ak9HQl8QfFTlGVWwcMXl+54jroRgEQ==", @@ -553,7 +553,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/config-resolver": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/config-resolver": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-1.1.0.tgz", "integrity": "sha512-7WD9eZHp46BxAjNGHJLmxhhyeiNWkBdVStd7SUJPUZqQGeIO/REtIrcIfKUfdiHTQ9jyu2SYoqvzqqaFc6987w==", @@ -567,7 +567,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/credential-provider-imds": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/credential-provider-imds": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-1.1.0.tgz", "integrity": "sha512-kUMOdEu3RP6ozH0Ga8OeMP8gSkBsK1UqZZKyPLFnpZHrtZuHSSt7M7gsHYB/bYQBZAo3o7qrGmRty3BubYtYxQ==", @@ -582,7 +582,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/fetch-http-handler": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/fetch-http-handler": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-1.1.0.tgz", "integrity": "sha512-N22C9R44u5WGlcY+Wuv8EXmCAq62wWwriRAuoczMEwAIjPbvHSthyPSLqI4S7kAST1j6niWg8kwpeJ3ReAv3xg==", @@ -594,7 +594,7 @@ "tslib": "^2.5.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/hash-node": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/hash-node": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-1.1.0.tgz", "integrity": "sha512-yiNKDGMzrQjnpnbLfkYKo+HwIxmBAsv0AI++QIJwvhfkLpUTBylelkv6oo78/YqZZS6h+bGfl0gILJsKE2wAKQ==", @@ -608,7 +608,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/invalid-dependency": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/invalid-dependency": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-1.1.0.tgz", "integrity": "sha512-h2rXn68ClTwzPXYzEUNkz+0B/A0Hz8YdFNTiEwlxkwzkETGKMxmsrQGFXwYm3jd736R5vkXcClXz1ddKrsaBEQ==", @@ -617,7 +617,7 @@ "tslib": "^2.5.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/is-array-buffer": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/is-array-buffer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-1.1.0.tgz", "integrity": "sha512-twpQ/n+3OWZJ7Z+xu43MJErmhB/WO/mMTnqR6PwWQShvSJ/emx5d1N59LQZk6ZpTAeuRWrc+eHhkzTp9NFjNRQ==", @@ -628,7 +628,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/middleware-content-length": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-content-length": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-1.1.0.tgz", "integrity": "sha512-iNxwhZ7Xc5+LjeDElEOi/Nh8fFsc9Dw9+5w7h7/GLFIU0RgAwBJuJtcP1vNTOwzW4B3hG+gRu8sQLqA9OEaTwA==", @@ -641,7 +641,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/middleware-endpoint": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-endpoint": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-1.1.0.tgz", "integrity": "sha512-PvpazNjVpxX2ICrzoFYCpFnjB39DKCpZds8lRpAB3p6HGrx6QHBaNvOzVhJGBf0jcAbfCdc5/W0n9z8VWaSSww==", @@ -656,7 +656,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/middleware-retry": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-retry": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-1.1.0.tgz", "integrity": "sha512-lINKYxIvT+W20YFOtHBKeGm7npuJg0/YCoShttU7fVpsmU+a2rdb9zrJn1MHqWfUL6DhTAWGa0tH2O7l4XrDcw==", @@ -673,7 +673,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/middleware-serde": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-serde": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-1.1.0.tgz", "integrity": "sha512-RiBMxhxuO9VTjHsjJvhzViyceoLhU6gtrnJGpAXY43wE49IstXIGEQz8MT50/hOq5EumX16FCpup0r5DVyfqNQ==", @@ -685,7 +685,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/middleware-stack": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-stack": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-1.1.0.tgz", "integrity": "sha512-XynYiIvXNea2BbLcppvpNK0zu8o2woJqgnmxqYTn4FWagH/Hr2QIk8LOsUz7BIJ4tooFhmx8urHKCdlPbbPDCA==", @@ -696,7 +696,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/node-config-provider": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/node-config-provider": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-1.1.0.tgz", "integrity": "sha512-2G4TlzUnmTrUY26VKTonQqydwb+gtM/mcl+TqDP8CnWtJKVL8ElPpKgLGScP04bPIRY9x2/10lDdoaRXDqPuCw==", @@ -710,7 +710,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/node-http-handler": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/node-http-handler": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-1.1.0.tgz", "integrity": "sha512-d3kRriEgaIiGXLziAM8bjnaLn1fthCJeTLZIwEIpzQqe6yPX0a+yQoLCTyjb2fvdLwkMoG4p7THIIB5cj5lkbg==", @@ -725,7 +725,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/property-provider": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/property-provider": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-1.2.0.tgz", "integrity": "sha512-qlJd9gT751i4T0t/hJAyNGfESfi08Fek8QiLcysoKPgR05qHhG0OYhlaCJHhpXy4ECW0lHyjvFM1smrCLIXVfw==", @@ -737,7 +737,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/protocol-http": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/protocol-http": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.2.0.tgz", "integrity": "sha512-GfGfruksi3nXdFok5RhgtOnWe5f6BndzYfmEXISD+5gAGdayFGpjWu5pIqIweTudMtse20bGbc+7MFZXT1Tb8Q==", @@ -749,7 +749,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/querystring-builder": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/querystring-builder": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-1.1.0.tgz", "integrity": "sha512-gDEi4LxIGLbdfjrjiY45QNbuDmpkwh9DX4xzrR2AzjjXpxwGyfSpbJaYhXARw9p17VH0h9UewnNQXNwaQyYMDA==", @@ -762,7 +762,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/querystring-parser": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/querystring-parser": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-1.1.0.tgz", "integrity": "sha512-Lm/FZu2qW3XX+kZ4WPwr+7aAeHf1Lm84UjNkKyBu16XbmEV7ukfhXni2aIwS2rcVf8Yv5E7wchGGpOFldj9V4Q==", @@ -774,7 +774,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/service-error-classification": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/service-error-classification": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-1.1.0.tgz", "integrity": "sha512-OCTEeJ1igatd5kFrS2VDlYbainNNpf7Lj1siFOxnRWqYOP9oNvC5HOJBd3t+Z8MbrmehBtuDJ2QqeBsfeiNkww==", @@ -782,7 +782,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/shared-ini-file-loader": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/shared-ini-file-loader": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-1.1.0.tgz", "integrity": "sha512-S/v33zvCWzFyGZGlsEF0XsZtNNR281UhR7byk3nRfsgw5lGpg51rK/zjMgulM+h6NSuXaFILaYrw1I1v4kMcuA==", @@ -794,7 +794,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/smithy-client": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/smithy-client": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-1.1.0.tgz", "integrity": "sha512-j32SGgVhv2G9nBTmel9u3OXux8KG20ssxuFakJrEeDug3kqbl1qrGzVLCe+Eib402UDtA0Sp1a4NZ2SEXDBxag==", @@ -808,7 +808,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/types": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/types": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", @@ -819,7 +819,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/url-parser": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/url-parser": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-1.1.0.tgz", "integrity": "sha512-tpvi761kzboiLNGEWczuybMPCJh6WHB3cz9gWAG95mSyaKXmmX8ZcMxoV+irZfxDqLwZVJ22XTumu32S7Ow8aQ==", @@ -829,7 +829,7 @@ "tslib": "^2.5.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-base64": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-base64": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-1.1.0.tgz", "integrity": "sha512-FpYmDmVbOXAxqvoVCwqehUN0zXS+lN8V7VS9O7I8MKeVHdSTsZzlwiMEvGoyTNOXWn8luF4CTDYgNHnZViR30g==", @@ -841,7 +841,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-body-length-browser": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-body-length-browser": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-1.1.0.tgz", "integrity": "sha512-cep3ioRxzRZ2Jbp3Kly7gy6iNVefYXiT6ETt8W01RQr3uwi1YMkrbU1p3lMR4KhX/91Nrk6UOgX1RH+oIt48RQ==", @@ -849,7 +849,7 @@ "tslib": "^2.5.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-body-length-node": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-body-length-node": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-1.1.0.tgz", "integrity": "sha512-fRHRjkUuT5em4HZoshySXmB1n3HAU7IS232s+qU4TicexhyGJpXMK/2+c56ePOIa1FOK2tV1Q3J/7Mae35QVSw==", @@ -860,7 +860,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-buffer-from": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-buffer-from": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-1.1.0.tgz", "integrity": "sha512-9m6NXE0ww+ra5HKHCHig20T+FAwxBAm7DIdwc/767uGWbRcY720ybgPacQNB96JMOI7xVr/CDa3oMzKmW4a+kw==", @@ -872,7 +872,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-config-provider": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-config-provider": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-1.1.0.tgz", "integrity": "sha512-rQ47YpNmF6Is4I9GiE3T3+0xQ+r7RKRKbmHYyGSbyep/0cSf9kteKcI0ssJTvveJ1K4QvwrxXj1tEFp/G2UqxQ==", @@ -883,7 +883,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-defaults-mode-browser": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-defaults-mode-browser": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-1.1.0.tgz", "integrity": "sha512-0bWhs1e412bfC5gwPCMe8Zbz0J8UoZ/meEQdo6MYj8Ne+c+QZ+KxVjx0a1dFYOclvM33SslL9dP0odn8kfblkg==", @@ -897,7 +897,7 @@ "node": ">= 10.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-defaults-mode-node": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-defaults-mode-node": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-1.1.0.tgz", "integrity": "sha512-440e25TUH2b+TeK5CwsjYFrI9ShVOgA31CoxCKiv4ncSK4ZM68XW5opYxQmzMbRWARGEMu2XEUeBmOgMU2RLsw==", @@ -913,7 +913,7 @@ "node": ">= 10.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-hex-encoding": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-hex-encoding": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-1.1.0.tgz", "integrity": "sha512-7UtIE9eH0u41zpB60Jzr0oNCQ3hMJUabMcKRUVjmyHTXiWDE4vjSqN6qlih7rCNeKGbioS7f/y2Jgym4QZcKFg==", @@ -924,7 +924,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-middleware": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-middleware": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-1.1.0.tgz", "integrity": "sha512-6hhckcBqVgjWAqLy2vqlPZ3rfxLDhFWEmM7oLh2POGvsi7j0tHkbN7w4DFhuBExVJAbJ/qqxqZdRY6Fu7/OezQ==", @@ -935,7 +935,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-retry": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-retry": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-1.1.0.tgz", "integrity": "sha512-ygQW5HBqYXpR3ua09UciS0sL7UGJzGiktrKkOuEJwARoUuzz40yaEGU6xd9Gs7KBmAaFC8gMfnghHtwZ2nyBCQ==", @@ -947,7 +947,7 @@ "node": ">= 14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-stream": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-1.1.0.tgz", "integrity": "sha512-w3lsdGsntaLQIrwDWJkIFKrFscgZXwU/oxsse09aSTNv5TckPhDeYea3LhsDrU5MGAG3vprhVZAKr33S45coVA==", @@ -965,7 +965,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-uri-escape": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-uri-escape": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-1.1.0.tgz", "integrity": "sha512-/jL/V1xdVRt5XppwiaEU8Etp5WHZj609n0xMTuehmCqdoOFbId1M+aEeDWZsQ+8JbEB/BJ6ynY2SlYmOaKtt8w==", @@ -976,7 +976,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-utf8": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-1.1.0.tgz", "integrity": "sha512-p/MYV+JmqmPyjdgyN2UxAeYDj9cBqCjp0C/NsTWnnjoZUVqoeZ6IrW915L9CAKWVECgv9lVQGc4u/yz26/bI1A==", @@ -988,7 +988,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/@smithy/util-waiter": { + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-waiter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-1.1.0.tgz", "integrity": "sha512-S6FNIB3UJT+5Efd/0DeziO5Rs82QAMODHW4v2V3oNRrwaBigY/7Yx3SiLudZuF9WpVsV08Ih3BjIH34nzZiinQ==", @@ -1001,7 +1001,7 @@ "node": ">=14.0.0" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/fast-xml-parser": { + "node_modules/@amzn/sagemaker-client/node_modules/fast-xml-parser": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", @@ -1022,7 +1022,7 @@ "fxparser": "src/cli/cli.js" } }, - "node_modules/@amzn/rhinestone-monarch-sagemaker-client/node_modules/uuid": { + "node_modules/@amzn/sagemaker-client/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", @@ -1030,1040 +1030,80 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/@amzn/sagemaker-client": { - "version": "1.0.0", - "resolved": "file:src.gen/@amzn/sagemaker-client/1.0.0.tgz", - "integrity": "sha512-rNMUzeACaCiIqR8aQo3G99xR+Qy6zhbGi9+6XRG5proUKetO3584dclmSnIUvDvzLWosFcl4GyP8tFqiahc6Jg==", + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "license": "Apache-2.0", "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.363.0", - "@aws-sdk/credential-provider-node": "3.363.0", - "@aws-sdk/middleware-host-header": "3.363.0", - "@aws-sdk/middleware-logger": "3.363.0", - "@aws-sdk/middleware-recursion-detection": "3.363.0", - "@aws-sdk/middleware-signing": "3.363.0", - "@aws-sdk/middleware-user-agent": "3.363.0", - "@aws-sdk/types": "3.357.0", - "@aws-sdk/util-user-agent-browser": "3.363.0", - "@aws-sdk/util-user-agent-node": "3.363.0", - "@smithy/config-resolver": "^1.0.1", - "@smithy/fetch-http-handler": "^1.0.1", - "@smithy/hash-node": "^1.0.1", - "@smithy/invalid-dependency": "^1.0.1", - "@smithy/middleware-content-length": "^1.0.1", - "@smithy/middleware-retry": "^1.0.3", - "@smithy/middleware-serde": "^1.0.1", - "@smithy/middleware-stack": "^1.0.1", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/node-http-handler": "^1.0.2", - "@smithy/protocol-http": "^1.1.0", - "@smithy/smithy-client": "^1.0.3", - "@smithy/types": "^1.1.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-base64": "^1.0.1", - "@smithy/util-body-length-browser": "^1.0.1", - "@smithy/util-body-length-node": "^1.0.1", - "@smithy/util-defaults-mode-browser": "^1.0.1", - "@smithy/util-defaults-mode-node": "^1.0.1", - "@smithy/util-retry": "^1.0.3", - "@smithy/util-utf8": "^1.0.1", - "@smithy/util-waiter": "^1.0.1", - "tslib": "^2.5.0", - "uuid": "^8.3.2" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", - "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "license": "Apache-2.0", "dependencies": { - "@aws-crypto/ie11-detection": "^3.0.0", - "@aws-crypto/sha256-js": "^3.0.0", - "@aws-crypto/supports-web-crypto": "^3.0.0", - "@aws-crypto/util": "^3.0.0", + "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" + "tslib": "^2.6.2" } }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-js": { + "node_modules/@aws-crypto/ie11-detection": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", - "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", "dependencies": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", "tslib": "^1.11.1" } }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/supports-web-crypto": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", - "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "license": "Apache-2.0", "dependencies": { - "tslib": "^1.11.1" + "@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/@amzn/sagemaker-client/node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/util": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", - "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "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-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/util/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/client-sso": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.363.0.tgz", - "integrity": "sha512-PZ+HfKSgS4hlMnJzG+Ev8/mgHd/b/ETlJWPSWjC/f2NwVoBQkBnqHjdyEx7QjF6nksJozcVh5Q+kkYLKc/QwBQ==", + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "license": "Apache-2.0", "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.363.0", - "@aws-sdk/middleware-logger": "3.363.0", - "@aws-sdk/middleware-recursion-detection": "3.363.0", - "@aws-sdk/middleware-user-agent": "3.363.0", - "@aws-sdk/types": "3.357.0", - "@aws-sdk/util-endpoints": "3.357.0", - "@aws-sdk/util-user-agent-browser": "3.363.0", - "@aws-sdk/util-user-agent-node": "3.363.0", - "@smithy/config-resolver": "^1.0.1", - "@smithy/fetch-http-handler": "^1.0.1", - "@smithy/hash-node": "^1.0.1", - "@smithy/invalid-dependency": "^1.0.1", - "@smithy/middleware-content-length": "^1.0.1", - "@smithy/middleware-endpoint": "^1.0.1", - "@smithy/middleware-retry": "^1.0.2", - "@smithy/middleware-serde": "^1.0.1", - "@smithy/middleware-stack": "^1.0.1", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/node-http-handler": "^1.0.2", - "@smithy/protocol-http": "^1.0.1", - "@smithy/smithy-client": "^1.0.3", - "@smithy/types": "^1.0.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-base64": "^1.0.1", - "@smithy/util-body-length-browser": "^1.0.1", - "@smithy/util-body-length-node": "^1.0.1", - "@smithy/util-defaults-mode-browser": "^1.0.1", - "@smithy/util-defaults-mode-node": "^1.0.1", - "@smithy/util-retry": "^1.0.2", - "@smithy/util-utf8": "^1.0.1", - "tslib": "^2.5.0" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.363.0.tgz", - "integrity": "sha512-V3Ebiq/zNtDS/O92HUWGBa7MY59RYSsqWd+E0XrXv6VYTA00RlMTbNcseivNgp2UghOgB9a20Nkz6EqAeIN+RQ==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.363.0", - "@aws-sdk/middleware-logger": "3.363.0", - "@aws-sdk/middleware-recursion-detection": "3.363.0", - "@aws-sdk/middleware-user-agent": "3.363.0", - "@aws-sdk/types": "3.357.0", - "@aws-sdk/util-endpoints": "3.357.0", - "@aws-sdk/util-user-agent-browser": "3.363.0", - "@aws-sdk/util-user-agent-node": "3.363.0", - "@smithy/config-resolver": "^1.0.1", - "@smithy/fetch-http-handler": "^1.0.1", - "@smithy/hash-node": "^1.0.1", - "@smithy/invalid-dependency": "^1.0.1", - "@smithy/middleware-content-length": "^1.0.1", - "@smithy/middleware-endpoint": "^1.0.1", - "@smithy/middleware-retry": "^1.0.2", - "@smithy/middleware-serde": "^1.0.1", - "@smithy/middleware-stack": "^1.0.1", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/node-http-handler": "^1.0.2", - "@smithy/protocol-http": "^1.0.1", - "@smithy/smithy-client": "^1.0.3", - "@smithy/types": "^1.0.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-base64": "^1.0.1", - "@smithy/util-body-length-browser": "^1.0.1", - "@smithy/util-body-length-node": "^1.0.1", - "@smithy/util-defaults-mode-browser": "^1.0.1", - "@smithy/util-defaults-mode-node": "^1.0.1", - "@smithy/util-retry": "^1.0.2", - "@smithy/util-utf8": "^1.0.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/client-sts": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.363.0.tgz", - "integrity": "sha512-0jj14WvBPJQ8xr72cL0mhlmQ90tF0O0wqXwSbtog6PsC8+KDE6Yf+WsxsumyI8E5O8u3eYijBL+KdqG07F/y/w==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/credential-provider-node": "3.363.0", - "@aws-sdk/middleware-host-header": "3.363.0", - "@aws-sdk/middleware-logger": "3.363.0", - "@aws-sdk/middleware-recursion-detection": "3.363.0", - "@aws-sdk/middleware-sdk-sts": "3.363.0", - "@aws-sdk/middleware-signing": "3.363.0", - "@aws-sdk/middleware-user-agent": "3.363.0", - "@aws-sdk/types": "3.357.0", - "@aws-sdk/util-endpoints": "3.357.0", - "@aws-sdk/util-user-agent-browser": "3.363.0", - "@aws-sdk/util-user-agent-node": "3.363.0", - "@smithy/config-resolver": "^1.0.1", - "@smithy/fetch-http-handler": "^1.0.1", - "@smithy/hash-node": "^1.0.1", - "@smithy/invalid-dependency": "^1.0.1", - "@smithy/middleware-content-length": "^1.0.1", - "@smithy/middleware-endpoint": "^1.0.1", - "@smithy/middleware-retry": "^1.0.1", - "@smithy/middleware-serde": "^1.0.1", - "@smithy/middleware-stack": "^1.0.1", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/node-http-handler": "^1.0.1", - "@smithy/protocol-http": "^1.1.0", - "@smithy/smithy-client": "^1.0.2", - "@smithy/types": "^1.1.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-base64": "^1.0.1", - "@smithy/util-body-length-browser": "^1.0.1", - "@smithy/util-body-length-node": "^1.0.1", - "@smithy/util-defaults-mode-browser": "^1.0.1", - "@smithy/util-defaults-mode-node": "^1.0.1", - "@smithy/util-retry": "^1.0.1", - "@smithy/util-utf8": "^1.0.1", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.363.0.tgz", - "integrity": "sha512-VAQ3zITT2Q0acht0HezouYnMFKZ2vIOa20X4zQA3WI0HfaP4D6ga6KaenbDcb/4VFiqfqiRHfdyXHP0ThcDRMA==", - "dependencies": { - "@aws-sdk/types": "3.357.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.363.0.tgz", - "integrity": "sha512-ZYN+INoqyX5FVC3rqUxB6O8nOWkr0gHRRBm1suoOlmuFJ/WSlW/uUGthRBY5x1AQQnBF8cpdlxZzGHd41lFVNw==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.363.0", - "@aws-sdk/credential-provider-process": "3.363.0", - "@aws-sdk/credential-provider-sso": "3.363.0", - "@aws-sdk/credential-provider-web-identity": "3.363.0", - "@aws-sdk/types": "3.357.0", - "@smithy/credential-provider-imds": "^1.0.1", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.363.0.tgz", - "integrity": "sha512-C1qXFIN2yMxD6pGgug0vR1UhScOki6VqdzuBHzXZAGu7MOjvgHNdscEcb3CpWnITHaPL2ztkiw75T1sZ7oIgQg==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.363.0", - "@aws-sdk/credential-provider-ini": "3.363.0", - "@aws-sdk/credential-provider-process": "3.363.0", - "@aws-sdk/credential-provider-sso": "3.363.0", - "@aws-sdk/credential-provider-web-identity": "3.363.0", - "@aws-sdk/types": "3.357.0", - "@smithy/credential-provider-imds": "^1.0.1", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.363.0.tgz", - "integrity": "sha512-fOKAINU7Rtj2T8pP13GdCt+u0Ml3gYynp8ki+1jMZIQ+Ju/MdDOqZpKMFKicMn3Z1ttUOgqr+grUdus6z8ceBQ==", - "dependencies": { - "@aws-sdk/types": "3.357.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.363.0.tgz", - "integrity": "sha512-5RUZ5oM0lwZSo3EehT0dXggOjgtxFogpT3cZvoLGtIwrPBvm8jOQPXQUlaqCj10ThF1sYltEyukz/ovtDwYGew==", - "dependencies": { - "@aws-sdk/client-sso": "3.363.0", - "@aws-sdk/token-providers": "3.363.0", - "@aws-sdk/types": "3.357.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.363.0.tgz", - "integrity": "sha512-Z6w7fjgy79pAax580wdixbStQw10xfyZ+hOYLcPudoYFKjoNx0NQBejg5SwBzCF/HQL23Ksm9kDfbXDX9fkPhA==", - "dependencies": { - "@aws-sdk/types": "3.357.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.363.0.tgz", - "integrity": "sha512-FobpclDCf5Y1ueyJDmb9MqguAdPssNMlnqWQpujhYVABq69KHu73fSCWSauFPUrw7YOpV8kG1uagDF0POSxHzA==", - "dependencies": { - "@aws-sdk/types": "3.357.0", - "@smithy/protocol-http": "^1.1.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-logger": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.363.0.tgz", - "integrity": "sha512-SSGgthScYnFGTOw8EzbkvquqweFmvn7uJihkpFekbtBNGC/jGOGO+8ziHjTQ8t/iI/YKubEwv+LMi0f77HKSEg==", - "dependencies": { - "@aws-sdk/types": "3.357.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.363.0.tgz", - "integrity": "sha512-MWD/57QgI/N7fG8rtzDTUdSqNpYohQfgj9XCFAoVeI/bU4usrkOrew43L4smJG4XrDxlNT8lSJlDtd64tuiUZA==", - "dependencies": { - "@aws-sdk/types": "3.357.0", - "@smithy/protocol-http": "^1.1.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.363.0.tgz", - "integrity": "sha512-ri8YaQvXP6odteVTMfxPqFR26Q0h9ejtqhUDv47P34FaKXedEM4nC6ix6o+5FEYj6l8syGyktftZ5O70NoEhug==", - "dependencies": { - "@aws-sdk/types": "3.357.0", - "@aws-sdk/util-endpoints": "3.357.0", - "@smithy/protocol-http": "^1.1.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/token-providers": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.363.0.tgz", - "integrity": "sha512-6+0aJ1zugNgsMmhTtW2LBWxOVSaXCUk2q3xyTchSXkNzallYaRiZMRkieW+pKNntnu0g5H1T0zyfCO0tbXwxEA==", - "dependencies": { - "@aws-sdk/client-sso-oidc": "3.363.0", - "@aws-sdk/types": "3.357.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/types": { - "version": "3.357.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.357.0.tgz", - "integrity": "sha512-/riCRaXg3p71BeWnShrai0y0QTdXcouPSM0Cn1olZbzTf7s71aLEewrc96qFrL70XhY4XvnxMpqQh+r43XIL3g==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/util-endpoints": { - "version": "3.357.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.357.0.tgz", - "integrity": "sha512-XHKyS5JClT9su9hDif715jpZiWHQF9gKZXER8tW0gOizU3R9cyWc9EsJ2BRhFNhi7nt/JF/CLUEc5qDx3ETbUw==", - "dependencies": { - "@aws-sdk/types": "3.357.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.363.0.tgz", - "integrity": "sha512-fk9ymBUIYbxiGm99Cn+kAAXmvMCWTf/cHAcB79oCXV4ELXdPa9lN5xQhZRFNxLUeXG4OAMEuCAUUuZEj8Fnc1Q==", - "dependencies": { - "@aws-sdk/types": "3.357.0", - "@smithy/types": "^1.1.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.363.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.363.0.tgz", - "integrity": "sha512-Fli/dvgGA9hdnQUrYb1//wNSFlK2jAfdJcfNXA6SeBYzSeH5pVGYF4kXF0FCdnMA3Fef+Zn1zAP/hw9v8VJHWQ==", - "dependencies": { - "@aws-sdk/types": "3.357.0", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/abort-controller": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-1.1.0.tgz", - "integrity": "sha512-5imgGUlZL4dW4YWdMYAKLmal9ny/tlenM81QZY7xYyb76z9Z/QOg7oM5Ak9HQl8QfFTlGVWwcMXl+54jroRgEQ==", - "dependencies": { - "@smithy/types": "^1.2.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/config-resolver": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-1.1.0.tgz", - "integrity": "sha512-7WD9eZHp46BxAjNGHJLmxhhyeiNWkBdVStd7SUJPUZqQGeIO/REtIrcIfKUfdiHTQ9jyu2SYoqvzqqaFc6987w==", - "dependencies": { - "@smithy/types": "^1.2.0", - "@smithy/util-config-provider": "^1.1.0", - "@smithy/util-middleware": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/credential-provider-imds": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-1.1.0.tgz", - "integrity": "sha512-kUMOdEu3RP6ozH0Ga8OeMP8gSkBsK1UqZZKyPLFnpZHrtZuHSSt7M7gsHYB/bYQBZAo3o7qrGmRty3BubYtYxQ==", - "dependencies": { - "@smithy/node-config-provider": "^1.1.0", - "@smithy/property-provider": "^1.2.0", - "@smithy/types": "^1.2.0", - "@smithy/url-parser": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/fetch-http-handler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-1.1.0.tgz", - "integrity": "sha512-N22C9R44u5WGlcY+Wuv8EXmCAq62wWwriRAuoczMEwAIjPbvHSthyPSLqI4S7kAST1j6niWg8kwpeJ3ReAv3xg==", - "dependencies": { - "@smithy/protocol-http": "^1.2.0", - "@smithy/querystring-builder": "^1.1.0", - "@smithy/types": "^1.2.0", - "@smithy/util-base64": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/hash-node": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-1.1.0.tgz", - "integrity": "sha512-yiNKDGMzrQjnpnbLfkYKo+HwIxmBAsv0AI++QIJwvhfkLpUTBylelkv6oo78/YqZZS6h+bGfl0gILJsKE2wAKQ==", - "dependencies": { - "@smithy/types": "^1.2.0", - "@smithy/util-buffer-from": "^1.1.0", - "@smithy/util-utf8": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/invalid-dependency": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-1.1.0.tgz", - "integrity": "sha512-h2rXn68ClTwzPXYzEUNkz+0B/A0Hz8YdFNTiEwlxkwzkETGKMxmsrQGFXwYm3jd736R5vkXcClXz1ddKrsaBEQ==", - "dependencies": { - "@smithy/types": "^1.2.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/is-array-buffer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-1.1.0.tgz", - "integrity": "sha512-twpQ/n+3OWZJ7Z+xu43MJErmhB/WO/mMTnqR6PwWQShvSJ/emx5d1N59LQZk6ZpTAeuRWrc+eHhkzTp9NFjNRQ==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-content-length": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-1.1.0.tgz", - "integrity": "sha512-iNxwhZ7Xc5+LjeDElEOi/Nh8fFsc9Dw9+5w7h7/GLFIU0RgAwBJuJtcP1vNTOwzW4B3hG+gRu8sQLqA9OEaTwA==", - "dependencies": { - "@smithy/protocol-http": "^1.2.0", - "@smithy/types": "^1.2.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-endpoint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-1.1.0.tgz", - "integrity": "sha512-PvpazNjVpxX2ICrzoFYCpFnjB39DKCpZds8lRpAB3p6HGrx6QHBaNvOzVhJGBf0jcAbfCdc5/W0n9z8VWaSSww==", - "dependencies": { - "@smithy/middleware-serde": "^1.1.0", - "@smithy/types": "^1.2.0", - "@smithy/url-parser": "^1.1.0", - "@smithy/util-middleware": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-retry": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-1.1.0.tgz", - "integrity": "sha512-lINKYxIvT+W20YFOtHBKeGm7npuJg0/YCoShttU7fVpsmU+a2rdb9zrJn1MHqWfUL6DhTAWGa0tH2O7l4XrDcw==", - "dependencies": { - "@smithy/protocol-http": "^1.2.0", - "@smithy/service-error-classification": "^1.1.0", - "@smithy/types": "^1.2.0", - "@smithy/util-middleware": "^1.1.0", - "@smithy/util-retry": "^1.1.0", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-serde": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-1.1.0.tgz", - "integrity": "sha512-RiBMxhxuO9VTjHsjJvhzViyceoLhU6gtrnJGpAXY43wE49IstXIGEQz8MT50/hOq5EumX16FCpup0r5DVyfqNQ==", - "dependencies": { - "@smithy/types": "^1.2.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-stack": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-1.1.0.tgz", - "integrity": "sha512-XynYiIvXNea2BbLcppvpNK0zu8o2woJqgnmxqYTn4FWagH/Hr2QIk8LOsUz7BIJ4tooFhmx8urHKCdlPbbPDCA==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/node-config-provider": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-1.1.0.tgz", - "integrity": "sha512-2G4TlzUnmTrUY26VKTonQqydwb+gtM/mcl+TqDP8CnWtJKVL8ElPpKgLGScP04bPIRY9x2/10lDdoaRXDqPuCw==", - "dependencies": { - "@smithy/property-provider": "^1.2.0", - "@smithy/shared-ini-file-loader": "^1.1.0", - "@smithy/types": "^1.2.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/node-http-handler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-1.1.0.tgz", - "integrity": "sha512-d3kRriEgaIiGXLziAM8bjnaLn1fthCJeTLZIwEIpzQqe6yPX0a+yQoLCTyjb2fvdLwkMoG4p7THIIB5cj5lkbg==", - "dependencies": { - "@smithy/abort-controller": "^1.1.0", - "@smithy/protocol-http": "^1.2.0", - "@smithy/querystring-builder": "^1.1.0", - "@smithy/types": "^1.2.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/property-provider": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-1.2.0.tgz", - "integrity": "sha512-qlJd9gT751i4T0t/hJAyNGfESfi08Fek8QiLcysoKPgR05qHhG0OYhlaCJHhpXy4ECW0lHyjvFM1smrCLIXVfw==", - "dependencies": { - "@smithy/types": "^1.2.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/protocol-http": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.2.0.tgz", - "integrity": "sha512-GfGfruksi3nXdFok5RhgtOnWe5f6BndzYfmEXISD+5gAGdayFGpjWu5pIqIweTudMtse20bGbc+7MFZXT1Tb8Q==", - "dependencies": { - "@smithy/types": "^1.2.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/querystring-builder": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-1.1.0.tgz", - "integrity": "sha512-gDEi4LxIGLbdfjrjiY45QNbuDmpkwh9DX4xzrR2AzjjXpxwGyfSpbJaYhXARw9p17VH0h9UewnNQXNwaQyYMDA==", - "dependencies": { - "@smithy/types": "^1.2.0", - "@smithy/util-uri-escape": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/querystring-parser": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-1.1.0.tgz", - "integrity": "sha512-Lm/FZu2qW3XX+kZ4WPwr+7aAeHf1Lm84UjNkKyBu16XbmEV7ukfhXni2aIwS2rcVf8Yv5E7wchGGpOFldj9V4Q==", - "dependencies": { - "@smithy/types": "^1.2.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/service-error-classification": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-1.1.0.tgz", - "integrity": "sha512-OCTEeJ1igatd5kFrS2VDlYbainNNpf7Lj1siFOxnRWqYOP9oNvC5HOJBd3t+Z8MbrmehBtuDJ2QqeBsfeiNkww==", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/shared-ini-file-loader": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-1.1.0.tgz", - "integrity": "sha512-S/v33zvCWzFyGZGlsEF0XsZtNNR281UhR7byk3nRfsgw5lGpg51rK/zjMgulM+h6NSuXaFILaYrw1I1v4kMcuA==", - "dependencies": { - "@smithy/types": "^1.2.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/smithy-client": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-1.1.0.tgz", - "integrity": "sha512-j32SGgVhv2G9nBTmel9u3OXux8KG20ssxuFakJrEeDug3kqbl1qrGzVLCe+Eib402UDtA0Sp1a4NZ2SEXDBxag==", - "dependencies": { - "@smithy/middleware-stack": "^1.1.0", - "@smithy/types": "^1.2.0", - "@smithy/util-stream": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/types": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", - "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/url-parser": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-1.1.0.tgz", - "integrity": "sha512-tpvi761kzboiLNGEWczuybMPCJh6WHB3cz9gWAG95mSyaKXmmX8ZcMxoV+irZfxDqLwZVJ22XTumu32S7Ow8aQ==", - "dependencies": { - "@smithy/querystring-parser": "^1.1.0", - "@smithy/types": "^1.2.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-base64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-1.1.0.tgz", - "integrity": "sha512-FpYmDmVbOXAxqvoVCwqehUN0zXS+lN8V7VS9O7I8MKeVHdSTsZzlwiMEvGoyTNOXWn8luF4CTDYgNHnZViR30g==", - "dependencies": { - "@smithy/util-buffer-from": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-body-length-browser": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-1.1.0.tgz", - "integrity": "sha512-cep3ioRxzRZ2Jbp3Kly7gy6iNVefYXiT6ETt8W01RQr3uwi1YMkrbU1p3lMR4KhX/91Nrk6UOgX1RH+oIt48RQ==", - "dependencies": { - "tslib": "^2.5.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-body-length-node": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-1.1.0.tgz", - "integrity": "sha512-fRHRjkUuT5em4HZoshySXmB1n3HAU7IS232s+qU4TicexhyGJpXMK/2+c56ePOIa1FOK2tV1Q3J/7Mae35QVSw==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-buffer-from": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-1.1.0.tgz", - "integrity": "sha512-9m6NXE0ww+ra5HKHCHig20T+FAwxBAm7DIdwc/767uGWbRcY720ybgPacQNB96JMOI7xVr/CDa3oMzKmW4a+kw==", - "dependencies": { - "@smithy/is-array-buffer": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-config-provider": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-1.1.0.tgz", - "integrity": "sha512-rQ47YpNmF6Is4I9GiE3T3+0xQ+r7RKRKbmHYyGSbyep/0cSf9kteKcI0ssJTvveJ1K4QvwrxXj1tEFp/G2UqxQ==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-defaults-mode-browser": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-1.1.0.tgz", - "integrity": "sha512-0bWhs1e412bfC5gwPCMe8Zbz0J8UoZ/meEQdo6MYj8Ne+c+QZ+KxVjx0a1dFYOclvM33SslL9dP0odn8kfblkg==", - "dependencies": { - "@smithy/property-provider": "^1.2.0", - "@smithy/types": "^1.2.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-defaults-mode-node": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-1.1.0.tgz", - "integrity": "sha512-440e25TUH2b+TeK5CwsjYFrI9ShVOgA31CoxCKiv4ncSK4ZM68XW5opYxQmzMbRWARGEMu2XEUeBmOgMU2RLsw==", - "dependencies": { - "@smithy/config-resolver": "^1.1.0", - "@smithy/credential-provider-imds": "^1.1.0", - "@smithy/node-config-provider": "^1.1.0", - "@smithy/property-provider": "^1.2.0", - "@smithy/types": "^1.2.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-hex-encoding": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-1.1.0.tgz", - "integrity": "sha512-7UtIE9eH0u41zpB60Jzr0oNCQ3hMJUabMcKRUVjmyHTXiWDE4vjSqN6qlih7rCNeKGbioS7f/y2Jgym4QZcKFg==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-middleware": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-1.1.0.tgz", - "integrity": "sha512-6hhckcBqVgjWAqLy2vqlPZ3rfxLDhFWEmM7oLh2POGvsi7j0tHkbN7w4DFhuBExVJAbJ/qqxqZdRY6Fu7/OezQ==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-retry": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-1.1.0.tgz", - "integrity": "sha512-ygQW5HBqYXpR3ua09UciS0sL7UGJzGiktrKkOuEJwARoUuzz40yaEGU6xd9Gs7KBmAaFC8gMfnghHtwZ2nyBCQ==", - "dependencies": { - "@smithy/service-error-classification": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-1.1.0.tgz", - "integrity": "sha512-w3lsdGsntaLQIrwDWJkIFKrFscgZXwU/oxsse09aSTNv5TckPhDeYea3LhsDrU5MGAG3vprhVZAKr33S45coVA==", - "dependencies": { - "@smithy/fetch-http-handler": "^1.1.0", - "@smithy/node-http-handler": "^1.1.0", - "@smithy/types": "^1.2.0", - "@smithy/util-base64": "^1.1.0", - "@smithy/util-buffer-from": "^1.1.0", - "@smithy/util-hex-encoding": "^1.1.0", - "@smithy/util-utf8": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-uri-escape": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-1.1.0.tgz", - "integrity": "sha512-/jL/V1xdVRt5XppwiaEU8Etp5WHZj609n0xMTuehmCqdoOFbId1M+aEeDWZsQ+8JbEB/BJ6ynY2SlYmOaKtt8w==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-1.1.0.tgz", - "integrity": "sha512-p/MYV+JmqmPyjdgyN2UxAeYDj9cBqCjp0C/NsTWnnjoZUVqoeZ6IrW915L9CAKWVECgv9lVQGc4u/yz26/bI1A==", - "dependencies": { - "@smithy/util-buffer-from": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-waiter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-1.1.0.tgz", - "integrity": "sha512-S6FNIB3UJT+5Efd/0DeziO5Rs82QAMODHW4v2V3oNRrwaBigY/7Yx3SiLudZuF9WpVsV08Ih3BjIH34nzZiinQ==", - "dependencies": { - "@smithy/abort-controller": "^1.1.0", - "@smithy/types": "^1.2.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/@amzn/sagemaker-client/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "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-crypto/crc32c": { - "version": "5.2.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/ie11-detection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", - "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", - "dependencies": { - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/sha1-browser": { - "version": "5.2.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "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-crypto/supports-web-crypto": { - "version": "5.2.0", - "license": "Apache-2.0", + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" } @@ -7158,52 +6198,227 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds": { - "version": "4.0.1", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/querystring-builder/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/querystring-parser/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/service-error-classification/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/property-provider": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/node-config-provider": { - "version": "4.0.1", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream": { + "version": "4.1.2", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/property-provider": { - "version": "4.0.1", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", "license": "Apache-2.0", "peer": true, "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/shared-ini-file-loader": { - "version": "4.0.1", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", "license": "Apache-2.0", "peer": true, "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, @@ -7211,56 +6426,56 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/types": { - "version": "4.1.0", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/protocol-http": { + "version": "5.0.1", "license": "Apache-2.0", "peer": true, "dependencies": { + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/url-parser": { - "version": "4.0.1", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/types": { + "version": "4.1.0", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/querystring-parser": "^4.0.1", - "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/is-array-buffer": { + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/util-base64": { "version": "4.0.0", "license": "Apache-2.0", "peer": true, "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/querystring-builder": { - "version": "4.0.1", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/util-utf8": { + "version": "4.0.0", "license": "Apache-2.0", "peer": true, "dependencies": { - "@smithy/types": "^4.1.0", - "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/querystring-builder/node_modules/@smithy/types": { - "version": "4.1.0", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", "license": "Apache-2.0", "peer": true, "dependencies": { @@ -7270,222 +6485,352 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/querystring-parser": { - "version": "4.0.1", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-utf8": { + "version": "3.0.0", "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/querystring-parser/node_modules/@smithy/types": { - "version": "4.1.0", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-utf8/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/service-error-classification": { - "version": "4.0.1", + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.1.0" + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/service-error-classification/node_modules/@smithy/types": { - "version": "4.1.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.730.0.tgz", + "integrity": "sha512-iJt2pL6RqWg7R3pja1WfcC2+oTjwaKFYndNE9oUQqyc6RN24XWUtGy9JnWqTUOy8jYzaP2eoF00fGeasSBX+Dw==", "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/credential-provider-node": "3.730.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.730.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-buffer-from": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/client-sso": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.730.0.tgz", + "integrity": "sha512-mI8kqkSuVlZklewEmN7jcbBMyVODBld3MsTjCKSl5ztduuPX69JD7nXLnWWPkw1PX4aGTO24AEoRMGNxntoXUg==", "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.730.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-hex-encoding": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/core": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.730.0.tgz", + "integrity": "sha512-jonKyR+2GcqbZj2WDICZS0c633keLc9qwXnePu83DfAoFXMMIMyoR/7FOGf8F3OrIdGh8KzE9VvST+nZCK9EJA==", "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/core": "^3.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/signature-v4": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream": { - "version": "4.1.2", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.730.0.tgz", + "integrity": "sha512-fFXgo3jBXLWqu8I07Hd96mS7RjrtpDgm3bZShm0F3lKtqDQF+hObFWq9A013SOE+RjMLVfbABhToXAYct3FcBw==", "dependencies": { - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/types": "^4.1.0", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { - "version": "5.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.730.0.tgz", + "integrity": "sha512-1aF3elbCzpVhWLAuV63iFElfLOqLGGTp4fkf2VAFIDO3hjshpXUQssTgIWiBwwtJYJdOSxaFrCU7u8frjr/5aQ==", "dependencies": { - "@smithy/protocol-http": "^5.0.1", - "@smithy/querystring-builder": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-base64": "^4.0.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-stream": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/node-http-handler": { - "version": "4.0.3", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.730.0.tgz", + "integrity": "sha512-zwsxkBuQuPp06o45ATAnznHzj3+ibop/EaTytNzSv0O87Q59K/jnS/bdtv1n6bhe99XCieRNTihvtS7YklzK7A==", "dependencies": { - "@smithy/abort-controller": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/querystring-builder": "^4.0.1", - "@smithy/types": "^4.1.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/protocol-http": { - "version": "5.0.1", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.730.0.tgz", + "integrity": "sha512-ztRjh1edY7ut2wwrj1XqHtqPY/NXEYIk5fYf04KKsp8zBi81ScVqP7C+Cst6PFKixjgLSG6RsqMx9GSAalVv0Q==", "dependencies": { - "@smithy/types": "^4.1.0", + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-ini": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/types": { - "version": "4.1.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.730.0.tgz", + "integrity": "sha512-cNKUQ81eptfZN8MlSqwUq3+5ln8u/PcY57UmLZ+npxUHanqO1akpgcpNsLpmsIkoXGbtSQrLuDUgH86lS/SWOw==", "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/util-base64": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.730.0.tgz", + "integrity": "sha512-SdI2xrTbquJLMxUh5LpSwB8zfiKq3/jso53xWRgrVfeDlrSzZuyV6QghaMs3KEEjcNzwEnTfSIjGQyRXG9VrEw==", "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", + "@aws-sdk/client-sso": "3.730.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/token-providers": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/util-utf8": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.730.0.tgz", + "integrity": "sha512-l5vdPmvF/d890pbvv5g1GZrdjaSQkyPH/Bc8dO/ZqkWxkIP8JNgl48S2zgf4DkP3ik9K2axWO828L5RsMDQzdA==", "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-uri-escape": { - "version": "4.0.0", - "license": "Apache-2.0", - "peer": true, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.723.0.tgz", + "integrity": "sha512-LLVzLvk299pd7v4jN9yOSaWDZDfH0SnBPb6q+FDPaOCMGBY8kuwQso7e/ozIKSmZHRMGO3IZrflasHM+rI+2YQ==", "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-logger": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.723.0.tgz", + "integrity": "sha512-chASQfDG5NJ8s5smydOEnNK7N0gDMyuPbx7dYYcm1t/PKtnVfvWF+DHCTrRC2Ej76gLJVCVizlAJKM8v8Kg3cg==", "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-utf8/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.723.0.tgz", + "integrity": "sha512-7usZMtoynT9/jxL/rkuDOFQ0C2mhXl4yCm67Rg7GNTstl67u7w5WN1aIRImMeztaKlw8ExjoTyo6WTs1Kceh7A==", "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.730.0.tgz", + "integrity": "sha512-aPMZvNmf2a42B41au3bA3ODU4HfHka2nYT/SAIhhVXH1ENYfAmZo7FraFPxetKepFMCtL7j4QE6/LDucK6liIw==", "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@smithy/core": "^3.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity": { + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/nested-clients": { "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.730.0.tgz", - "integrity": "sha512-iJt2pL6RqWg7R3pja1WfcC2+oTjwaKFYndNE9oUQqyc6RN24XWUtGy9JnWqTUOy8jYzaP2eoF00fGeasSBX+Dw==", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.730.0.tgz", + "integrity": "sha512-vilIgf1/7kre8DdE5zAQkDOwHFb/TahMn/6j2RZwFLlK7cDk91r19deSiVYnKQkupDMtOfNceNqnorM4I3PDzw==", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.730.0", - "@aws-sdk/credential-provider-node": "3.730.0", "@aws-sdk/middleware-host-header": "3.723.0", "@aws-sdk/middleware-logger": "3.723.0", "@aws-sdk/middleware-recursion-detection": "3.723.0", @@ -7526,407 +6871,379 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/client-sso": { - "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.730.0.tgz", - "integrity": "sha512-mI8kqkSuVlZklewEmN7jcbBMyVODBld3MsTjCKSl5ztduuPX69JD7nXLnWWPkw1PX4aGTO24AEoRMGNxntoXUg==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.723.0.tgz", + "integrity": "sha512-tGF/Cvch3uQjZIj34LY2mg8M2Dr4kYG8VU8Yd0dFnB1ybOEOveIK/9ypUo9ycZpB9oO6q01KRe5ijBaxNueUQg==", "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.730.0", - "@aws-sdk/middleware-host-header": "3.723.0", - "@aws-sdk/middleware-logger": "3.723.0", - "@aws-sdk/middleware-recursion-detection": "3.723.0", - "@aws-sdk/middleware-user-agent": "3.730.0", - "@aws-sdk/region-config-resolver": "3.723.0", "@aws-sdk/types": "3.723.0", - "@aws-sdk/util-endpoints": "3.730.0", - "@aws-sdk/util-user-agent-browser": "3.723.0", - "@aws-sdk/util-user-agent-node": "3.730.0", - "@smithy/config-resolver": "^4.0.0", - "@smithy/core": "^3.0.0", - "@smithy/fetch-http-handler": "^5.0.0", - "@smithy/hash-node": "^4.0.0", - "@smithy/invalid-dependency": "^4.0.0", - "@smithy/middleware-content-length": "^4.0.0", - "@smithy/middleware-endpoint": "^4.0.0", - "@smithy/middleware-retry": "^4.0.0", - "@smithy/middleware-serde": "^4.0.0", - "@smithy/middleware-stack": "^4.0.0", "@smithy/node-config-provider": "^4.0.0", - "@smithy/node-http-handler": "^4.0.0", - "@smithy/protocol-http": "^5.0.0", - "@smithy/smithy-client": "^4.0.0", "@smithy/types": "^4.0.0", - "@smithy/url-parser": "^4.0.0", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.0", - "@smithy/util-defaults-mode-node": "^4.0.0", - "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.0", - "@smithy/util-retry": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/core": { + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/token-providers": { "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.730.0.tgz", - "integrity": "sha512-jonKyR+2GcqbZj2WDICZS0c633keLc9qwXnePu83DfAoFXMMIMyoR/7FOGf8F3OrIdGh8KzE9VvST+nZCK9EJA==", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.730.0.tgz", + "integrity": "sha512-BSPssGj54B/AABWXARIPOT/1ybFahM1ldlfmXy9gRmZi/afe9geWJGlFYCCt3PmqR+1Ny5XIjSfue+kMd//drQ==", "dependencies": { + "@aws-sdk/nested-clients": "3.730.0", "@aws-sdk/types": "3.723.0", - "@smithy/core": "^3.0.0", - "@smithy/node-config-provider": "^4.0.0", "@smithy/property-provider": "^4.0.0", - "@smithy/protocol-http": "^5.0.0", - "@smithy/signature-v4": "^5.0.0", - "@smithy/smithy-client": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", "@smithy/types": "^4.0.0", - "@smithy/util-middleware": "^4.0.0", - "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.730.0.tgz", - "integrity": "sha512-fFXgo3jBXLWqu8I07Hd96mS7RjrtpDgm3bZShm0F3lKtqDQF+hObFWq9A013SOE+RjMLVfbABhToXAYct3FcBw==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/types": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.723.0.tgz", + "integrity": "sha512-LmK3kwiMZG1y5g3LGihT9mNkeNOmwEyPk6HGcJqh0wOSV4QpWoKu2epyKE4MLQNUUlz2kOVbVbOrwmI6ZcteuA==", + "dependencies": { + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-endpoints": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.730.0.tgz", + "integrity": "sha512-1KTFuVnk+YtLgWr6TwDiggcDqtPpOY2Cszt3r2lkXfaEAX6kHyOZi1vdvxXjPU5LsOBJem8HZ7KlkmrEi+xowg==", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.723.0.tgz", + "integrity": "sha512-Wh9I6j2jLhNFq6fmXydIpqD1WyQLyTfSxjW9B+PXSnPyk3jtQW8AKQur7p97rO8LAUzVI0bv8kb3ZzDEVbquIg==", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.730.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.730.0.tgz", + "integrity": "sha512-yBvkOAjqsDEl1va4eHNOhnFBk0iCY/DBFNyhvtTMqPF4NO+MITWpFs3J9JtZKzJlQ6x0Yb9TLQ8NhDjEISz5Ug==", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/abort-controller": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", + "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/config-resolver": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", + "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", "dependencies": { - "@aws-sdk/core": "3.730.0", - "@aws-sdk/types": "3.723.0", - "@smithy/property-provider": "^4.0.0", - "@smithy/types": "^4.0.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.730.0.tgz", - "integrity": "sha512-1aF3elbCzpVhWLAuV63iFElfLOqLGGTp4fkf2VAFIDO3hjshpXUQssTgIWiBwwtJYJdOSxaFrCU7u8frjr/5aQ==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/core": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.5.3.tgz", + "integrity": "sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA==", "dependencies": { - "@aws-sdk/core": "3.730.0", - "@aws-sdk/types": "3.723.0", - "@smithy/fetch-http-handler": "^5.0.0", - "@smithy/node-http-handler": "^4.0.0", - "@smithy/property-provider": "^4.0.0", - "@smithy/protocol-http": "^5.0.0", - "@smithy/smithy-client": "^4.0.0", - "@smithy/types": "^4.0.0", - "@smithy/util-stream": "^4.0.0", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.730.0.tgz", - "integrity": "sha512-zwsxkBuQuPp06o45ATAnznHzj3+ibop/EaTytNzSv0O87Q59K/jnS/bdtv1n6bhe99XCieRNTihvtS7YklzK7A==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", + "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", "dependencies": { - "@aws-sdk/core": "3.730.0", - "@aws-sdk/credential-provider-env": "3.730.0", - "@aws-sdk/credential-provider-http": "3.730.0", - "@aws-sdk/credential-provider-process": "3.730.0", - "@aws-sdk/credential-provider-sso": "3.730.0", - "@aws-sdk/credential-provider-web-identity": "3.730.0", - "@aws-sdk/nested-clients": "3.730.0", - "@aws-sdk/types": "3.723.0", - "@smithy/credential-provider-imds": "^4.0.0", - "@smithy/property-provider": "^4.0.0", - "@smithy/shared-ini-file-loader": "^4.0.0", - "@smithy/types": "^4.0.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.730.0.tgz", - "integrity": "sha512-ztRjh1edY7ut2wwrj1XqHtqPY/NXEYIk5fYf04KKsp8zBi81ScVqP7C+Cst6PFKixjgLSG6RsqMx9GSAalVv0Q==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.4.tgz", + "integrity": "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==", "dependencies": { - "@aws-sdk/credential-provider-env": "3.730.0", - "@aws-sdk/credential-provider-http": "3.730.0", - "@aws-sdk/credential-provider-ini": "3.730.0", - "@aws-sdk/credential-provider-process": "3.730.0", - "@aws-sdk/credential-provider-sso": "3.730.0", - "@aws-sdk/credential-provider-web-identity": "3.730.0", - "@aws-sdk/types": "3.723.0", - "@smithy/credential-provider-imds": "^4.0.0", - "@smithy/property-provider": "^4.0.0", - "@smithy/shared-ini-file-loader": "^4.0.0", - "@smithy/types": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.730.0.tgz", - "integrity": "sha512-cNKUQ81eptfZN8MlSqwUq3+5ln8u/PcY57UmLZ+npxUHanqO1akpgcpNsLpmsIkoXGbtSQrLuDUgH86lS/SWOw==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/hash-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", + "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", "dependencies": { - "@aws-sdk/core": "3.730.0", - "@aws-sdk/types": "3.723.0", - "@smithy/property-provider": "^4.0.0", - "@smithy/shared-ini-file-loader": "^4.0.0", - "@smithy/types": "^4.0.0", + "@smithy/types": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.730.0.tgz", - "integrity": "sha512-SdI2xrTbquJLMxUh5LpSwB8zfiKq3/jso53xWRgrVfeDlrSzZuyV6QghaMs3KEEjcNzwEnTfSIjGQyRXG9VrEw==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/invalid-dependency": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", + "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", "dependencies": { - "@aws-sdk/client-sso": "3.730.0", - "@aws-sdk/core": "3.730.0", - "@aws-sdk/token-providers": "3.730.0", - "@aws-sdk/types": "3.723.0", - "@smithy/property-provider": "^4.0.0", - "@smithy/shared-ini-file-loader": "^4.0.0", - "@smithy/types": "^4.0.0", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.730.0.tgz", - "integrity": "sha512-l5vdPmvF/d890pbvv5g1GZrdjaSQkyPH/Bc8dO/ZqkWxkIP8JNgl48S2zgf4DkP3ik9K2axWO828L5RsMDQzdA==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", "dependencies": { - "@aws-sdk/core": "3.730.0", - "@aws-sdk/nested-clients": "3.730.0", - "@aws-sdk/types": "3.723.0", - "@smithy/property-provider": "^4.0.0", - "@smithy/types": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.723.0.tgz", - "integrity": "sha512-LLVzLvk299pd7v4jN9yOSaWDZDfH0SnBPb6q+FDPaOCMGBY8kuwQso7e/ozIKSmZHRMGO3IZrflasHM+rI+2YQ==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-content-length": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", + "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", "dependencies": { - "@aws-sdk/types": "3.723.0", - "@smithy/protocol-http": "^5.0.0", - "@smithy/types": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-logger": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.723.0.tgz", - "integrity": "sha512-chASQfDG5NJ8s5smydOEnNK7N0gDMyuPbx7dYYcm1t/PKtnVfvWF+DHCTrRC2Ej76gLJVCVizlAJKM8v8Kg3cg==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-endpoint": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.11.tgz", + "integrity": "sha512-zDogwtRLzKl58lVS8wPcARevFZNBOOqnmzWWxVe9XiaXU2CADFjvJ9XfNibgkOWs08sxLuSr81NrpY4mgp9OwQ==", "dependencies": { - "@aws-sdk/types": "3.723.0", - "@smithy/types": "^4.0.0", + "@smithy/core": "^3.5.3", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-middleware": "^4.0.4", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.723.0.tgz", - "integrity": "sha512-7usZMtoynT9/jxL/rkuDOFQ0C2mhXl4yCm67Rg7GNTstl67u7w5WN1aIRImMeztaKlw8ExjoTyo6WTs1Kceh7A==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-retry": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.12.tgz", + "integrity": "sha512-wvIH70c4e91NtRxdaLZF+mbLZ/HcC6yg7ySKUiufL6ESp6zJUSnJucZ309AvG9nqCFHSRB5I6T3Ez1Q9wCh0Ww==", "dependencies": { - "@aws-sdk/types": "3.723.0", - "@smithy/protocol-http": "^5.0.0", - "@smithy/types": "^4.0.0", - "tslib": "^2.6.2" + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.5", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "tslib": "^2.6.2", + "uuid": "^9.0.1" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.730.0.tgz", - "integrity": "sha512-aPMZvNmf2a42B41au3bA3ODU4HfHka2nYT/SAIhhVXH1ENYfAmZo7FraFPxetKepFMCtL7j4QE6/LDucK6liIw==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-serde": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", + "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", "dependencies": { - "@aws-sdk/core": "3.730.0", - "@aws-sdk/types": "3.723.0", - "@aws-sdk/util-endpoints": "3.730.0", - "@smithy/core": "^3.0.0", - "@smithy/protocol-http": "^5.0.0", - "@smithy/types": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/nested-clients": { - "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.730.0.tgz", - "integrity": "sha512-vilIgf1/7kre8DdE5zAQkDOwHFb/TahMn/6j2RZwFLlK7cDk91r19deSiVYnKQkupDMtOfNceNqnorM4I3PDzw==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-stack": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", + "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.730.0", - "@aws-sdk/middleware-host-header": "3.723.0", - "@aws-sdk/middleware-logger": "3.723.0", - "@aws-sdk/middleware-recursion-detection": "3.723.0", - "@aws-sdk/middleware-user-agent": "3.730.0", - "@aws-sdk/region-config-resolver": "3.723.0", - "@aws-sdk/types": "3.723.0", - "@aws-sdk/util-endpoints": "3.730.0", - "@aws-sdk/util-user-agent-browser": "3.723.0", - "@aws-sdk/util-user-agent-node": "3.730.0", - "@smithy/config-resolver": "^4.0.0", - "@smithy/core": "^3.0.0", - "@smithy/fetch-http-handler": "^5.0.0", - "@smithy/hash-node": "^4.0.0", - "@smithy/invalid-dependency": "^4.0.0", - "@smithy/middleware-content-length": "^4.0.0", - "@smithy/middleware-endpoint": "^4.0.0", - "@smithy/middleware-retry": "^4.0.0", - "@smithy/middleware-serde": "^4.0.0", - "@smithy/middleware-stack": "^4.0.0", - "@smithy/node-config-provider": "^4.0.0", - "@smithy/node-http-handler": "^4.0.0", - "@smithy/protocol-http": "^5.0.0", - "@smithy/smithy-client": "^4.0.0", - "@smithy/types": "^4.0.0", - "@smithy/url-parser": "^4.0.0", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.0", - "@smithy/util-defaults-mode-node": "^4.0.0", - "@smithy/util-endpoints": "^3.0.0", - "@smithy/util-middleware": "^4.0.0", - "@smithy/util-retry": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-config-provider": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", + "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.723.0.tgz", - "integrity": "sha512-tGF/Cvch3uQjZIj34LY2mg8M2Dr4kYG8VU8Yd0dFnB1ybOEOveIK/9ypUo9ycZpB9oO6q01KRe5ijBaxNueUQg==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-http-handler": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", + "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", "dependencies": { - "@aws-sdk/types": "3.723.0", - "@smithy/node-config-provider": "^4.0.0", - "@smithy/types": "^4.0.0", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.0", + "@smithy/abort-controller": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/token-providers": { - "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.730.0.tgz", - "integrity": "sha512-BSPssGj54B/AABWXARIPOT/1ybFahM1ldlfmXy9gRmZi/afe9geWJGlFYCCt3PmqR+1Ny5XIjSfue+kMd//drQ==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/property-provider": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", + "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", "dependencies": { - "@aws-sdk/nested-clients": "3.730.0", - "@aws-sdk/types": "3.723.0", - "@smithy/property-provider": "^4.0.0", - "@smithy/shared-ini-file-loader": "^4.0.0", - "@smithy/types": "^4.0.0", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/types": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.723.0.tgz", - "integrity": "sha512-LmK3kwiMZG1y5g3LGihT9mNkeNOmwEyPk6HGcJqh0wOSV4QpWoKu2epyKE4MLQNUUlz2kOVbVbOrwmI6ZcteuA==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/protocol-http": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", + "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", "dependencies": { - "@smithy/types": "^4.0.0", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-endpoints": { - "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.730.0.tgz", - "integrity": "sha512-1KTFuVnk+YtLgWr6TwDiggcDqtPpOY2Cszt3r2lkXfaEAX6kHyOZi1vdvxXjPU5LsOBJem8HZ7KlkmrEi+xowg==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-builder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", + "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", "dependencies": { - "@aws-sdk/types": "3.723.0", - "@smithy/types": "^4.0.0", - "@smithy/util-endpoints": "^3.0.0", + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.723.0.tgz", - "integrity": "sha512-Wh9I6j2jLhNFq6fmXydIpqD1WyQLyTfSxjW9B+PXSnPyk3jtQW8AKQur7p97rO8LAUzVI0bv8kb3ZzDEVbquIg==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", + "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", "dependencies": { - "@aws-sdk/types": "3.723.0", - "@smithy/types": "^4.0.0", - "bowser": "^2.11.0", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.730.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.730.0.tgz", - "integrity": "sha512-yBvkOAjqsDEl1va4eHNOhnFBk0iCY/DBFNyhvtTMqPF4NO+MITWpFs3J9JtZKzJlQ6x0Yb9TLQ8NhDjEISz5Ug==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/service-error-classification": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.5.tgz", + "integrity": "sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA==", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.730.0", - "@aws-sdk/types": "3.723.0", - "@smithy/node-config-provider": "^4.0.0", - "@smithy/types": "^4.0.0", - "tslib": "^2.6.2" + "@smithy/types": "^4.3.1" }, "engines": { "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/abort-controller": { + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/shared-ini-file-loader": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", - "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", + "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -7935,76 +7252,70 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/config-resolver": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", - "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/signature-v4": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", + "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", "dependencies": { - "@smithy/node-config-provider": "^4.1.3", + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-middleware": "^4.0.4", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/core": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.5.3.tgz", - "integrity": "sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/smithy-client": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.3.tgz", + "integrity": "sha512-xxzNYgA0HD6ETCe5QJubsxP0hQH3QK3kbpJz3QrosBCuIWyEXLR/CO5hFb2OeawEKUxMNhz3a1nuJNN2np2RMA==", "dependencies": { - "@smithy/middleware-serde": "^4.0.8", + "@smithy/core": "^3.5.3", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-stack": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", "@smithy/util-stream": "^4.2.2", - "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/credential-provider-imds": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", - "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/types": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", + "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", "dependencies": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/fetch-http-handler": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.4.tgz", - "integrity": "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/url-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", + "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", "dependencies": { - "@smithy/protocol-http": "^5.1.2", - "@smithy/querystring-builder": "^4.0.4", + "@smithy/querystring-parser": "^4.0.4", "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/hash-node": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", - "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", "dependencies": { - "@smithy/types": "^4.3.1", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" @@ -8013,22 +7324,21 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/invalid-dependency": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", - "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", "dependencies": { - "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/is-array-buffer": { + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-node": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", - "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", "dependencies": { "tslib": "^2.6.2" }, @@ -8036,62 +7346,103 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-content-length": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", - "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", "dependencies": { - "@smithy/protocol-http": "^5.1.2", + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.19.tgz", + "integrity": "sha512-mvLMh87xSmQrV5XqnUYEPoiFFeEGYeAKIDDKdhE2ahqitm8OHM3aSvhqL6rrK6wm1brIk90JhxDf5lf2hbrLbQ==", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-endpoint": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.11.tgz", - "integrity": "sha512-zDogwtRLzKl58lVS8wPcARevFZNBOOqnmzWWxVe9XiaXU2CADFjvJ9XfNibgkOWs08sxLuSr81NrpY4mgp9OwQ==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.19.tgz", + "integrity": "sha512-8tYnx+LUfj6m+zkUUIrIQJxPM1xVxfRBvoGHua7R/i6qAxOMjqR6CpEpDwKoIs1o0+hOjGvkKE23CafKL0vJ9w==", "dependencies": { - "@smithy/core": "^3.5.3", - "@smithy/middleware-serde": "^4.0.8", + "@smithy/config-resolver": "^4.1.4", + "@smithy/credential-provider-imds": "^4.0.6", "@smithy/node-config-provider": "^4.1.3", - "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-middleware": "^4.0.4", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-retry": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.12.tgz", - "integrity": "sha512-wvIH70c4e91NtRxdaLZF+mbLZ/HcC6yg7ySKUiufL6ESp6zJUSnJucZ309AvG9nqCFHSRB5I6T3Ez1Q9wCh0Ww==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-endpoints": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", + "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-middleware": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", + "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", "dependencies": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/service-error-classification": "^4.0.5", - "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", - "tslib": "^2.6.2", - "uuid": "^9.0.1" + "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-serde": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", - "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-retry": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.5.tgz", + "integrity": "sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg==", "dependencies": { - "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.5", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, @@ -8099,360 +7450,531 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-stack": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", - "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-stream": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.2.tgz", + "integrity": "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==", "dependencies": { + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-config-provider": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", - "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", "dependencies": { - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-http-handler": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", - "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", "dependencies": { - "@smithy/abort-controller": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/querystring-builder": "^4.0.4", - "@smithy/types": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/property-provider": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", - "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", + "node_modules/@aws-sdk/client-ec2": { + "version": "3.695.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.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-sdk-ec2": "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", + "@smithy/util-waiter": "^3.1.8", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "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": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/protocol-http": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", - "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.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": ">=18.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-builder": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", - "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", - "@smithy/util-uri-escape": "^4.0.0", + "@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": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", - "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@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": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/service-error-classification": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.5.tgz", - "integrity": "sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA==", - "dependencies": { - "@smithy/types": "^4.3.1" - }, - "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/shared-ini-file-loader": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", - "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.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" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/signature-v4": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", - "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-uri-escape": "^4.0.0", - "@smithy/util-utf8": "^4.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": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/smithy-client": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.3.tgz", - "integrity": "sha512-xxzNYgA0HD6ETCe5QJubsxP0hQH3QK3kbpJz3QrosBCuIWyEXLR/CO5hFb2OeawEKUxMNhz3a1nuJNN2np2RMA==", - "dependencies": { - "@smithy/core": "^3.5.3", - "@smithy/middleware-endpoint": "^4.1.11", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-stream": "^4.2.2", - "tslib": "^2.6.2" + "node": ">=16.0.0" }, - "engines": { - "node": ">=18.0.0" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/types": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", - "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "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": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/url-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", - "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.0.4", - "@smithy/types": "^4.3.1", + "@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": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-base64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", - "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.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": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", - "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", - "dependencies": { - "tslib": "^2.6.2" + "node": ">=16.0.0" }, - "engines": { - "node": ">=18.0.0" + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-node": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", - "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "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": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-buffer-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", - "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-config-provider": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", - "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "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": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.19", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.19.tgz", - "integrity": "sha512-mvLMh87xSmQrV5XqnUYEPoiFFeEGYeAKIDDKdhE2ahqitm8OHM3aSvhqL6rrK6wm1brIk90JhxDf5lf2hbrLbQ==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.0.4", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "bowser": "^2.11.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": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.19", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.19.tgz", - "integrity": "sha512-8tYnx+LUfj6m+zkUUIrIQJxPM1xVxfRBvoGHua7R/i6qAxOMjqR6CpEpDwKoIs1o0+hOjGvkKE23CafKL0vJ9w==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.1.4", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", + "@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": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-endpoints": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", - "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", + "@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": ">=18.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-hex-encoding": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", - "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-middleware": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", - "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-retry": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.5.tgz", - "integrity": "sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg==", + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.0.5", - "@smithy/types": "^4.3.1", + "@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": ">=18.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-stream": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.2.tgz", - "integrity": "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==", + "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-uri-escape": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", - "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-utf8": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", - "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2": { - "version": "3.695.0", + "node_modules/@aws-sdk/client-iam": { + "version": "3.693.0", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -8464,7 +7986,6 @@ "@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-sdk-ec2": "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", @@ -8497,15 +8018,13 @@ "@smithy/util-retry": "^3.0.9", "@smithy/util-utf8": "^3.0.0", "@smithy/util-waiter": "^3.1.8", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" + "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sso": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8552,7 +8071,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso-oidc": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sso-oidc": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8603,7 +8122,7 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sts": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sts": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8652,7 +8171,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/core": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/core": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8672,7 +8191,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-http": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-http": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8691,7 +8210,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-ini": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-ini": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8715,7 +8234,7 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-node": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-node": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8736,7 +8255,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-sso": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-sso": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8753,7 +8272,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-web-identity": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8770,7 +8289,7 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-host-header": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-host-header": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8783,7 +8302,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-logger": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-logger": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8795,7 +8314,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-recursion-detection": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8808,7 +8327,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-user-agent": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-user-agent": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8824,7 +8343,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/region-config-resolver": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/region-config-resolver": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8839,7 +8358,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/token-providers": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/token-providers": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8856,7 +8375,7 @@ "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-endpoints": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-endpoints": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8869,7 +8388,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-user-agent-browser": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8879,7 +8398,7 @@ "tslib": "^2.6.2" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-user-agent-node": { + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-user-agent-node": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -8901,7 +8420,7 @@ } } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/is-array-buffer": { + "node_modules/@aws-sdk/client-iam/node_modules/@smithy/is-array-buffer": { "version": "3.0.0", "license": "Apache-2.0", "dependencies": { @@ -8911,7 +8430,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/util-buffer-from": { + "node_modules/@aws-sdk/client-iam/node_modules/@smithy/util-buffer-from": { "version": "3.0.0", "license": "Apache-2.0", "dependencies": { @@ -8922,41 +8441,165 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/util-utf8": { + "node_modules/@aws-sdk/client-iam/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda": { + "version": "3.637.0", + "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", + "@smithy/util-waiter": "^3.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "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-sdk/client-lambda/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { "version": "3.0.0", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", + "@smithy/is-array-buffer": "^3.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam": { + "node_modules/@aws-sdk/client-s3": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/client-sso-oidc": "3.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-bucket-endpoint": "3.693.0", + "@aws-sdk/middleware-expect-continue": "3.693.0", + "@aws-sdk/middleware-flexible-checksums": "3.693.0", "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-location-constraint": "3.693.0", "@aws-sdk/middleware-logger": "3.693.0", "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-sdk-s3": "3.693.0", + "@aws-sdk/middleware-ssec": "3.693.0", "@aws-sdk/middleware-user-agent": "3.693.0", "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/signature-v4-multi-region": "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", + "@aws-sdk/xml-builder": "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-blob-browser": "^3.1.8", "@smithy/hash-node": "^3.0.9", + "@smithy/hash-stream-node": "^3.1.8", "@smithy/invalid-dependency": "^3.0.9", + "@smithy/md5-js": "^3.0.9", "@smithy/middleware-content-length": "^3.0.11", "@smithy/middleware-endpoint": "^3.2.2", "@smithy/middleware-retry": "^3.0.26", @@ -8976,6 +8619,7 @@ "@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", "@smithy/util-waiter": "^3.1.8", "tslib": "^2.6.2" @@ -8984,7 +8628,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sso": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9031,7 +8675,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sso-oidc": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso-oidc": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9082,7 +8726,7 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sts": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sts": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9131,7 +8775,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/core": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/core": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9151,7 +8795,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-http": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9170,7 +8814,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-ini": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9194,7 +8838,7 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-node": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9215,7 +8859,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-sso": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9232,7 +8876,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-web-identity": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9249,7 +8893,7 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-host-header": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9262,7 +8906,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-logger": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9274,7 +8918,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-recursion-detection": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9287,7 +8931,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-user-agent": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9303,7 +8947,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/region-config-resolver": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/region-config-resolver": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9318,7 +8962,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/token-providers": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9335,7 +8979,7 @@ "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-endpoints": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9348,7 +8992,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-user-agent-browser": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9358,7 +9002,7 @@ "tslib": "^2.6.2" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-user-agent-node": { + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { "version": "3.693.0", "license": "Apache-2.0", "dependencies": { @@ -9380,7 +9024,7 @@ } } }, - "node_modules/@aws-sdk/client-iam/node_modules/@smithy/is-array-buffer": { + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/is-array-buffer": { "version": "3.0.0", "license": "Apache-2.0", "dependencies": { @@ -9390,7 +9034,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@smithy/util-buffer-from": { + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-buffer-from": { "version": "3.0.0", "license": "Apache-2.0", "dependencies": { @@ -9401,105 +9045,7 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-lambda": { - "version": "3.637.0", - "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", - "@smithy/util-waiter": "^3.1.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/types": { - "version": "3.609.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.4", - "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-sdk/client-lambda/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/util-utf8": { + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-utf8": { "version": "3.0.0", "license": "Apache-2.0", "dependencies": { @@ -9510,56 +9056,32 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-s3": { + "node_modules/@aws-sdk/client-sagemaker": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sagemaker/-/client-sagemaker-3.693.0.tgz", + "integrity": "sha512-iInrrb7V9f0CRBiVCaaxCbpoBRQ5BqxX4elRYI6gE/pSDD2tPqmRfm4reahMtTUcKg1jaSGuvqJLfOpp0HTozQ==", "license": "Apache-2.0", "dependencies": { - "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/client-sso-oidc": "3.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-bucket-endpoint": "3.693.0", - "@aws-sdk/middleware-expect-continue": "3.693.0", - "@aws-sdk/middleware-flexible-checksums": "3.693.0", "@aws-sdk/middleware-host-header": "3.693.0", - "@aws-sdk/middleware-location-constraint": "3.693.0", "@aws-sdk/middleware-logger": "3.693.0", "@aws-sdk/middleware-recursion-detection": "3.693.0", - "@aws-sdk/middleware-sdk-s3": "3.693.0", - "@aws-sdk/middleware-ssec": "3.693.0", "@aws-sdk/middleware-user-agent": "3.693.0", "@aws-sdk/region-config-resolver": "3.693.0", - "@aws-sdk/signature-v4-multi-region": "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", - "@aws-sdk/xml-builder": "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-blob-browser": "^3.1.8", "@smithy/hash-node": "^3.0.9", - "@smithy/hash-stream-node": "^3.1.8", "@smithy/invalid-dependency": "^3.0.9", - "@smithy/md5-js": "^3.0.9", "@smithy/middleware-content-length": "^3.0.11", "@smithy/middleware-endpoint": "^3.2.2", "@smithy/middleware-retry": "^3.0.26", @@ -9579,17 +9101,20 @@ "@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", "@smithy/util-waiter": "^3.1.8", - "tslib": "^2.6.2" + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/client-sso": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.693.0.tgz", + "integrity": "sha512-QEynrBC26x6TG9ZMzApR/kZ3lmt4lEIs2D+cHuDxt6fDGzahBUsQFBwJqhizzsM97JJI5YvmJhmihoYjdSSaXA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -9635,8 +9160,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso-oidc": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/client-sso-oidc": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.693.0.tgz", + "integrity": "sha512-UEDbYlYtK/e86OOMyFR4zEPyenIxDzO2DRdz3fwVW7RzZ94wfmSwBh/8skzPTuY1G7sI064cjHW0b0QG01Sdtg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -9686,8 +9213,10 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sts": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/client-sts": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.693.0.tgz", + "integrity": "sha512-4S2y7VEtvdnjJX4JPl4kDQlslxXEZFnC50/UXVUYSt/AMc5A/GgspFNA5FVz4E3Gwpfobbf23hR2NBF8AGvYoQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -9735,8 +9264,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/core": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/core": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.693.0.tgz", + "integrity": "sha512-v6Z/kWmLFqRLDPEwl9hJGhtTgIFHjZugSfF1Yqffdxf4n1AWgtHS7qSegakuMyN5pP4K2tvUD8qHJ+gGe2Bw2A==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", @@ -9755,8 +9286,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-http": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.693.0.tgz", + "integrity": "sha512-sL8MvwNJU7ZpD7/d2VVb3by1GknIJUxzTIgYtVkDVA/ojo+KRQSSHxcj0EWWXF5DTSh2Tm+LrEug3y1ZyKHsDA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "3.693.0", @@ -9774,8 +9307,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-ini": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.693.0.tgz", + "integrity": "sha512-kvaa4mXhCCOuW7UQnBhYqYfgWmwy7WSBSDClutwSLPZvgrhYj2l16SD2lN4IfYdxARYMJJ1lFYp3/jJG/9Yk4Q==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "3.693.0", @@ -9798,8 +9333,10 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-node": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.693.0.tgz", + "integrity": "sha512-42WMsBjTNnjYxYuM3qD/Nq+8b7UdMopUq5OduMDxoM3mFTV6PXMMnfI4Z1TNnR4tYRvPXAnuNltF6xmjKbSJRA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/credential-provider-env": "3.693.0", @@ -9819,8 +9356,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-sso": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.693.0.tgz", + "integrity": "sha512-479UlJxY+BFjj3pJFYUNC0DCMrykuG7wBAXfsvZqQxKUa83DnH5Q1ID/N2hZLkxjGd4ZW0AC3lTOMxFelGzzpQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-sso": "3.693.0", @@ -9836,8 +9375,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.693.0.tgz", + "integrity": "sha512-8LB210Pr6VeCiSb2hIra+sAH4KUBLyGaN50axHtIgufVK8jbKIctTZcVY5TO9Se+1107TsruzeXS7VeqVdJfFA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "3.693.0", @@ -9853,8 +9394,10 @@ "@aws-sdk/client-sts": "^3.693.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-host-header": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.693.0.tgz", + "integrity": "sha512-BCki6sAZ5jYwIN/t3ElCiwerHad69ipHwPsDCxJQyeiOnJ8HG+lEpnVIfrnI8A0fLQNSF3Gtx6ahfBpKiv1Oug==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", @@ -9866,8 +9409,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-logger": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.693.0.tgz", + "integrity": "sha512-dXnXDPr+wIiJ1TLADACI1g9pkSB21KkMIko2u4CJ2JCBoxi5IqeTnVoa6YcC8GdFNVRl+PorZ3Zqfmf1EOTC6w==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", @@ -9878,8 +9423,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.693.0.tgz", + "integrity": "sha512-0LDmM+VxXp0u3rG0xQRWD/q6Ubi7G8I44tBPahevD5CaiDZTkmNTrVUf0VEJgVe0iCKBppACMBDkLB0/ETqkFw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", @@ -9891,8 +9438,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-user-agent": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.693.0.tgz", + "integrity": "sha512-/KUq/KEpFFbQmNmpp7SpAtFAdViquDfD2W0QcG07zYBfz9MwE2ig48ALynXm5sMpRmnG7sJXjdvPtTsSVPfkiw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "3.693.0", @@ -9907,8 +9456,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/region-config-resolver": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/region-config-resolver": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.693.0.tgz", + "integrity": "sha512-YLUkMsUY0GLW/nfwlZ69cy1u07EZRmsv8Z9m0qW317/EZaVx59hcvmcvb+W4bFqj5E8YImTjoGfE4cZ0F9mkyw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", @@ -9922,8 +9473,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/token-providers": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.693.0.tgz", + "integrity": "sha512-nDBTJMk1l/YmFULGfRbToOA2wjf+FkQT4dMgYCv+V9uSYsMzQj8A7Tha2dz9yv4vnQgYaEiErQ8d7HVyXcVEoA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", @@ -9939,8 +9492,10 @@ "@aws-sdk/client-sso-oidc": "^3.693.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/util-endpoints": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.693.0.tgz", + "integrity": "sha512-eo4F6DRQ/kxS3gxJpLRv+aDNy76DxQJL5B3DPzpr9Vkq0ygVoi4GT5oIZLVaAVIJmi6k5qq9dLsYZfWLUxJJSg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", @@ -9952,8 +9507,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.693.0.tgz", + "integrity": "sha512-6EUfuKOujtddy18OLJUaXfKBgs+UcbZ6N/3QV4iOkubCUdeM1maIqs++B9bhCbWeaeF5ORizJw5FTwnyNjE/mw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.692.0", @@ -9962,8 +9519,10 @@ "tslib": "^2.6.2" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/util-user-agent-node": { "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.693.0.tgz", + "integrity": "sha512-td0OVX8m5ZKiXtecIDuzY3Y3UZIzvxEr57Hp21NOwieqKCG2UeyQWWeGPv0FQaU7dpTkvFmVNI+tx9iB8V/Nhg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/middleware-user-agent": "3.693.0", @@ -9984,8 +9543,10 @@ } } }, - "node_modules/@aws-sdk/client-s3/node_modules/@smithy/is-array-buffer": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/is-array-buffer": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -9994,8 +9555,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-buffer-from": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/util-buffer-from": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^3.0.0", @@ -10005,8 +9568,10 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-utf8": { + "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/util-utf8": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^3.0.0", @@ -30430,6 +29995,7 @@ "@aws-sdk/client-iam": "<3.731.0", "@aws-sdk/client-lambda": "<3.731.0", "@aws-sdk/client-s3": "<3.731.0", + "@aws-sdk/client-sagemaker": "<3.696.0", "@aws-sdk/client-sfn": "<3.731.0", "@aws-sdk/client-ssm": "<3.731.0", "@aws-sdk/client-sso": "<3.731.0", From efe222395b043f941682e6004863e62cd80c17b6 Mon Sep 17 00:00:00 2001 From: Alvin Solidum Date: Tue, 1 Jul 2025 23:49:12 -0700 Subject: [PATCH 07/12] Trigger CI rerun From 67e46000a4cf0a3d101932fb75551c33af049efc Mon Sep 17 00:00:00 2001 From: aws-asolidu Date: Wed, 2 Jul 2025 14:34:00 -0700 Subject: [PATCH 08/12] feat(sagemaker-connect): Disable deeplink reconnect for phase 1 and update error page styling (#2149) ## Problem - Disable deep link reconnect logic for the Phase 1 release of the SageMaker Connect feature, as backend support is not yet ready. Logic will be re-enabled in Phase 2. - Fix styling issues on the error page. ## Solution - Comment out reconnect logic and associated tests. - Return a 200 response with a message for reconnect attempts. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- .../sagemaker/detached-server/errorPage.ts | 8 +-- .../detached-server/routes/getSessionAsync.ts | 48 ++++++++------- .../routes/getSessionAsync.test.ts | 60 ++++++++++--------- 3 files changed, 63 insertions(+), 53 deletions(-) diff --git a/packages/core/src/awsService/sagemaker/detached-server/errorPage.ts b/packages/core/src/awsService/sagemaker/detached-server/errorPage.ts index b297c8e7083..e7c02c3e2f2 100644 --- a/packages/core/src/awsService/sagemaker/detached-server/errorPage.ts +++ b/packages/core/src/awsService/sagemaker/detached-server/errorPage.ts @@ -104,19 +104,19 @@ export async function openErrorPage(title: string, message: string) { } .error-icon { - font-size: 48px; + font-size: 25px; color: #f44336; margin-bottom: 20px; } .title { - font-size: 20px; - font-weight: 600; + font-size: 14px; + font-weight: 700; margin-bottom: 12px; } .message { - font-size: 15px; + font-size: 14px; color: #cccccc; } diff --git a/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts b/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts index e956f3d7019..32c7c876945 100644 --- a/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts +++ b/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts @@ -8,7 +8,6 @@ import { IncomingMessage, ServerResponse } from 'http' import url from 'url' import { SessionStore } from '../sessionStore' -import { readServerInfo, open } from '../utils' export async function handleGetSessionAsync(req: IncomingMessage, res: ServerResponse): Promise { const parsedUrl = url.parse(req.url || '', true) @@ -38,29 +37,38 @@ export async function handleGetSessionAsync(req: IncomingMessage, res: ServerRes }) ) return + } else { + res.writeHead(200, { 'Content-Type': 'text/plain' }) + res.end( + `No session found for connection identifier: ${connectionIdentifier}. Reconnecting for deeplink is not supported yet.` + ) + return } - const status = await store.getStatus(connectionIdentifier, requestId) - if (status === 'pending') { - res.writeHead(204) - res.end() - return - } else if (status === 'not-started') { - const serverInfo = await readServerInfo() - const refreshUrl = await store.getRefreshUrl(connectionIdentifier) + // Temporarily disabling reconnect logic for the 7/3 Phase 1 launch. + // Will re-enable in the next release around 7/14. - const url = `${refreshUrl}?connection_identifier=${encodeURIComponent( - connectionIdentifier - )}&request_id=${encodeURIComponent(requestId)}&call_back_url=${encodeURIComponent( - `http://localhost:${serverInfo.port}/refresh_token` - )}` + // const status = await store.getStatus(connectionIdentifier, requestId) + // if (status === 'pending') { + // res.writeHead(204) + // res.end() + // return + // } else if (status === 'not-started') { + // const serverInfo = await readServerInfo() + // const refreshUrl = await store.getRefreshUrl(connectionIdentifier) - await open(url) - res.writeHead(202, { 'Content-Type': 'text/plain' }) - res.end('Session is not ready yet. Please retry in a few seconds.') - await store.markPending(connectionIdentifier, requestId) - return - } + // const url = `${refreshUrl}?connection_identifier=${encodeURIComponent( + // connectionIdentifier + // )}&request_id=${encodeURIComponent(requestId)}&call_back_url=${encodeURIComponent( + // `http://localhost:${serverInfo.port}/refresh_token` + // )}` + + // await open(url) + // res.writeHead(202, { 'Content-Type': 'text/plain' }) + // res.end('Session is not ready yet. Please retry in a few seconds.') + // await store.markPending(connectionIdentifier, requestId) + // return + // } } catch (err) { console.error('Error handling session async request:', err) res.writeHead(500, { 'Content-Type': 'text/plain' }) diff --git a/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSessionAsync.test.ts b/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSessionAsync.test.ts index 0de04217ef8..f8d76912b2b 100644 --- a/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSessionAsync.test.ts +++ b/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSessionAsync.test.ts @@ -8,7 +8,6 @@ import * as sinon from 'sinon' import assert from 'assert' import { SessionStore } from '../../../../../awsService/sagemaker/detached-server/sessionStore' import { handleGetSessionAsync } from '../../../../../awsService/sagemaker/detached-server/routes/getSessionAsync' -import * as utils from '../../../../../awsService/sagemaker/detached-server/utils' describe('handleGetSessionAsync', () => { let req: Partial @@ -52,43 +51,46 @@ describe('handleGetSessionAsync', () => { }) }) - it('responds with 204 if session is pending', async () => { - req = { url: '/session_async?connection_identifier=abc&request_id=req123' } - storeStub.getFreshEntry.returns(Promise.resolve(undefined)) - storeStub.getStatus.returns(Promise.resolve('pending')) + // Temporarily disabling reconnect logic for the 7/3 Phase 1 launch. + // Will re-enable in the next release around 7/14. - await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) + // it('responds with 204 if session is pending', async () => { + // req = { url: '/session_async?connection_identifier=abc&request_id=req123' } + // storeStub.getFreshEntry.returns(Promise.resolve(undefined)) + // storeStub.getStatus.returns(Promise.resolve('pending')) - assert(resWriteHead.calledWith(204)) - assert(resEnd.calledOnce) - }) + // await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) - it('responds with 202 if status is not-started and opens browser', async () => { - req = { url: '/session_async?connection_identifier=abc&request_id=req123' } + // assert(resWriteHead.calledWith(204)) + // assert(resEnd.calledOnce) + // }) - storeStub.getFreshEntry.returns(Promise.resolve(undefined)) - storeStub.getStatus.returns(Promise.resolve('not-started')) - storeStub.getRefreshUrl.returns(Promise.resolve('https://example.com/refresh')) - storeStub.markPending.returns(Promise.resolve()) + // it('responds with 202 if status is not-started and opens browser', async () => { + // req = { url: '/session_async?connection_identifier=abc&request_id=req123' } - sinon.stub(utils, 'readServerInfo').resolves({ pid: 1234, port: 4567 }) - sinon.stub(utils, 'open').resolves() - await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) + // storeStub.getFreshEntry.returns(Promise.resolve(undefined)) + // storeStub.getStatus.returns(Promise.resolve('not-started')) + // storeStub.getRefreshUrl.returns(Promise.resolve('https://example.com/refresh')) + // storeStub.markPending.returns(Promise.resolve()) - assert(resWriteHead.calledWith(202)) - assert(resEnd.calledWithMatch(/Session is not ready yet/)) - assert(storeStub.markPending.calledWith('abc', 'req123')) - }) + // sinon.stub(utils, 'readServerInfo').resolves({ pid: 1234, port: 4567 }) + // sinon.stub(utils, 'open').resolves() + // await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) - it('responds with 500 if unexpected error occurs', async () => { - req = { url: '/session_async?connection_identifier=abc&request_id=req123' } - storeStub.getFreshEntry.throws(new Error('fail')) + // assert(resWriteHead.calledWith(202)) + // assert(resEnd.calledWithMatch(/Session is not ready yet/)) + // assert(storeStub.markPending.calledWith('abc', 'req123')) + // }) - await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) + // it('responds with 500 if unexpected error occurs', async () => { + // req = { url: '/session_async?connection_identifier=abc&request_id=req123' } + // storeStub.getFreshEntry.throws(new Error('fail')) - assert(resWriteHead.calledWith(500)) - assert(resEnd.calledWith('Unexpected error')) - }) + // await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) + + // assert(resWriteHead.calledWith(500)) + // assert(resEnd.calledWith('Unexpected error')) + // }) afterEach(() => { sinon.restore() From c337ed552c53088f895f368fa82d4e45544e78a0 Mon Sep 17 00:00:00 2001 From: aws-asolidu Date: Thu, 3 Jul 2025 08:55:32 -0700 Subject: [PATCH 09/12] Merge staging into feature/sagemaker-connect (#2151) ## Problem - Merge staging into feature branch ## Solution - fix merge conflicts --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Co-authored-by: aws-toolkit-automation <> Co-authored-by: Laxman Reddy <141967714+laileni-aws@users.noreply.github.com> Co-authored-by: aws-toolkit-automation <43144436+aws-toolkit-automation@users.noreply.github.com> Co-authored-by: Roger Zhang Co-authored-by: Reed Hamilton <49345456+rhamilt@users.noreply.github.com> Co-authored-by: Jacob Chung Co-authored-by: aws-ides-bot --- package-lock.json | 11 +- package.json | 2 +- packages/amazonq/.changes/1.81.0.json | 10 + ...-8d48cfa7-278f-4b38-8121-0738bb0841b1.json | 4 - packages/amazonq/CHANGELOG.md | 4 + packages/amazonq/package.json | 30 +- packages/core/package.json | 48 +- packages/core/package.nls.json | 4 + .../icons/aws/lambda/create-stack-light.svg | 12 + .../icons/aws/lambda/create-stack.svg | 12 + .../core/resources/markdown/lambda2sam.md | 42 + .../core/resources/markdown/lambdaEdit.md | 19 + packages/core/src/auth/utils.ts | 28 +- .../src/awsService/appBuilder/activation.ts | 7 + .../appBuilder/lambda2sam/lambda2sam.ts | 1006 +++++++++++++++++ .../core/src/awsService/appBuilder/utils.ts | 503 +++++++++ packages/core/src/awsexplorer/activation.ts | 6 +- packages/core/src/commands.ts | 20 +- packages/core/src/lambda/activation.ts | 73 +- .../src/lambda/commands/downloadLambda.ts | 70 +- .../core/src/lambda/commands/editLambda.ts | 244 ++++ .../core/src/lambda/commands/uploadLambda.ts | 70 +- .../lambda/explorer/cloudFormationNodes.ts | 3 +- .../lambda/explorer/lambdaFunctionFileNode.ts | 38 + .../explorer/lambdaFunctionFolderNode.ts | 60 + .../src/lambda/explorer/lambdaFunctionNode.ts | 58 +- .../lambdaFunctionNodeDecorationProvider.ts | 107 ++ .../core/src/lambda/explorer/lambdaNodes.ts | 13 +- .../src/lambda/models/samLambdaRuntime.ts | 8 +- packages/core/src/lambda/uriHandlers.ts | 62 + packages/core/src/lambda/utils.ts | 100 +- packages/core/src/login/webview/vue/login.vue | 5 + .../core/src/shared/clients/lambdaClient.ts | 56 + .../shared/cloudformation/cloudformation.ts | 4 + .../src/shared/telemetry/vscodeTelemetry.json | 4 + .../appBuilder/lambda2sam/lambda2sam.test.ts | 272 +++++ .../lambda2sam/lambda2samCoreLogic.test.ts | 784 +++++++++++++ .../lambda2sam/lambda2samDownload.test.ts | 312 +++++ .../test/awsService/appBuilder/utils.test.ts | 562 ++++++++- .../test/lambda/commands/editLambda.test.ts | 251 ++++ .../explorer/lambdaFunctionFileNode.test.ts | 58 + .../explorer/lambdaFunctionFolderNode.test.ts | 57 + .../explorer/lambdaFunctionNode.test.ts | 74 +- ...mbdaFunctionNodeDecorationProvider.test.ts | 147 +++ .../test/lambda/explorer/lambdaNodes.test.ts | 4 +- .../core/src/test/lambda/uriHandlers.test.ts | 36 + packages/core/src/test/lambda/utils.test.ts | 183 ++- ...-b2f13a50-0474-464c-b503-611edce7c356.json | 4 + ...-fc398d68-b7e1-4f37-8746-867381f402c6.json | 4 + packages/toolkit/package.json | 124 +- 50 files changed, 5457 insertions(+), 158 deletions(-) create mode 100644 packages/amazonq/.changes/1.81.0.json delete mode 100644 packages/amazonq/.changes/next-release/Bug Fix-8d48cfa7-278f-4b38-8121-0738bb0841b1.json create mode 100644 packages/core/resources/icons/aws/lambda/create-stack-light.svg create mode 100644 packages/core/resources/icons/aws/lambda/create-stack.svg create mode 100644 packages/core/resources/markdown/lambda2sam.md create mode 100644 packages/core/resources/markdown/lambdaEdit.md create mode 100644 packages/core/src/awsService/appBuilder/lambda2sam/lambda2sam.ts create mode 100644 packages/core/src/lambda/commands/editLambda.ts create mode 100644 packages/core/src/lambda/explorer/lambdaFunctionFileNode.ts create mode 100644 packages/core/src/lambda/explorer/lambdaFunctionFolderNode.ts create mode 100644 packages/core/src/lambda/explorer/lambdaFunctionNodeDecorationProvider.ts create mode 100644 packages/core/src/lambda/uriHandlers.ts create mode 100644 packages/core/src/test/awsService/appBuilder/lambda2sam/lambda2sam.test.ts create mode 100644 packages/core/src/test/awsService/appBuilder/lambda2sam/lambda2samCoreLogic.test.ts create mode 100644 packages/core/src/test/awsService/appBuilder/lambda2sam/lambda2samDownload.test.ts create mode 100644 packages/core/src/test/lambda/commands/editLambda.test.ts create mode 100644 packages/core/src/test/lambda/explorer/lambdaFunctionFileNode.test.ts create mode 100644 packages/core/src/test/lambda/explorer/lambdaFunctionFolderNode.test.ts create mode 100644 packages/core/src/test/lambda/explorer/lambdaFunctionNodeDecorationProvider.test.ts create mode 100644 packages/core/src/test/lambda/uriHandlers.test.ts create mode 100644 packages/toolkit/.changes/next-release/Feature-b2f13a50-0474-464c-b503-611edce7c356.json create mode 100644 packages/toolkit/.changes/next-release/Feature-fc398d68-b7e1-4f37-8746-867381f402c6.json diff --git a/package-lock.json b/package-lock.json index 20cea0e60ac..4f3095262b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "vscode-nls-dev": "^4.0.4" }, "devDependencies": { - "@aws-toolkits/telemetry": "^1.0.324", + "@aws-toolkits/telemetry": "^1.0.326", "@playwright/browser-chromium": "^1.43.1", "@stylistic/eslint-plugin": "^2.11.0", "@types/he": "^1.2.3", @@ -15008,11 +15008,10 @@ } }, "node_modules/@aws-toolkits/telemetry": { - "version": "1.0.324", - "resolved": "https://registry.npmjs.org/@aws-toolkits/telemetry/-/telemetry-1.0.324.tgz", - "integrity": "sha512-O4K9Ip3ge+EdTITOhMNcVxp+DPxK/1JHm9XcDwg/5N3q9SbwQ7/WeVtTHkvgq+IiQGKjnJ/4Vuyw3/3h29K7ww==", + "version": "1.0.326", + "resolved": "https://registry.npmjs.org/@aws-toolkits/telemetry/-/telemetry-1.0.326.tgz", + "integrity": "sha512-4PpnljGgERDJpdAJdBHKb9eaDhq8ktYiWoYS/mCG2ojplGvEP/ymzfzPJ6apUErT3iu74+md1x5JL8h7N7/ZFA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "ajv": "^6.12.6", "cross-spawn": "^7.0.6", @@ -29963,7 +29962,7 @@ }, "packages/amazonq": { "name": "amazon-q-vscode", - "version": "1.81.0-SNAPSHOT", + "version": "1.82.0-SNAPSHOT", "license": "Apache-2.0", "dependencies": { "aws-core-vscode": "file:../core/" diff --git a/package.json b/package.json index 8dab28aba35..53e03dc56ce 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "skippedTestReport": "ts-node ./scripts/skippedTestReport.ts ./packages/amazonq/test/e2e/" }, "devDependencies": { - "@aws-toolkits/telemetry": "^1.0.324", + "@aws-toolkits/telemetry": "^1.0.326", "@playwright/browser-chromium": "^1.43.1", "@stylistic/eslint-plugin": "^2.11.0", "@types/he": "^1.2.3", diff --git a/packages/amazonq/.changes/1.81.0.json b/packages/amazonq/.changes/1.81.0.json new file mode 100644 index 00000000000..b93c5693ad4 --- /dev/null +++ b/packages/amazonq/.changes/1.81.0.json @@ -0,0 +1,10 @@ +{ + "date": "2025-07-02", + "version": "1.81.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Stop auto inline completion when deleting code" + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/next-release/Bug Fix-8d48cfa7-278f-4b38-8121-0738bb0841b1.json b/packages/amazonq/.changes/next-release/Bug Fix-8d48cfa7-278f-4b38-8121-0738bb0841b1.json deleted file mode 100644 index 533a519e5a1..00000000000 --- a/packages/amazonq/.changes/next-release/Bug Fix-8d48cfa7-278f-4b38-8121-0738bb0841b1.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "Stop auto inline completion when deleting code" -} diff --git a/packages/amazonq/CHANGELOG.md b/packages/amazonq/CHANGELOG.md index 518e3d406f4..78d0d6b79df 100644 --- a/packages/amazonq/CHANGELOG.md +++ b/packages/amazonq/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.81.0 2025-07-02 + +- **Bug Fix** Stop auto inline completion when deleting code + ## 1.80.0 2025-07-01 - Miscellaneous non-user-facing changes diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index cad5bcf324e..1ae1c68c298 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -2,7 +2,7 @@ "name": "amazon-q-vscode", "displayName": "Amazon Q", "description": "The most capable generative AI-powered assistant for building, operating, and transforming software, with advanced capabilities for managing data and AI", - "version": "1.81.0-SNAPSHOT", + "version": "1.82.0-SNAPSHOT", "extensionKind": [ "workspace" ], @@ -1227,98 +1227,98 @@ "fontCharacter": "\\f1d0" } }, - "aws-lambda-function": { + "aws-lambda-create-stack": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d1" } }, - "aws-mynah-MynahIconBlack": { + "aws-lambda-create-stack-light": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d2" } }, - "aws-mynah-MynahIconWhite": { + "aws-lambda-function": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d3" } }, - "aws-mynah-logo": { + "aws-mynah-MynahIconBlack": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d4" } }, - "aws-redshift-cluster": { + "aws-mynah-MynahIconWhite": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d5" } }, - "aws-redshift-cluster-connected": { + "aws-mynah-logo": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d6" } }, - "aws-redshift-database": { + "aws-redshift-cluster": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d7" } }, - "aws-redshift-redshift-cluster-connected": { + "aws-redshift-cluster-connected": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d8" } }, - "aws-redshift-schema": { + "aws-redshift-database": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d9" } }, - "aws-redshift-table": { + "aws-redshift-redshift-cluster-connected": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1da" } }, - "aws-s3-bucket": { + "aws-redshift-schema": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1db" } }, - "aws-s3-create-bucket": { + "aws-redshift-table": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1dc" } }, - "aws-sagemaker-code-editor": { + "aws-s3-bucket": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1dd" } }, - "aws-sagemaker-jupyter-lab": { + "aws-s3-create-bucket": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", diff --git a/packages/core/package.json b/packages/core/package.json index 177a0394125..7371d41159b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -309,124 +309,138 @@ "fontCharacter": "\\f1d0" } }, - "aws-lambda-function": { + "aws-lambda-create-stack": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d1" } }, - "aws-mynah-MynahIconBlack": { + "aws-lambda-create-stack-light": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d2" } }, - "aws-mynah-MynahIconWhite": { + "aws-lambda-function": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d3" } }, - "aws-mynah-logo": { + "aws-mynah-MynahIconBlack": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d4" } }, - "aws-redshift-cluster": { + "aws-mynah-MynahIconWhite": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d5" } }, - "aws-redshift-cluster-connected": { + "aws-mynah-logo": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d6" } }, - "aws-redshift-database": { + "aws-redshift-cluster": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d7" } }, - "aws-redshift-redshift-cluster-connected": { + "aws-redshift-cluster-connected": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d8" } }, - "aws-redshift-schema": { + "aws-redshift-database": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d9" } }, - "aws-redshift-table": { + "aws-redshift-redshift-cluster-connected": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1da" } }, - "aws-s3-bucket": { + "aws-redshift-schema": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1db" } }, - "aws-s3-create-bucket": { + "aws-redshift-table": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1dc" } }, - "aws-sagemaker-code-editor": { + "aws-s3-bucket": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1dd" } }, - "aws-sagemaker-jupyter-lab": { + "aws-s3-create-bucket": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1de" } }, - "aws-schemas-registry": { + "aws-sagemaker-code-editor": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1df" } }, - "aws-schemas-schema": { + "aws-sagemaker-jupyter-lab": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1e0" } }, - "aws-stepfunctions-preview": { + "aws-schemas-registry": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1e1" } + }, + "aws-schemas-schema": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e2" + } + }, + "aws-stepfunctions-preview": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e3" + } } } }, diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index 8daff18a48d..b3cf958c980 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -160,7 +160,11 @@ "AWS.command.downloadLambda": "Download...", "AWS.command.uploadLambda": "Upload Lambda...", "AWS.command.invokeLambda": "Invoke in the cloud", + "AWS.command.openLambdaFile": "Open your Lambda code", + "AWS.command.quickDeployLambda": "Save and deploy your code", + "AWS.command.openLambdaWorkspace": "Open in a workspace", "AWS.command.invokeLambda.cn": "Invoke on Amazon", + "AWS.command.lambda.convertToSam": "Convert to SAM Application", "AWS.command.refreshAwsExplorer": "Refresh Explorer", "AWS.command.refreshCdkExplorer": "Refresh CDK Explorer", "AWS.command.cdk.help": "View CDK Documentation", diff --git a/packages/core/resources/icons/aws/lambda/create-stack-light.svg b/packages/core/resources/icons/aws/lambda/create-stack-light.svg new file mode 100644 index 00000000000..3dd66689af0 --- /dev/null +++ b/packages/core/resources/icons/aws/lambda/create-stack-light.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/core/resources/icons/aws/lambda/create-stack.svg b/packages/core/resources/icons/aws/lambda/create-stack.svg new file mode 100644 index 00000000000..b8a08164556 --- /dev/null +++ b/packages/core/resources/icons/aws/lambda/create-stack.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/core/resources/markdown/lambda2sam.md b/packages/core/resources/markdown/lambda2sam.md new file mode 100644 index 00000000000..ab43a89729a --- /dev/null +++ b/packages/core/resources/markdown/lambda2sam.md @@ -0,0 +1,42 @@ +# Welcome to Lambda Development with AWS SAM + +This project was generated from existing ${sourceType} to ${stackName} stack using the AWS Toolkit for VS Code. Your Lambda functions are now a project in AWS Serverless Application Model (SAM). Here, you can manage your functions as infrastructure as code using the AWS SAM template. This eliminates the need for manual changes in the AWS Console, provides better version control, and allows automated deployments of your serverless resources. + +${warning} + +## Prerequisites + +Confirm you have installed the following tools: + +- **The AWS CLI**: Needed to interact with AWS services from the command line. +- **The AWS SAM CLI:** Needed to locally build, invoke, and deploy your functions. Version 1.98+ is required. +- **Docker**: Optional, but required if you want to invoke locally, Docker is required. + +**Note:** For help on installing these tools, choose the **Application Builder** panel in **EXPLORER** or the AWS Toolkit Extension, and select **Walkthrough of Application Builder**. + +## What you can do with AWS SAM + +Your functions are ready for local development. You can either use the **AWS Application Builder** or the **SAM CLI** to edit and manage your functions. + +To get started using Application Builder, choose the **Application Builder** panel in **EXPLORER** or the AWS Toolkit Extension, and select **Walkthrough of Application Builder**. + +Use the following SAM CLI commands to manage your functions: + +- **Build Your Code:** Run [`sam build`](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-build.html) in the terminal to compile your code and install dependencies. +- **Test Locally:** Run the [`sam local invoke`](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-invoke.html) and [`sam local start-api`](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-start-api.html)commands in the terminal. +- **Deploy Your Changes:** Run [`sam deploy --guided`](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-deploy.html) in the terminal to deploy your updated function to AWS. +- **Verify Deployment:** Run [`sam remote invoke`](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-remote-invoke.html) or go to the Lambda Console + +## Quick Reference + +- **SAM Template**: [template.yaml](./template.yaml) - Contains your infrastructure as code +- **SAM Configuration**: [samconfig.toml](./samconfig.toml) - Contains deployment configuration + +## Advanced features + +You can also debug your functions locally with breakpoints, manage environment variables, work with layers and dependencies, and configure function triggers and permissions through the AWS Toolkit interface. For more details, refer to the following resources + +- [AWS toolkit for Visual Studio Code User Guide](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +- [Working with Application Builder](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/appbuilder-overview-overview.html) +- [AWS SAM Developer Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) +- [AWS SAM command line reference](http://https//docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html) diff --git a/packages/core/resources/markdown/lambdaEdit.md b/packages/core/resources/markdown/lambdaEdit.md new file mode 100644 index 00000000000..c8842cf19ec --- /dev/null +++ b/packages/core/resources/markdown/lambdaEdit.md @@ -0,0 +1,19 @@ +# Welcome to Lambda Local Development + +Learn how to view your Lambda Function locally, iterate, and deploy changes to the AWS Cloud. + +## Edit your Lambda function + +- Make changes to your function code and save it. You will be prompted to deploy if you're done editing. You can come back later if you have more changes to make. +- Using the terminal and your favorite package manager, add dependencies for your project. + +## Manage your Lambda functions + +- Select the AWS icon in the left sidebar and select **EXPLORER** +- In your desired region, select the Lambda dropdown menu: + - To save and deploy a previously edited Lambda function, select the cloud deploy icon next to your Lambda function. + - To remotely invoke a function, select the play icon next to your Lambda function. + +## Advanced Features + +- To convert to a Lambda function to an AWS SAM application, select the ![createStack](./create-stack.svg) icon next to your Lambda function. For details on what AWS SAM is and how it can help you, see the [AWS Serverless Application Model Developer Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html). diff --git a/packages/core/src/auth/utils.ts b/packages/core/src/auth/utils.ts index 9da35fa06e5..910c5cee949 100644 --- a/packages/core/src/auth/utils.ts +++ b/packages/core/src/auth/utils.ts @@ -71,7 +71,7 @@ export async function promptForConnection(auth: Auth, type?: 'iam' | 'iam-only' if (resp === 'addNewConnection') { // We could call this command directly, but it either lives in packages/toolkit or will at some point. const source: AuthSource = 'addConnectionQuickPick' // enforcing type sanity check - await vscode.commands.executeCommand('aws.toolkit.auth.manageConnections', placeholder, source) + await vscode.commands.executeCommand('aws.toolkit.auth.manageConnections', placeholder, source, undefined, true) return undefined } @@ -89,8 +89,9 @@ export async function promptForConnection(auth: Auth, type?: 'iam' | 'iam-only' export async function promptAndUseConnection(...[auth, type]: Parameters) { return telemetry.aws_setCredentials.run(async (span) => { let conn = await promptForConnection(auth, type) + // Returning because either conn is a valid connection, or the customer selected 'addNewConnection' or 'editCredentials' if (!conn) { - throw new CancellationError('user') + return } // HACK: We assume that if we are toolkit we want AWS account scopes. @@ -113,7 +114,11 @@ export async function promptAndUseConnection(...[auth, type]: Parameters async function registerAppBuilderCommands(context: ExtContext): Promise { const source = 'AppBuilderWalkthrough' context.extensionContext.subscriptions.push( + Commands.register({ id: 'aws.toolkit.lambda.convertToSam', autoconnect: true }, async (lambdaNode) => { + await telemetry.appbuilder_lambda2sam.run(async () => { + telemetry.record({ source: 'explorer' }) + await lambdaToSam(lambdaNode) + }) + }), Commands.register('aws.toolkit.installSAMCLI', async () => { await getOrInstallCliWrapper('sam-cli', source) }), diff --git a/packages/core/src/awsService/appBuilder/lambda2sam/lambda2sam.ts b/packages/core/src/awsService/appBuilder/lambda2sam/lambda2sam.ts new file mode 100644 index 00000000000..20f7c372583 --- /dev/null +++ b/packages/core/src/awsService/appBuilder/lambda2sam/lambda2sam.ts @@ -0,0 +1,1006 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { LambdaFunctionNode } from '../../../lambda/explorer/lambdaFunctionNode' +import fs from '../../../shared/fs/fs' +import { getLogger } from '../../../shared/logger/logger' +import * as os from 'os' +import { + LAMBDA_FUNCTION_TYPE, + LAMBDA_LAYER_TYPE, + LAMBDA_URL_TYPE, + SERVERLESS_FUNCTION_TYPE, + SERVERLESS_LAYER_TYPE, + Template, + TemplateResources, + loadByContents, + save, + tryLoad, + ZipResourceProperties, + Resource, +} from '../../../shared/cloudformation/cloudformation' + +import { downloadUnzip, getLambdaClient, getCFNClient, isPermissionError } from '../utils' +import { openProjectInWorkspace } from '../walkthrough' +import { ToolkitError } from '../../../shared/errors' +import { ResourcesToImport, StackResource } from 'aws-sdk/clients/cloudformation' +import { SignatureV4 } from '@smithy/signature-v4' +import { Sha256 } from '@aws-crypto/sha256-js' +import { getIAMConnection } from '../../../auth/utils' +import globals from '../../../shared/extensionGlobals' +import { Runtime, telemetry } from '../../../shared/telemetry/telemetry' + +/** + * Information about a CloudFormation stack + */ +export interface StackInfo { + stackId: string + stackName: string + isSamTemplate: boolean + template: Template +} + +/** + * Main entry point for converting a Lambda function to a SAM project + */ +export async function lambdaToSam(lambdaNode: LambdaFunctionNode): Promise { + try { + // Show progress notification for the overall process + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: `Converting ${lambdaNode.name} to SAM project`, + cancellable: false, + }, + async (progress) => { + // 0. Prompt user for project location + const saveUri = await promptForProjectLocation() + if (!saveUri) { + getLogger().info('User canceled project location selection') + return + } + progress.report({ increment: 0, message: 'Checking stack association...' }) + + // 1. Determine which scenario applies to this Lambda function + telemetry.record({ runtime: lambdaNode?.configuration?.Runtime as Runtime | undefined }) + let stackInfo = await determineStackAssociation(lambdaNode) + + // 2. Handle the appropriate scenario + let samTemplate: Template + let sourceType: 'LambdaFunction' | 'SAMStack' | 'CFNStack' + let stackName: string | undefined + if (!stackInfo) { + telemetry.record({ action: 'deployStack' }) + // Scenario 1: Lambda doesn't belong to any stack + sourceType = 'LambdaFunction' + progress.report({ increment: 30, message: 'Generating template...' }) + // 2.1 call api to get CFN + let cfnTemplate: Template + let resourcesToImport: ResourcesToImport + try { + ;[cfnTemplate, resourcesToImport] = await callExternalApiForCfnTemplate(lambdaNode) + } catch (error) { + throw new ToolkitError(`Failed to generate template: ${error}`) + } + + // 2.2. Deploy the CFN template to create a stack + progress.report({ increment: 20, message: 'Deploying template...' }) + stackName = await promptForStackName(lambdaNode.name.replaceAll('_', '-')) + if (!stackName) { + throw new ToolkitError('Stack name not provided') + } + + stackInfo = await deployCfnTemplate( + cfnTemplate, + resourcesToImport, + stackName, + lambdaNode.regionCode + ) + samTemplate = { + AWSTemplateFormatVersion: stackInfo.template.AWSTemplateFormatVersion, + Transform: 'AWS::Serverless-2016-10-31', + Parameters: stackInfo.template.Parameters, + Globals: stackInfo.template.Globals, + Resources: stackInfo.template.Resources, + } + } else if (stackInfo.isSamTemplate) { + // Scenario 3: Lambda belongs to a stack deployed by SAM + sourceType = 'SAMStack' + progress.report({ increment: 50, message: 'Processing SAM template...' }) + samTemplate = stackInfo.template + stackName = stackInfo.stackName + } else { + // Scenario 2: Lambda belongs to a CFN stack + sourceType = 'CFNStack' + progress.report({ increment: 50, message: 'Creating SAM project from CFN...' }) + samTemplate = { + AWSTemplateFormatVersion: stackInfo.template.AWSTemplateFormatVersion, + Transform: 'AWS::Serverless-2016-10-31', + Parameters: stackInfo.template.Parameters, + Globals: stackInfo.template.Globals, + Resources: stackInfo.template.Resources, + } + stackName = stackInfo.stackName + } + + const projectUri = vscode.Uri.joinPath(saveUri[0], stackName) + + telemetry.record({ iac: sourceType }) + + // 3. Process Lambda functions in the template + if (!samTemplate.Resources) { + throw new ToolkitError('Template does not contain any resource, please retry') + } + + progress.report({ message: 'Downloading Lambda function code...' }) + await cfn2sam(samTemplate.Resources, projectUri, stackInfo, lambdaNode.regionCode) + + // 4. Save the SAM template + progress.report({ message: 'Saving SAM template...' }) + await save(samTemplate, vscode.Uri.joinPath(projectUri, 'template.yaml').fsPath) + + // 5. Create a basic README.md + // Use stack name from stackInfo if available, otherwise use the Lambda function name + progress.report({ message: 'Creating Readme...' }) + await createReadme(stackName, sourceType, projectUri) + + // 6. Create samconfig.toml + progress.report({ message: 'Creating SAM configuration...' }) + await createSAMConfig(stackName, lambdaNode.regionCode, projectUri) + + // 7. Open the project in VS Code + await openProjectInWorkspace(projectUri) + + // 8. Show success message + void vscode.window.showInformationMessage(`SAM project created successfully at ${projectUri.fsPath}`) + progress.report({ increment: 100, message: 'Done!' }) + } + ) + } catch (err) { + throw new ToolkitError(`Failed to convert Lambda to SAM: ${err instanceof Error ? err.message : String(err)}`) + } +} + +export async function createReadme( + stackName: string, + sourceType: 'LambdaFunction' | 'SAMStack' | 'CFNStack', + projectUri: vscode.Uri +) { + const warningSection = + sourceType !== 'LambdaFunction' + ? '' + : `**[Warning**: Currently only a subset of resource support converting to SAM, For any missing resources, please check the Lambda Console and add them manually to your SAM template. ]` + const lambda2SAMReadmeSource = 'resources/markdown/lambda2sam.md' + const readme = (await fs.readFileText(globals.context.asAbsolutePath(lambda2SAMReadmeSource))) + .replace(/\$\{sourceType\}/g, sourceType) + .replace(/\$\{stackName\}/g, stackName) + .replace(/\$\{warning\}/g, warningSection) + + await fs.writeFile(vscode.Uri.joinPath(projectUri, 'README.md'), readme) +} + +export async function createSAMConfig(stackName: string, region: string, projectUri: vscode.Uri) { + const samConfigContent = `# More information about the configuration file can be found here: +# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html +version = 0.1 + +[default] +[default.global.parameters] +stack_name = "${stackName}" +region = "${region}"` + await fs.writeFile(vscode.Uri.joinPath(projectUri, 'samconfig.toml'), samConfigContent) +} + +/** + * Determines if the Lambda function is associated with a CloudFormation stack + * and if that stack was deployed using SAM + */ +export async function determineStackAssociation(lambdaNode: LambdaFunctionNode): Promise { + try { + // Get Lambda function details including tags + const lambdaClient = getLambdaClient(lambdaNode.regionCode) + const functionDetails = await lambdaClient.getFunction(lambdaNode.name) + + // Check if the Lambda function has CloudFormation stack tags + if (!functionDetails.Tags) { + // Lambda doesn't have any tags, so it's not part of a stack + return undefined + } + + // Look for the CloudFormation stack ID tag + const stackIdTag = functionDetails.Tags['aws:cloudformation:stack-id'] + if (!stackIdTag) { + // Lambda doesn't have a CloudFormation stack ID tag + return undefined + } + + // Get the stack name tag if available, otherwise extract from stack ID + let stackName = functionDetails.Tags['aws:cloudformation:stack-name'] + if (!stackName) { + // Extract stack name from stack ID + const stackIdParts = stackIdTag.split('/') + stackName = stackIdParts.length > 1 ? stackIdParts[1] : '' + } + + // Create CloudFormation client + const cfn = await getCFNClient(lambdaNode.regionCode) + + // stack could be in DELETE_COMPLETE status or doesn't exist + const describeStacksResult = await cfn.describeStacks({ StackName: stackIdTag }) + if (!describeStacksResult.Stacks || describeStacksResult.Stacks.length === 0) { + return undefined + } + if (describeStacksResult.Stacks![0].StackStatus === 'DELETE_COMPLETE') { + return undefined + } + // Get the original stack template + const templateResponse = await cfn.getTemplate({ + StackName: stackIdTag, + TemplateStage: 'Original', // Critical to get the original SAM template + }) + + const templateBody = templateResponse.TemplateBody || '{}' + const template = await loadByContents(templateBody, false) + + // Determine if it's a SAM template by checking for the transform + const isSamTemplate = ifSamTemplate(template) + + return { + stackId: stackIdTag, + stackName, + isSamTemplate, + template, + } + } catch (err) { + throw new ToolkitError(`Error determining stack association: ${err}, please try again`) + } +} + +/** + * Checks if a template is a SAM template by looking for the SAM transform + */ +export function ifSamTemplate(template: Template): boolean { + // Check for SAM transform + if (template.Transform) { + if (typeof template.Transform === 'string') { + return template.Transform.startsWith('AWS::Serverless') + } else if (typeof template.Transform === 'object' && Array.isArray(template.Transform)) { + // Handle case where Transform might be an array + return template.Transform.some((t: string) => typeof t === 'string' && t.startsWith('AWS::Serverless')) + } + } + + return false +} + +/** + * Calls the external API to generate a CloudFormation template for a Lambda function + * Note: This is a placeholder for the actual API call + */ +export async function callExternalApiForCfnTemplate( + lambdaNode: LambdaFunctionNode +): Promise<[Template, ResourcesToImport]> { + const conn = await getIAMConnection() + if (!conn || conn.type !== 'iam') { + return [{}, []] + } + + const cred = await conn.getCredentials() + const signer = new SignatureV4({ + credentials: cred, + region: lambdaNode.regionCode, + service: 'lambdaconsole', + sha256: Sha256, + }) + + // TODO: govcloud URL is in a slightly different format + const url = new URL( + `https://${lambdaNode.regionCode}.prod.topology.console.lambda.aws.a2z.com/lambda-api/topology/topology?lambdaArn=${lambdaNode.arn}` + ) + + const signedRequest = await signer.sign({ + method: 'GET', + headers: { + host: url.hostname, + }, + hostname: url.hostname, + path: url.pathname, + query: Object.fromEntries(url.searchParams), + protocol: url.protocol, + }) + + const response = await fetch(url.toString(), { + method: 'GET', + headers: { + Accept: 'application/xml', + 'Content-Type': 'application/json', + ...signedRequest.headers, + }, + }) + + if (!response.ok) { + getLogger().error('Failed to retrieve generated CloudFormation template: %O', await response.json()) + throw new ToolkitError(`Failed to retrieve generated CloudFormation template ID: ${response.statusText}`) + } + + const data = await response.json() + if (!data.cloudFormationTemplateId) { + throw new ToolkitError('No template ID returned') + } + + let status: string | undefined = 'CREATE_IN_PROGRESS' + let getGeneratedTemplateResponse + let resourcesToImport: ResourcesToImport = [] + const cfn = await getCFNClient(lambdaNode.regionCode) + + // Wait for template generation to complete + while (status !== 'COMPLETE') { + getGeneratedTemplateResponse = await cfn.getGeneratedTemplate({ + Format: 'YAML', + GeneratedTemplateName: data.cloudFormationTemplateId, + }) + + status = getGeneratedTemplateResponse.Status + if (status === 'FAILED') { + throw new ToolkitError('CloudFormation template create status FAILED') + } + + // Add a small delay to avoid hitting API rate limits + if (status !== 'COMPLETE') { + await new Promise((resolve) => setTimeout(resolve, 1000)) + } + } + + // Get the generated template details to extract resource information + const describeGeneratedTemplateResponse = await cfn.describeGeneratedTemplate({ + GeneratedTemplateName: data.cloudFormationTemplateId, + }) + + if (describeGeneratedTemplateResponse.Status === 'FAILED') { + throw new ToolkitError('CloudFormation template describe request failed') + } + + // Build resourcesToImport from the generated template resources + if (describeGeneratedTemplateResponse.Resources) { + resourcesToImport = describeGeneratedTemplateResponse.Resources.filter( + (resource) => resource.LogicalResourceId && resource.ResourceType && resource.ResourceIdentifier + ).map((resource) => { + const resourceIdentifier = { ...resource.ResourceIdentifier! } + + // Fix Lambda function identifiers - extract function name from ARN + if (resource.ResourceType === 'AWS::Lambda::Function' && resourceIdentifier.FunctionName) { + // FunctionName might be returned as 'arn:aws:lambda:region:account:function:name' + // We need to extract just the function name + const functionNameOrArn = resourceIdentifier.FunctionName + if (functionNameOrArn.startsWith('arn:')) { + const arnParts = functionNameOrArn.split(':') + // ARN format: arn:aws:lambda:region:account:function:function-name + if (arnParts.length >= 7 && arnParts[5] === 'function') { + resourceIdentifier.FunctionName = arnParts[6] + } + } + } + + return { + ResourceType: resource.ResourceType!, + LogicalResourceId: resource.LogicalResourceId!, + ResourceIdentifier: resourceIdentifier, + } + }) + } + + const cfnTemplate = getGeneratedTemplateResponse!.TemplateBody + + const load = await tryLoad(vscode.Uri.from({ scheme: 'untitled' }), cfnTemplate) + if (!load.template || !load.template.Resources) { + throw new ToolkitError('Failed to load CloudFormation template') + } + + return [load.template, resourcesToImport] +} + +/** + * Prompts the user for a stack name + */ +export async function promptForStackName(defaultName: string): Promise { + return vscode.window.showInputBox({ + title: 'Enter Stack Name', + prompt: 'Enter a name for the CloudFormation stack', + value: `${defaultName}-stack`, + validateInput: (value) => { + if (!value) { + return 'Stack name is required' + } + if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(value)) { + return 'Stack name must start with a letter and contain only letters, numbers, and hyphens' + } + return undefined + }, + }) +} + +async function promptForProjectLocation(): Promise { + return vscode.window.showOpenDialog({ + canSelectFolders: true, + canSelectFiles: false, + canSelectMany: false, + openLabel: 'Select SAM project location', + // if not workspace, use home dir + defaultUri: vscode.workspace.workspaceFolders?.[0]?.uri ?? vscode.Uri.file(os.homedir()), + }) +} + +/** + * Deploys a CloudFormation template to create a stack and imports the existing Lambda function + */ +export async function deployCfnTemplate( + template: Template, + resourcesToImport: ResourcesToImport, + stackName: string, + region: string +): Promise { + const cfn = await getCFNClient(region) + + removeUnwantedCodeParameters(template) + + // Convert template object to JSON string + const templateBody = JSON.stringify(template) + + // Create a change set to import the existing resources + const changeSetName = `ImportLambda-${Date.now()}` + const changeSetResponse = await cfn.createChangeSet({ + StackName: stackName, + ChangeSetName: changeSetName, + ChangeSetType: 'IMPORT', + TemplateBody: templateBody, + Capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND'], + ResourcesToImport: resourcesToImport, + }) + + if (!changeSetResponse.Id) { + throw new ToolkitError('Failed to create change set') + } + + // Wait for change set creation to complete + await cfn + .waitFor('changeSetCreateComplete', { + StackName: stackName, + ChangeSetName: changeSetName, + $waiter: { + delay: 2, + }, + }) + .catch(async (err: any) => { + // If the change set failed to create, get the status reason + const describeResponse = await cfn.describeChangeSet({ + StackName: stackName, + ChangeSetName: changeSetName, + }) + + throw new ToolkitError(`Change set creation failed: ${describeResponse.StatusReason || err.message}`) + }) + + // Execute the change set + await cfn.executeChangeSet({ + StackName: stackName, + ChangeSetName: changeSetName, + }) + + // Wait for stack import to complete + await cfn + .waitFor('stackImportComplete', { + StackName: stackName, + $waiter: { + delay: 2, + }, + }) + .catch(async () => { + // If the stack import failed, wait for stack update complete instead + // (AWS SDK might not have stackImportComplete waiter) + await cfn.waitFor('stackUpdateComplete', { + StackName: stackName, + $waiter: { + delay: 2, + }, + }) + }) + + // Get the stack ID + const describeStackResponse = await cfn.describeStacks({ + StackName: stackName, + }) + + if (!describeStackResponse.Stacks || !describeStackResponse.Stacks[0].StackId) { + throw new ToolkitError('Failed to get stack information') + } + + // Return information about the deployed stack + return { + stackId: describeStackResponse.Stacks[0].StackId, + stackName, + template, + isSamTemplate: false, + } +} + +export function removeUnwantedCodeParameters(template: Template) { + if (!template.Resources) { + throw new Error('No Resources found in template') + } + + const lambdaKey = Object.keys(template.Resources).find( + (key) => template.Resources![key]?.Type === 'AWS::Lambda::Function' + ) + + if (!lambdaKey) { + throw new Error('No Lambda function found in template') + } + + template.Resources[lambdaKey]!.Properties!.Code = { + ZipFile: '', + } + + template.Parameters = {} +} + +/** + * Extracts the logical ID from an intrinsic function like !Ref or !GetAtt + * Returns undefined if the value is not an intrinsic function + */ +export function extractLogicalIdFromIntrinsic(value: any): string | undefined { + // Check for Ref: { "Ref": "logicalId" } + if (typeof value === 'object' && value !== null && Object.keys(value).length === 1 && value.Ref) { + return value.Ref + } + + // Check for GetAtt: { "Fn::GetAtt": ["logicalId", "Arn"] } + if ( + typeof value === 'object' && + value !== null && + Object.keys(value).length === 1 && + value['Fn::GetAtt'] && + Array.isArray(value['Fn::GetAtt']) && + value['Fn::GetAtt'].length === 2 && + value['Fn::GetAtt'][1] === 'Arn' + ) { + return value['Fn::GetAtt'][0] + } + + return undefined +} + +/** + * the main tansform to convert a CFN template to a sam project + * @param resources the parsed CFN template + * @param projectDir selected local location for project + * @param stackInfo + * @param region + */ +export async function cfn2sam( + resources: TemplateResources, + projectDir: vscode.Uri, + stackInfo: StackInfo, + region: string +): Promise { + const lambdaProcess = processLambdaResources(resources, projectDir, stackInfo, region) + const lambdaLayerProcess = processLambdaLayerResources(resources, projectDir, stackInfo, region) + await Promise.all([lambdaProcess, lambdaLayerProcess]) + await processLambdaUrlResources(resources) +} + +/** + * Processes Lambda resources in a template, transforming AWS::Lambda::Function to AWS::Serverless::Function + */ +export function Lambda2Serverless(resourceProp: ZipResourceProperties, key: string): Resource { + const dlqConfig = resourceProp.DeadLetterConfig + return { + Type: SERVERLESS_FUNCTION_TYPE, + Metadata: resourceProp.Metadata, + Properties: { + ...resourceProp, + // Transform Tags from array to object format + Tags: resourceProp.Tags + ? Object.fromEntries( + resourceProp.Tags.filter((item: { Key: any; Value: any }) => item.Key !== 'lambda:createdBy').map( + (item: { Key: any; Value: any }) => [item.Key, item.Value] + ) + ) + : undefined, + // Remove Code property (S3 reference) + Code: undefined, + // Map TracingConfig.Mode to Tracing property + Tracing: resourceProp.TracingConfig?.Mode, + TracingConfig: undefined, + // Transform DeadLetterConfig to DeadLetterQueue + DeadLetterQueue: dlqConfig + ? { + Type: dlqConfig.TargetArn.split(':')[2] === 'sqs' ? 'SQS' : 'SNS', + TargetArn: dlqConfig.TargetArn, + } + : undefined, + // Set CodeUri to the local path + CodeUri: key, + }, + } +} + +/** + * Processes Lambda URL resources in a template, transforming AWS::Lambda::Url to AWS::Serverless::Function.FunctionUrlConfig + */ +export async function processLambdaResources( + resources: TemplateResources, + projectDir: vscode.Uri, + stackInfo: StackInfo, + region: string +): Promise { + await Promise.all( + Object.entries(resources).map(async ([key, resource]) => { + if (!resource) { + return + } + + const resourceProp = resource.Properties + if (!resourceProp || resourceProp.PackageType === 'Image') { + return + } + + if (resource.Type === LAMBDA_FUNCTION_TYPE) { + // Transform AWS::Lambda::Function to AWS::Serverless::Function + try { + await downloadLambdaFunctionCode(key, stackInfo, projectDir, region, resourceProp.FunctionName) + + // Transform to Serverless Function + resources[key] = Lambda2Serverless(resourceProp, key) + } catch (err) { + throw new ToolkitError( + `Failed to process Lambda function ${key}: ${err instanceof Error ? err.message : String(err)}` + ) + } + } else if (resource.Type === SERVERLESS_FUNCTION_TYPE) { + // Update CodeUri for AWS::Serverless::Function + try { + await downloadLambdaFunctionCode(key, stackInfo, projectDir, region, resourceProp.FunctionName) + // Update the CodeUri to point to the local directory + resourceProp.CodeUri = key + } catch (err) { + throw new ToolkitError( + `Failed to process Serverless function ${key}: ${err instanceof Error ? err.message : String(err)}` + ) + } + } + }) + ) +} + +/** + * Processes Lambda Layer resources in a template, transforming AWS::Lambda::LayerVersion to AWS::Serverless::LayerVersion + */ +export async function processLambdaLayerResources( + resources: TemplateResources, + projectDir: vscode.Uri, + stackInfo: StackInfo, + region: string +): Promise { + // Process each resource + await Promise.all( + Object.entries(resources).map(async ([key, resource]) => { + if (!resource || (resource.Type !== LAMBDA_LAYER_TYPE && resource.Type !== SERVERLESS_LAYER_TYPE)) { + return + } + + const resourceProp = resource.Properties + if (!resourceProp) { + return + } + + try { + // Download the layer code + await downloadLayerVersionResourceByName(key, stackInfo, projectDir, region) + + // Transform to Serverless LayerVersion + resources[key] = { + Type: SERVERLESS_LAYER_TYPE, + Properties: { + ...resourceProp, + // Remove Content property (S3 reference) + Content: undefined, + // Set ContentUri to the local path + ContentUri: key, + }, + } + + getLogger().info(`Successfully transformed Lambda Layer ${key} to Serverless LayerVersion`) + } catch (err) { + throw new ToolkitError( + `Failed to process Lambda Layer ${key}: ${err instanceof Error ? err.message : String(err)}` + ) + } + }) + ) +} + +/** + * Processes Lambda URL resources in a template, transforming AWS::Lambda::Url to AWS::Serverless::Function.FunctionUrlConfig + */ +export async function processLambdaUrlResources(resources: TemplateResources): Promise { + for (const [key, resource] of Object.entries(resources)) { + if (resource && resource.Type === LAMBDA_URL_TYPE) { + try { + const resourceProp = resource.Properties + if (!resourceProp) { + continue + } + + // Skip if Qualifier is present (not supported in FunctionUrlConfig) + if (resourceProp.Qualifier) { + getLogger().info( + `Skipping Lambda URL ${key} because Qualifier is not supported in FunctionUrlConfig` + ) + continue + } + + // Find the target function using TargetFunctionArn + const targetFunctionArn = resourceProp.TargetFunctionArn + if (!targetFunctionArn) { + getLogger().warn(`Lambda URL ${key} does not have a TargetFunctionArn`) + continue + } + + const targetFunctionKey = extractLogicalIdFromIntrinsic(targetFunctionArn) + if (!targetFunctionKey) { + getLogger().debug(`Could not extract logical ID from TargetFunctionArn in Lambda URL ${key}`) + continue + } + + const targetFunction = resources[targetFunctionKey] + // if MyLambdaFunction 's url is not formated as MyLambdaFunctionUrl, then we shouldn't transform it + if ( + !targetFunction || + targetFunction.Type !== SERVERLESS_FUNCTION_TYPE || + targetFunctionKey + 'Url' !== key + ) { + getLogger().debug(`Target function ${targetFunctionKey} not found or not a Serverless Function`) + continue + } + + // Add FunctionUrlConfig to the Serverless Function + if (!targetFunction.Properties) { + // skip if target function is not correctly setup + continue + } + + // Now we can safely add FunctionUrlConfig + if (targetFunction.Properties) { + targetFunction.Properties.FunctionUrlConfig = { + AuthType: resourceProp.AuthType, + Cors: resourceProp.Cors, + InvokeMode: resourceProp.InvokeMode, + } + } + + // Remove the original Lambda URL resource + delete resources[key] + + getLogger().info( + `Successfully transformed Lambda URL ${key} to FunctionUrlConfig in ${targetFunctionKey}` + ) + } catch (err) { + throw new ToolkitError( + `Failed to process Lambda URL ${key}: ${err instanceof Error ? err.message : String(err)}` + ) + } + } + } +} + +/** + * Download lambda function code based on logical resource ID or physical resrouce ID + * If logical id is given, it will try to find the physical id first and then download the code + * If physical id is given, it will download the code directly + * @param resourceName logical name of Lambda function in CFN template + * @param stackInfo + * @param targetDir Local location to store the code + * @param region + * @param physicalResourceId Physical name of Lambda function + */ +export async function downloadLambdaFunctionCode( + resourceName: string, // This is the logical name from CFN + stackInfo: StackInfo, + targetDir: vscode.Uri, + region: string, + physicalResourceId?: string +) { + try { + if (!physicalResourceId || typeof physicalResourceId !== 'string') { + physicalResourceId = await getPhysicalIdfromCFNResourceName( + resourceName, + region, + stackInfo.stackId, + LAMBDA_FUNCTION_TYPE + ) + if (!physicalResourceId) { + throw new ToolkitError(`Could not find physical resource ID for ${resourceName}`) + } + } + + const lambdaClient = getLambdaClient(region) + const functionDetails = await lambdaClient.getFunction(physicalResourceId) + + if (!functionDetails.Code || !functionDetails.Code.Location) { + throw new ToolkitError(`Could not determine code location for function: ${physicalResourceId}`) + } + + const outputPath = vscode.Uri.joinPath(targetDir, resourceName) + await downloadUnzip(functionDetails.Code.Location, outputPath) + + getLogger().info(`Successfully downloaded and extracted: ${resourceName}`) + } catch (err) { + throw new ToolkitError( + `Failed to download resource ${resourceName}: ${err instanceof Error ? err.message : String(err)}` + ) + } +} + +/** + * Get physical resource ID from CFN resource name + * @param name CFN resource name + * @param region + * @param stackId + * @returns Physical resrouce ID + */ +export async function getPhysicalIdfromCFNResourceName( + name: string, + region: string, + stackId: string, + resourceType: string +): Promise { + // Create CloudFormation client + const cfn = await getCFNClient(region) + + try { + // First try the exact match approach + let describeResult + try { + describeResult = await cfn.describeStackResource({ + StackName: stackId, + LogicalResourceId: name, + }) + } catch (error) { + // If it's a permission error, re-throw it immediately + if (isPermissionError(error)) { + throw error + } + // For other errors (like ResourceNotFound), continue to fuzzy matching + describeResult = undefined + } + + if (describeResult?.StackResourceDetail?.PhysicalResourceId) { + const physicalResourceId = describeResult.StackResourceDetail.PhysicalResourceId + getLogger().debug(`Resource ${name} found with exact match, physical ID: ${physicalResourceId}`) + return physicalResourceId + } + + // only do fuzzy matching for layer, function doesn't have random suffix + if (resourceType === LAMBDA_FUNCTION_TYPE || resourceType === SERVERLESS_FUNCTION_TYPE) { + throw new ToolkitError(`Could not find physical resource ID for ${name}`) + } + + // If exact match fails, get all resources and try fuzzy matching + getLogger().debug(`Resource ${name} not found with exact match, trying fuzzy match...`) + const resources = await cfn.describeStackResources({ + StackName: stackId, + }) + + if (!resources.StackResources || resources.StackResources.length === 0) { + getLogger().debug(`No resources found in stack ${stackId}`) + return undefined + } + + // Find resources that start with the given name (SAM transform often adds suffixes) + const matchingResources = resources.StackResources.filter((resource: StackResource) => + resource.LogicalResourceId.startsWith(name) + ) + + if (matchingResources.length === 0) { + // Try a more flexible approach - check if the resource name is a substring + const substringMatches = resources.StackResources.filter((resource: StackResource) => + resource.LogicalResourceId.includes(name) + ) + + if (substringMatches.length === 0) { + getLogger().debug(`No fuzzy matches found for resource ${name}`) + return undefined + } + + // Use the first substring match + const match = substringMatches[0] + getLogger().debug( + `Resource ${name} matched with ${match.LogicalResourceId} using substring match, physical ID: ${match.PhysicalResourceId}` + ) + return match.PhysicalResourceId + } + + // If we have multiple matches, prefer exact prefix match + // Sort by length to get the closest match (shortest additional suffix) + matchingResources.sort( + (a: StackResource, b: StackResource) => a.LogicalResourceId.length - b.LogicalResourceId.length + ) + + const bestMatch = matchingResources[0] + getLogger().debug( + `Resource ${name} matched with ${bestMatch.LogicalResourceId} using prefix match, physical ID: ${bestMatch.PhysicalResourceId}` + ) + return bestMatch.PhysicalResourceId + } catch (err) { + throw ToolkitError.chain(err, `Error finding physical ID for resource ${name}, please retry`) + } +} + +/** + * Download a Lambda Layer resource by name and stack info + * @param resourceName Layer's Logical name from CFN + * @param stackInfo + * @param targetDir local location to store + * @param region + */ +export async function downloadLayerVersionResourceByName( + resourceName: string, // This is the logical name from CFN + stackInfo: StackInfo, + targetDir: vscode.Uri, + region: string +) { + try { + const physicalResourceId = await getPhysicalIdfromCFNResourceName( + resourceName, + region, + stackInfo.stackId, + LAMBDA_LAYER_TYPE + ) + if (!physicalResourceId) { + throw new ToolkitError(`Could not find physical resource ID for ${resourceName}`) + } + + getLogger().debug(`Resource ${resourceName} has physical ID ${physicalResourceId} and type LayerVersion`) + + // Parse the ARN to extract layer name and version + // Format: arn:aws:lambda:region:account-id:layer:layer-name:version + const arnParts = physicalResourceId.split(':') + if (arnParts.length < 8) { + throw new ToolkitError(`Invalid layer ARN format: ${physicalResourceId}`) + } + + const layerName = arnParts[6] + const version = parseInt(arnParts[7], 10) + + if (isNaN(version)) { + throw new ToolkitError(`Invalid version number in layer ARN: ${physicalResourceId}`) + } + + getLogger().debug(`Extracted layer name: ${layerName}, version: ${version} from ARN`) + + const lambdaClient = getLambdaClient(region) + + // Get the layer version details directly using the extracted name and version + const layerDetails = await lambdaClient.getLayerVersion(layerName, version) + + if (!layerDetails.Content || !layerDetails.Content.Location) { + throw new ToolkitError(`Could not determine code location for layer: ${layerName}:${version}`) + } + + // Download Lambda layer code using the presigned URL + const presignedUrl = layerDetails.Content.Location + + // Use node-fetch to download from the presigned URL + const outputPath = vscode.Uri.joinPath(targetDir, resourceName) + await downloadUnzip(presignedUrl, outputPath) + + getLogger().info(`Successfully downloaded and extracted layer ${layerName}:${version} to: ${resourceName}`) + } catch (err) { + throw new ToolkitError( + `Failed to download resource ${resourceName}: ${err instanceof Error ? err.message : String(err)}, please retry` + ) + } +} diff --git a/packages/core/src/awsService/appBuilder/utils.ts b/packages/core/src/awsService/appBuilder/utils.ts index 63b116b20eb..ba5b3baf7f8 100644 --- a/packages/core/src/awsService/appBuilder/utils.ts +++ b/packages/core/src/awsService/appBuilder/utils.ts @@ -19,8 +19,479 @@ import fs from '../../shared/fs/fs' import { getLogger } from '../../shared/logger/logger' import { RuntimeFamily, getFamily } from '../../lambda/models/samLambdaRuntime' import { showMessage } from '../../shared/utilities/messages' +import { DefaultLambdaClient } from '../../shared/clients/lambdaClient' +import AdmZip from 'adm-zip' +import { CloudFormation, Lambda } from 'aws-sdk' +import { isAwsError, UnknownError } from '../../shared/errors' const localize = nls.loadMessageBundle() +/** + * Interface for mapping AWS service actions to their required permissions + */ +interface PermissionMapping { + service: 'cloudformation' | 'lambda' + action: string + requiredPermissions: string[] + documentation?: string +} + +/** + * Comprehensive mapping of AWS service actions to their required permissions + */ +const PermissionMappings: PermissionMapping[] = [ + // CloudFormation permissions + { + service: 'cloudformation', + action: 'describeStacks', + requiredPermissions: ['cloudformation:DescribeStacks'], + documentation: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_DescribeStacks.html', + }, + { + service: 'cloudformation', + action: 'getTemplate', + requiredPermissions: ['cloudformation:GetTemplate'], + documentation: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_GetTemplate.html', + }, + { + service: 'cloudformation', + action: 'createChangeSet', + requiredPermissions: ['cloudformation:CreateChangeSet'], + documentation: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_CreateChangeSet.html', + }, + { + service: 'cloudformation', + action: 'executeChangeSet', + requiredPermissions: ['cloudformation:ExecuteChangeSet'], + documentation: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_ExecuteChangeSet.html', + }, + { + service: 'cloudformation', + action: 'describeChangeSet', + requiredPermissions: ['cloudformation:DescribeChangeSet'], + documentation: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_DescribeChangeSet.html', + }, + { + service: 'cloudformation', + action: 'describeStackResources', + requiredPermissions: ['cloudformation:DescribeStackResources'], + documentation: + 'https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_DescribeStackResources.html', + }, + { + service: 'cloudformation', + action: 'describeStackResource', + requiredPermissions: ['cloudformation:DescribeStackResource'], + documentation: + 'https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_DescribeStackResource.html', + }, + { + service: 'cloudformation', + action: 'getGeneratedTemplate', + requiredPermissions: ['cloudformation:GetGeneratedTemplate'], + documentation: + 'https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_GetGeneratedTemplate.html', + }, + { + service: 'cloudformation', + action: 'describeGeneratedTemplate', + requiredPermissions: ['cloudformation:DescribeGeneratedTemplate'], + documentation: + 'https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_DescribeGeneratedTemplate.html', + }, + // Lambda permissions + { + service: 'lambda', + action: 'getFunction', + requiredPermissions: ['lambda:GetFunction'], + documentation: 'https://docs.aws.amazon.com/lambda/latest/api/API_GetFunction.html', + }, + { + service: 'lambda', + action: 'listFunctions', + requiredPermissions: ['lambda:ListFunctions'], + documentation: 'https://docs.aws.amazon.com/lambda/latest/api/API_ListFunctions.html', + }, + { + service: 'lambda', + action: 'getLayerVersion', + requiredPermissions: ['lambda:GetLayerVersion'], + documentation: 'https://docs.aws.amazon.com/lambda/latest/api/API_GetLayerVersion.html', + }, + { + service: 'lambda', + action: 'listLayerVersions', + requiredPermissions: ['lambda:ListLayerVersions'], + documentation: 'https://docs.aws.amazon.com/lambda/latest/api/API_ListLayerVersions.html', + }, + { + service: 'lambda', + action: 'listFunctionUrlConfigs', + requiredPermissions: ['lambda:GetFunctionUrlConfig'], + documentation: 'https://docs.aws.amazon.com/lambda/latest/api/API_GetFunctionUrlConfig.html', + }, + { + service: 'lambda', + action: 'updateFunctionCode', + requiredPermissions: ['lambda:UpdateFunctionCode'], + documentation: 'https://docs.aws.amazon.com/lambda/latest/api/API_UpdateFunctionCode.html', + }, + { + service: 'lambda', + action: 'deleteFunction', + requiredPermissions: ['lambda:DeleteFunction'], + documentation: 'https://docs.aws.amazon.com/lambda/latest/api/API_DeleteFunction.html', + }, + { + service: 'lambda', + action: 'invoke', + requiredPermissions: ['lambda:InvokeFunction'], + documentation: 'https://docs.aws.amazon.com/lambda/latest/api/API_Invoke.html', + }, +] + +/** + * Creates an enhanced error message for permission-related failures + */ +function createEnhancedPermissionError( + originalError: unknown, + service: 'cloudformation' | 'lambda', + action: string, + resourceArn?: string +): ToolkitError { + const mapping = PermissionMappings.find((m) => m.service === service && m.action === action) + + if (!mapping) { + return ToolkitError.chain(originalError, `Permission denied for ${service}:${action}`) + } + + const permissionsList = mapping.requiredPermissions.map((p) => ` - ${p}`).join('\n') + const resourceInfo = resourceArn ? `\nResource: ${resourceArn}` : '' + + const message = `Permission denied: Missing required permissions for ${service}:${action} + +Required permissions: +${permissionsList}${resourceInfo} + +To fix this issue: +1. Contact your AWS administrator to add the missing permissions +2. Add these permissions to your IAM user/role policy +3. If using IAM roles, ensure the role has these permissions attached + +${mapping.documentation ? `Documentation: ${mapping.documentation}` : ''}` + + return new ToolkitError(message, { + code: 'InsufficientPermissions', + cause: UnknownError.cast(originalError), + details: { + service, + action, + requiredPermissions: mapping.requiredPermissions, + resourceArn, + }, + }) +} + +/** + * Checks if an error is a permission-related error + */ +export function isPermissionError(error: unknown): boolean { + return ( + isAwsError(error) && + (error.code === 'AccessDeniedException' || + error.code === 'UnauthorizedOperation' || + error.code === 'Forbidden' || + error.code === 'AccessDenied' || + (error as any).statusCode === 403) + ) +} + +/** + * Enhanced Lambda client wrapper that provides better error messages for permission issues + */ +export class EnhancedLambdaClient { + constructor( + private readonly client: DefaultLambdaClient, + private readonly regionCode: string + ) {} + + async deleteFunction(name: string): Promise { + try { + return await this.client.deleteFunction(name) + } catch (error) { + if (isPermissionError(error)) { + throw createEnhancedPermissionError( + error, + 'lambda', + 'deleteFunction', + `arn:aws:lambda:${this.regionCode}:*:function:${name}` + ) + } + throw error + } + } + + async invoke(name: string, payload?: Lambda.InvocationRequest['Payload']): Promise { + try { + return await this.client.invoke(name, payload) + } catch (error) { + if (isPermissionError(error)) { + throw createEnhancedPermissionError( + error, + 'lambda', + 'invoke', + `arn:aws:lambda:${this.regionCode}:*:function:${name}` + ) + } + throw error + } + } + + async *listFunctions(): AsyncIterableIterator { + try { + yield* this.client.listFunctions() + } catch (error) { + if (isPermissionError(error)) { + throw createEnhancedPermissionError(error, 'lambda', 'listFunctions') + } + throw error + } + } + + async getFunction(name: string): Promise { + try { + return await this.client.getFunction(name) + } catch (error) { + if (isPermissionError(error)) { + throw createEnhancedPermissionError( + error, + 'lambda', + 'getFunction', + `arn:aws:lambda:${this.regionCode}:*:function:${name}` + ) + } + throw error + } + } + + async getLayerVersion(name: string, version: number): Promise { + try { + return await this.client.getLayerVersion(name, version) + } catch (error) { + if (isPermissionError(error)) { + throw createEnhancedPermissionError( + error, + 'lambda', + 'getLayerVersion', + `arn:aws:lambda:${this.regionCode}:*:layer:${name}:${version}` + ) + } + throw error + } + } + + async *listLayerVersions(name: string): AsyncIterableIterator { + try { + yield* this.client.listLayerVersions(name) + } catch (error) { + if (isPermissionError(error)) { + throw createEnhancedPermissionError( + error, + 'lambda', + 'listLayerVersions', + `arn:aws:lambda:${this.regionCode}:*:layer:${name}` + ) + } + throw error + } + } + + async getFunctionUrlConfigs(name: string): Promise { + try { + return await this.client.getFunctionUrlConfigs(name) + } catch (error) { + if (isPermissionError(error)) { + throw createEnhancedPermissionError( + error, + 'lambda', + 'listFunctionUrlConfigs', + `arn:aws:lambda:${this.regionCode}:*:function:${name}` + ) + } + throw error + } + } + + async updateFunctionCode(name: string, zipFile: Uint8Array): Promise { + try { + return await this.client.updateFunctionCode(name, zipFile) + } catch (error) { + if (isPermissionError(error)) { + throw createEnhancedPermissionError( + error, + 'lambda', + 'updateFunctionCode', + `arn:aws:lambda:${this.regionCode}:*:function:${name}` + ) + } + throw error + } + } +} + +/** + * Enhanced CloudFormation client wrapper that provides better error messages for permission issues + */ +export class EnhancedCloudFormationClient { + constructor( + private readonly client: CloudFormation, + private readonly regionCode: string + ) {} + + async describeStacks(params: CloudFormation.DescribeStacksInput): Promise { + try { + return await this.client.describeStacks(params).promise() + } catch (error) { + if (isPermissionError(error)) { + const stackArn = params.StackName + ? `arn:aws:cloudformation:${this.regionCode}:*:stack/${params.StackName}/*` + : undefined + throw createEnhancedPermissionError(error, 'cloudformation', 'describeStacks', stackArn) + } + throw error + } + } + + async getTemplate(params: CloudFormation.GetTemplateInput): Promise { + try { + return await this.client.getTemplate(params).promise() + } catch (error) { + if (isPermissionError(error)) { + const stackArn = params.StackName + ? `arn:aws:cloudformation:${this.regionCode}:*:stack/${params.StackName}/*` + : undefined + throw createEnhancedPermissionError(error, 'cloudformation', 'getTemplate', stackArn) + } + throw error + } + } + + async createChangeSet(params: CloudFormation.CreateChangeSetInput): Promise { + try { + return await this.client.createChangeSet(params).promise() + } catch (error) { + if (isPermissionError(error)) { + const stackArn = params.StackName + ? `arn:aws:cloudformation:${this.regionCode}:*:stack/${params.StackName}/*` + : undefined + throw createEnhancedPermissionError(error, 'cloudformation', 'createChangeSet', stackArn) + } + throw error + } + } + + async executeChangeSet( + params: CloudFormation.ExecuteChangeSetInput + ): Promise { + try { + return await this.client.executeChangeSet(params).promise() + } catch (error) { + if (isPermissionError(error)) { + const stackArn = params.StackName + ? `arn:aws:cloudformation:${this.regionCode}:*:stack/${params.StackName}/*` + : undefined + throw createEnhancedPermissionError(error, 'cloudformation', 'executeChangeSet', stackArn) + } + throw error + } + } + + async describeChangeSet( + params: CloudFormation.DescribeChangeSetInput + ): Promise { + try { + return await this.client.describeChangeSet(params).promise() + } catch (error) { + if (isPermissionError(error)) { + const stackArn = params.StackName + ? `arn:aws:cloudformation:${this.regionCode}:*:stack/${params.StackName}/*` + : undefined + throw createEnhancedPermissionError(error, 'cloudformation', 'describeChangeSet', stackArn) + } + throw error + } + } + + async describeStackResources( + params: CloudFormation.DescribeStackResourcesInput + ): Promise { + try { + return await this.client.describeStackResources(params).promise() + } catch (error) { + if (isPermissionError(error)) { + const stackArn = params.StackName + ? `arn:aws:cloudformation:${this.regionCode}:*:stack/${params.StackName}/*` + : undefined + throw createEnhancedPermissionError(error, 'cloudformation', 'describeStackResources', stackArn) + } + throw error + } + } + + async describeStackResource( + params: CloudFormation.DescribeStackResourceInput + ): Promise { + try { + return await this.client.describeStackResource(params).promise() + } catch (error) { + if (isPermissionError(error)) { + const stackArn = params.StackName + ? `arn:aws:cloudformation:${this.regionCode}:*:stack/${params.StackName}/*` + : undefined + throw createEnhancedPermissionError(error, 'cloudformation', 'describeStackResource', stackArn) + } + throw error + } + } + + async getGeneratedTemplate( + params: CloudFormation.GetGeneratedTemplateInput + ): Promise { + try { + return await this.client.getGeneratedTemplate(params).promise() + } catch (error) { + if (isPermissionError(error)) { + throw createEnhancedPermissionError(error, 'cloudformation', 'getGeneratedTemplate') + } + throw error + } + } + + async describeGeneratedTemplate( + params: CloudFormation.DescribeGeneratedTemplateInput + ): Promise { + try { + return await this.client.describeGeneratedTemplate(params).promise() + } catch (error) { + if (isPermissionError(error)) { + throw createEnhancedPermissionError(error, 'cloudformation', 'describeGeneratedTemplate') + } + throw error + } + } + + async waitFor(state: string, params: any): Promise { + try { + return await this.client.waitFor(state as any, params).promise() + } catch (error) { + if (isPermissionError(error)) { + // For waitFor operations, we'll provide a generic permission error since the specific action varies + throw createEnhancedPermissionError(error, 'cloudformation', 'describeStacks') + } + throw error + } + } +} + export async function runOpenTemplate(arg?: TreeNode) { const templateUri = arg ? (arg.resource as SamAppLocation).samTemplateUri : await promptUserForTemplate() if (!templateUri || !(await fs.exists(templateUri))) { @@ -199,3 +670,35 @@ export async function deployTypePrompt() { } return selected } + +export async function downloadUnzip(url: string, destination: vscode.Uri) { + const response = await fetch(url) + if (!response.ok) { + throw new Error(`Failed to download Lambda layer code: ${response.statusText}`) + } + + // Get the response as an ArrayBuffer + const arrayBuffer = await response.arrayBuffer() + const zipBuffer = Buffer.from(arrayBuffer) + + // Create AdmZip instance with the buffer + const zip = new AdmZip(zipBuffer) + + // Create output directory if it doesn't exist + if (!(await fs.exists(destination))) { + await fs.mkdir(destination) + } + + // Extract zip contents to output path + zip.extractAllTo(destination.fsPath, true) +} + +export function getLambdaClient(region: string): EnhancedLambdaClient { + const originalClient = new DefaultLambdaClient(region) + return new EnhancedLambdaClient(originalClient, region) +} + +export async function getCFNClient(regionCode: string): Promise { + const originalClient = await globals.sdkClientBuilder.createAwsService(CloudFormation, {}, regionCode) + return new EnhancedCloudFormationClient(originalClient, regionCode) +} diff --git a/packages/core/src/awsexplorer/activation.ts b/packages/core/src/awsexplorer/activation.ts index f904658fcaa..ec4c23ccd79 100644 --- a/packages/core/src/awsexplorer/activation.ts +++ b/packages/core/src/awsexplorer/activation.ts @@ -36,6 +36,7 @@ import { TreeNode } from '../shared/treeview/resourceTreeDataProvider' import { getSourceNode } from '../shared/utilities/treeNodeUtils' import { openAwsCFNConsoleCommand, openAwsConsoleCommand } from '../shared/awsConsole' import { StackNameNode } from '../awsService/appBuilder/explorer/nodes/deployedStack' +import { LambdaFunctionNodeDecorationProvider } from '../lambda/explorer/lambdaFunctionNodeDecorationProvider' /** * Activates the AWS Explorer UI and related functionality. @@ -65,7 +66,10 @@ export async function activate(args: { telemetry.aws_expandExplorerNode.emit({ serviceType: element.element.serviceId, result: 'Succeeded' }) } }) - globals.context.subscriptions.push(view) + globals.context.subscriptions.push( + view, + vscode.window.registerFileDecorationProvider(LambdaFunctionNodeDecorationProvider.getInstance()) + ) await registerAwsExplorerCommands(args.context, awsExplorer, args.toolkitOutputChannel) diff --git a/packages/core/src/commands.ts b/packages/core/src/commands.ts index db038a72bbd..f69a8dd173c 100644 --- a/packages/core/src/commands.ts +++ b/packages/core/src/commands.ts @@ -46,7 +46,7 @@ import { Commands, VsCodeCommandArg, placeholder, vscodeComponent } from './shar import { isValidResponse } from './shared/wizards/wizard' import { CancellationError } from './shared/utilities/timeoutUtils' import { ToolkitError } from './shared/errors' -import { setContext } from './shared/vscode/setContext' +import { getContext, setContext } from './shared/vscode/setContext' function switchConnections(auth: Auth | TreeNode | unknown) { if (!(auth instanceof Auth)) { @@ -103,7 +103,7 @@ export function registerCommands(context: vscode.ExtensionContext) { const manageConnections = Commands.register( { id: 'aws.toolkit.auth.manageConnections', compositeKey: { 1: 'source' } }, - async (_: VsCodeCommandArg, source: AuthSource, serviceToShow?: ServiceItemId) => { + async (_: VsCodeCommandArg, source: AuthSource, serviceToShow?: ServiceItemId, blocking?: boolean) => { if (_ !== placeholder) { source = AuthSources.vscodeComponent } @@ -124,7 +124,23 @@ export function registerCommands(context: vscode.ExtensionContext) { CommonAuthWebview.authSource = source await vscode.commands.executeCommand('aws.explorer.setLoginService', serviceToShow) await setContext('aws.explorer.showAuthView', true) + + // While the auth view is open, we want to be blocking (if the command has been specified to be blocking) + const authWindowPromise = new Promise((resolve) => { + if (!blocking) { + resolve() + } + + const check = globals.clock.setInterval(() => { + if (getContext('aws.explorer.showAuthView') === false) { + clearInterval(check) + resolve() + } + }, 500) + }) + await vscode.commands.executeCommand('aws.toolkit.AmazonCommonAuth.focus') + await authWindowPromise } ) diff --git a/packages/core/src/lambda/activation.ts b/packages/core/src/lambda/activation.ts index d799173697e..1bb91a737b3 100644 --- a/packages/core/src/lambda/activation.ts +++ b/packages/core/src/lambda/activation.ts @@ -3,18 +3,21 @@ * SPDX-License-Identifier: Apache-2.0 */ +import * as path from 'path' import * as vscode from 'vscode' +import * as nls from 'vscode-nls' + import { Lambda } from 'aws-sdk' import { deleteLambda } from './commands/deleteLambda' import { uploadLambdaCommand } from './commands/uploadLambda' import { LambdaFunctionNode } from './explorer/lambdaFunctionNode' -import { downloadLambdaCommand } from './commands/downloadLambda' +import { downloadLambdaCommand, openLambdaFile } from './commands/downloadLambda' import { tryRemoveFolder } from '../shared/filesystemUtilities' import { ExtContext } from '../shared/extensions' import { invokeRemoteLambda } from './vue/remoteInvoke/invokeLambda' import { registerSamDebugInvokeVueCommand, registerSamInvokeVueCommand } from './vue/configEditor/samInvokeBackend' import { Commands } from '../shared/vscode/commands2' -import { DefaultLambdaClient } from '../shared/clients/lambdaClient' +import { DefaultLambdaClient, getFunctionWithCredentials } from '../shared/clients/lambdaClient' import { copyLambdaUrl } from './commands/copyLambdaUrl' import { ResourceNode } from '../awsService/appBuilder/explorer/nodes/resourceNode' import { isTreeNode, TreeNode } from '../shared/treeview/resourceTreeDataProvider' @@ -24,11 +27,50 @@ import { liveTailRegistry, liveTailCodeLensProvider } from '../awsService/cloudW import { getFunctionLogGroupName } from '../awsService/cloudWatchLogs/activation' import { ToolkitError, isError } from '../shared/errors' import { LogStreamFilterResponse } from '../awsService/cloudWatchLogs/wizard/liveTailLogStreamSubmenu' +import { tempDirPath } from '../shared/filesystemUtilities' +import fs from '../shared/fs/fs' +import { deployFromTemp, editLambda, getReadme, openLambdaFolderForEdit } from './commands/editLambda' +import { getTempLocation } from './utils' +import { registerLambdaUriHandler } from './uriHandlers' + +const localize = nls.loadMessageBundle() /** * Activates Lambda components. */ export async function activate(context: ExtContext): Promise { + try { + if (vscode.workspace.workspaceFolders) { + for (const workspaceFolder of vscode.workspace.workspaceFolders) { + // Making the comparison case insensitive because Windows can have `C\` or `c\` + const workspacePath = workspaceFolder.uri.fsPath.toLowerCase() + const tempPath = path.join(tempDirPath, 'lambda').toLowerCase() + if (workspacePath.startsWith(tempPath)) { + const name = path.basename(workspaceFolder.uri.fsPath) + const region = path.basename(path.dirname(workspaceFolder.uri.fsPath)) + const getFunctionOutput = await getFunctionWithCredentials(region, name) + const configuration = getFunctionOutput.Configuration + await editLambda( + { + name, + region, + // Configuration as any due to the difference in types between sdkV2 and sdkV3 + configuration: configuration as any, + }, + true + ) + + const readmeUri = vscode.Uri.file(await getReadme()) + await vscode.commands.executeCommand('markdown.showPreview', readmeUri, vscode.ViewColumn.Two) + } + } + } + } catch (e) { + void vscode.window.showWarningMessage( + localize('AWS.lambda.open.failure', `Unable to edit Lambda Function locally: ${e}`) + ) + } + context.extensionContext.subscriptions.push( Commands.register('aws.deleteLambda', async (node: LambdaFunctionNode | TreeNode) => { const sourceNode = getSourceNode(node) @@ -47,6 +89,7 @@ export async function activate(context: ExtContext): Promise { source: source, }) }), + // Capture debug finished events, and delete the temporary directory if it exists vscode.debug.onDidTerminateDebugSession(async (session) => { if ( @@ -56,10 +99,12 @@ export async function activate(context: ExtContext): Promise { await tryRemoveFolder(session.configuration.baseBuildDir) } }), + Commands.register('aws.downloadLambda', async (node: LambdaFunctionNode | TreeNode) => { const sourceNode = getSourceNode(node) await downloadLambdaCommand(sourceNode) }), + Commands.register({ id: 'aws.uploadLambda', autoconnect: true }, async (arg?: unknown) => { if (arg instanceof LambdaFunctionNode) { await uploadLambdaCommand({ @@ -73,6 +118,26 @@ export async function activate(context: ExtContext): Promise { await uploadLambdaCommand() } }), + + Commands.register({ id: 'aws.quickDeployLambda' }, async (node: LambdaFunctionNode) => { + const functionName = node.configuration.FunctionName! + const region = node.regionCode + const lambda = { name: functionName, region, configuration: node.configuration } + const tempLocation = getTempLocation(functionName, region) + + if (await fs.existsDir(tempLocation)) { + await deployFromTemp(lambda, vscode.Uri.file(tempLocation)) + } + }), + + Commands.register('aws.openLambdaFile', async (path: string) => { + await openLambdaFile(path) + }), + + Commands.register('aws.lambda.openWorkspace', async (node: LambdaFunctionNode) => { + await openLambdaFolderForEdit(node.functionName, node.regionCode) + }), + Commands.register('aws.copyLambdaUrl', async (node: LambdaFunctionNode | TreeNode) => { const sourceNode = getSourceNode(node) await copyLambdaUrl(sourceNode, new DefaultLambdaClient(sourceNode.regionCode)) @@ -116,6 +181,8 @@ export async function activate(context: ExtContext): Promise { throw err } } - }) + }), + + registerLambdaUriHandler() ) } diff --git a/packages/core/src/lambda/commands/downloadLambda.ts b/packages/core/src/lambda/commands/downloadLambda.ts index e2e1dc2be91..80932a34a76 100644 --- a/packages/core/src/lambda/commands/downloadLambda.ts +++ b/packages/core/src/lambda/commands/downloadLambda.ts @@ -11,7 +11,7 @@ import { LambdaFunctionNode } from '../explorer/lambdaFunctionNode' import { showConfirmationMessage } from '../../shared/utilities/messages' import { LaunchConfiguration, getReferencedHandlerPaths } from '../../shared/debug/launchConfiguration' -import { makeTemporaryToolkitFolder, fileExists, tryRemoveFolder } from '../../shared/filesystemUtilities' +import { makeTemporaryToolkitFolder, tryRemoveFolder } from '../../shared/filesystemUtilities' import * as localizedText from '../../shared/localizedText' import { getLogger } from '../../shared/logger/logger' import { HttpResourceFetcher } from '../../shared/resourcefetcher/node/httpResourceFetcher' @@ -26,6 +26,7 @@ import { DefaultLambdaClient } from '../../shared/clients/lambdaClient' import { telemetry } from '../../shared/telemetry/telemetry' import { Result, Runtime } from '../../shared/telemetry/telemetry' import { fs } from '../../shared/fs/fs' +import { LambdaFunction } from './uploadLambda' export async function downloadLambdaCommand(functionNode: LambdaFunctionNode) { const result = await runDownloadLambda(functionNode) @@ -75,6 +76,22 @@ async function runDownloadLambda(functionNode: LambdaFunctionNode): Promise { return await vscode.window.withProgress( { location: vscode.ProgressLocation.Notification, @@ -82,7 +99,7 @@ async function runDownloadLambda(functionNode: LambdaFunctionNode): Promise { - return selectedUri === val.uri - }).length === 0 - ) { - await addFolderToWorkspace({ uri: selectedUri! }, true) + if (workspaceFolders) { + if ( + workspaceFolders.filter((val) => { + return selectedUri === val.uri + }).length === 0 + ) { + await addFolderToWorkspace({ uri: selectedUri! }, true) + } + const workspaceFolder = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(downloadLocation))! + + await addLaunchConfigEntry(lambdaLocation, lambda, workspaceFolder) } - const workspaceFolder = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(downloadLocation))! - - await addLaunchConfigEntry(lambdaLocation, functionNode, workspaceFolder) - return 'Succeeded' } catch (e) { // failed to open handler file or add launch config. @@ -137,17 +155,17 @@ async function downloadAndUnzipLambda( message?: string | undefined increment?: number | undefined }>, - functionNode: LambdaFunctionNode, + lambda: LambdaFunction, extractLocation: string, - lambda = new DefaultLambdaClient(functionNode.regionCode) + lambdaClient = new DefaultLambdaClient(lambda.region) ): Promise { - const functionArn = functionNode.configuration.FunctionArn! + const functionArn = lambda.configuration!.FunctionArn! let tempDir: string | undefined try { tempDir = await makeTemporaryToolkitFolder() const downloadLocation = path.join(tempDir, 'function.zip') - const response = await lambda.getFunction(functionArn) + const response = await lambdaClient.getFunction(functionArn) const codeLocation = response.Code!.Location! // arbitrary increments since there's no "busy" state for progress bars @@ -177,7 +195,7 @@ async function downloadAndUnzipLambda( } export async function openLambdaFile(lambdaLocation: string): Promise { - if (!(await fileExists(lambdaLocation))) { + if (!(await fs.exists(lambdaLocation))) { const warning = localize( 'AWS.lambda.download.fileNotFound', 'Handler file {0} not found in downloaded function.', @@ -193,16 +211,16 @@ export async function openLambdaFile(lambdaLocation: string): Promise { async function addLaunchConfigEntry( lambdaLocation: string, - functionNode: LambdaFunctionNode, + lambda: LambdaFunction, workspaceFolder: vscode.WorkspaceFolder ): Promise { - const handler = functionNode.configuration.Handler! + const handler = lambda.configuration!.Handler! const samDebugConfig = createCodeAwsSamDebugConfig( workspaceFolder, handler, - computeLambdaRoot(lambdaLocation, functionNode), - functionNode.configuration.Runtime! + computeLambdaRoot(lambdaLocation, lambda), + lambda.configuration!.Runtime! ) const launchConfig = new LaunchConfiguration(vscode.Uri.file(lambdaLocation)) @@ -218,8 +236,8 @@ async function addLaunchConfigEntry( * @param lambdaLocation Lambda handler file location * @param functionNode Function node */ -function computeLambdaRoot(lambdaLocation: string, functionNode: LambdaFunctionNode): string { - const lambdaDetails = getLambdaDetails(functionNode.configuration) +function computeLambdaRoot(lambdaLocation: string, lambda: LambdaFunction): string { + const lambdaDetails = getLambdaDetails(lambda.configuration!) const normalizedLocation = pathutils.normalize(lambdaLocation) const lambdaIndex = normalizedLocation.indexOf(`/${lambdaDetails.fileName}`) diff --git a/packages/core/src/lambda/commands/editLambda.ts b/packages/core/src/lambda/commands/editLambda.ts new file mode 100644 index 00000000000..05476a8c765 --- /dev/null +++ b/packages/core/src/lambda/commands/editLambda.ts @@ -0,0 +1,244 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import * as nls from 'vscode-nls' +import { LambdaFunctionNode } from '../explorer/lambdaFunctionNode' +import { downloadLambdaInLocation, openLambdaFile } from './downloadLambda' +import { LambdaFunction, runUploadDirectory } from './uploadLambda' +import { + compareCodeSha, + getFunctionInfo, + getLambdaDetails, + getTempLocation, + lambdaEdits, + lambdaTempPath, + setFunctionInfo, +} from '../utils' +import { showConfirmationMessage } from '../../shared/utilities/messages' +import fs from '../../shared/fs/fs' +import globals from '../../shared/extensionGlobals' +import { LambdaFunctionNodeDecorationProvider } from '../explorer/lambdaFunctionNodeDecorationProvider' +import path from 'path' +import { telemetry } from '../../shared/telemetry/telemetry' +import { ToolkitError } from '../../shared/errors' + +const localize = nls.loadMessageBundle() + +let lastPromptTime = Date.now() - 5000 + +export function watchForUpdates(lambda: LambdaFunction, projectUri: vscode.Uri): void { + const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(projectUri, '*')) + const startTime = globals.clock.Date.now() + + watcher.onDidChange(async (fileUri) => { + await promptForSync(lambda, projectUri, fileUri) + }) + + watcher.onDidCreate(async (fileUri) => { + // When the code is downloaded and the watcher is set, this will immediately trigger the onDidCreate + // To avoid this, we must check that the file was actually created AFTER the watcher was created + if ((await fs.stat(fileUri.fsPath)).ctime < startTime) { + return + } + await promptForSync(lambda, projectUri, fileUri) + }) + + watcher.onDidDelete(async (fileUri) => { + // We don't want to sync if the whole directory has been deleted + if (fileUri.fsPath !== projectUri.fsPath) { + await promptForSync(lambda, projectUri, fileUri) + } + }) +} + +// Creating this function for testing, can't mock the vscode.window in the tests +export async function promptDeploy() { + const confirmItem = localize('AWS.lambda.upload.sync', 'Deploy') + const cancelItem = localize('AWS.lambda.upload.noSync', 'No, thanks') + const response = await vscode.window.showInformationMessage( + localize('AWS.lambda.upload.confirmSync', 'Would you like to deploy these changes to the cloud?'), + confirmItem, + cancelItem + ) + return response === confirmItem +} + +export async function promptForSync(lambda: LambdaFunction, projectUri: vscode.Uri, fileUri: vscode.Uri) { + if (!(await fs.existsDir(projectUri.fsPath)) || globals.clock.Date.now() - lastPromptTime < 5000) { + return + } + + await setFunctionInfo(lambda, { + undeployed: true, + }) + + await LambdaFunctionNodeDecorationProvider.getInstance().addBadge( + fileUri, + vscode.Uri.from({ scheme: 'lambda', path: `${lambda.region}/${lambda.name}` }) + ) + + lastPromptTime = globals.clock.Date.now() + if (await promptDeploy()) { + await deployFromTemp(lambda, projectUri) + } +} + +async function confirmOutdatedChanges(prompt: string): Promise { + return await showConfirmationMessage({ + prompt, + confirm: localize('AWS.lambda.upload.overwrite', 'Overwrite'), + cancel: localize('AWS.lambda.upload.noOverwrite', 'Cancel'), + }) +} + +export async function deployFromTemp(lambda: LambdaFunction, projectUri: vscode.Uri) { + return telemetry.lambda_quickDeploy.run(async () => { + const prompt = localize( + 'AWS.lambda.upload.confirmOutdatedSync', + 'There are changes to your Function in the cloud after you created this local copy, overwrite anyway?' + ) + + const isShaDifferent = !(await compareCodeSha(lambda)) + const overwriteChanges = isShaDifferent ? await confirmOutdatedChanges(prompt) : true + + if (overwriteChanges) { + // Reset the lastPrompt time because we don't want to retrigger the watcher flow + lastPromptTime = globals.clock.Date.now() + await vscode.workspace.saveAll() + try { + await runUploadDirectory(lambda, 'zip', projectUri) + } catch { + throw new ToolkitError('Failed to deploy Lambda function', { code: 'deployFailure' }) + } + await setFunctionInfo(lambda, { + lastDeployed: globals.clock.Date.now(), + undeployed: false, + }) + await LambdaFunctionNodeDecorationProvider.getInstance().removeBadge( + projectUri, + vscode.Uri.from({ scheme: 'lambda', path: `${lambda.region}/${lambda.name}` }) + ) + if (isShaDifferent) { + telemetry.record({ action: 'overwriteChanges' }) + } + } else { + telemetry.record({ action: 'cancelOverwrite' }) + } + }) +} + +export async function editLambdaCommand(functionNode: LambdaFunctionNode) { + const region = functionNode.regionCode + const functionName = functionNode.configuration.FunctionName! + return await editLambda({ name: functionName, region, configuration: functionNode.configuration }) +} + +export async function editLambda(lambda: LambdaFunction, onActivation?: boolean) { + return await telemetry.lambda_quickEditFunction.run(async () => { + telemetry.record({ source: onActivation ? 'workspace' : 'explorer' }) + const { name, region, configuration } = lambda + const downloadLocation = getTempLocation(lambda.name, lambda.region) + const downloadLocationName = vscode.workspace.asRelativePath(downloadLocation, true) + + // We don't want to do anything if the folder already exists as a workspace folder, it means it's already being edited + if ( + vscode.workspace.workspaceFolders?.some((folder) => folder.uri.fsPath === downloadLocation && !onActivation) + ) { + return downloadLocation + } + + const prompt = localize( + 'AWS.lambda.download.confirmOutdatedSync', + 'There are changes to your Function in the cloud since you last edited locally, do you want to overwrite your local changes?' + ) + + // We want to overwrite changes in the following cases: + // 1. There is no code sha locally (getCodeShaLocal returns falsy) + // 2. There is a code sha locally, it does not match the one remotely, and the user confirms they want to overwrite it + const localExists = !!(await getFunctionInfo(lambda, 'sha')) + // This record tells us if they're attempting to edit a function they've edited before + telemetry.record({ action: localExists ? 'existingEdit' : 'newEdit' }) + + const overwriteChanges = + !localExists || (!(await compareCodeSha(lambda)) ? await confirmOutdatedChanges(prompt) : false) + + if (overwriteChanges) { + try { + // Clear directory contents instead of deleting to avoid Windows EBUSY errors + if (await fs.existsDir(downloadLocation)) { + const entries = await fs.readdir(downloadLocation) + await Promise.all( + entries.map((entry) => + fs.delete(path.join(downloadLocation, entry[0]), { recursive: true, force: true }) + ) + ) + } else { + await fs.mkdir(downloadLocation) + } + + await downloadLambdaInLocation(lambda, downloadLocationName, downloadLocation) + + // Watching for updates, then setting info, then removing the badges must be done in this order + // This is because the files creating can throw the watcher, which sometimes leads to changes being marked as undeployed + watchForUpdates(lambda, vscode.Uri.file(downloadLocation)) + await setFunctionInfo(lambda, { + lastDeployed: globals.clock.Date.now(), + undeployed: false, + sha: lambda.configuration!.CodeSha256, + }) + await LambdaFunctionNodeDecorationProvider.getInstance().removeBadge( + vscode.Uri.file(downloadLocation), + vscode.Uri.from({ scheme: 'lambda', path: `${lambda.region}/${lambda.name}` }) + ) + } catch { + throw new ToolkitError('Failed to download Lambda function', { code: 'failedDownload' }) + } + } else { + const lambdaLocation = path.join(downloadLocation, getLambdaDetails(lambda.configuration!).fileName) + await openLambdaFile(lambdaLocation) + watchForUpdates(lambda, vscode.Uri.file(downloadLocation)) + } + + const newEdit = { location: downloadLocationName, region, functionName: name, configuration } + lambdaEdits.push(newEdit) + + return downloadLocation + }) +} + +export async function openLambdaFolderForEdit(name: string, region: string) { + const downloadLocation = getTempLocation(name, region) + + if ( + vscode.workspace.workspaceFolders?.some((workspaceFolder) => + workspaceFolder.uri.fsPath.toLowerCase().startsWith(downloadLocation.toLowerCase()) + ) + ) { + // If the folder already exists in the workspace, show that folder + await vscode.commands.executeCommand('workbench.action.focusSideBar') + await vscode.commands.executeCommand('workbench.view.explorer') + } else { + await fs.mkdir(downloadLocation) + + await vscode.commands.executeCommand('vscode.openFolder', vscode.Uri.file(downloadLocation), { + newWindow: true, + noRecentEntry: true, + }) + } +} + +export async function getReadme(): Promise { + const readmeSource = 'resources/markdown/lambdaEdit.md' + const readmeDestination = path.join(lambdaTempPath, 'README.md') + const readmeContent = await fs.readFileText(globals.context.asAbsolutePath(readmeSource)) + await fs.writeFile(readmeDestination, readmeContent) + + // Put cloud deploy icon in the readme + const createStackIconSource = 'resources/icons/aws/lambda/create-stack-light.svg' + const createStackIconDestination = path.join(lambdaTempPath, 'create-stack.svg') + await fs.copy(globals.context.asAbsolutePath(createStackIconSource), createStackIconDestination) + + return readmeDestination +} diff --git a/packages/core/src/lambda/commands/uploadLambda.ts b/packages/core/src/lambda/commands/uploadLambda.ts index 692d07409a2..8105506ee45 100644 --- a/packages/core/src/lambda/commands/uploadLambda.ts +++ b/packages/core/src/lambda/commands/uploadLambda.ts @@ -19,13 +19,14 @@ import { SamCliBuildInvocation } from '../../shared/sam/cli/samCliBuild' import { getSamCliContext } from '../../shared/sam/cli/samCliContext' import { SamTemplateGenerator } from '../../shared/templates/sam/samTemplateGenerator' import { addCodiconToString } from '../../shared/utilities/textUtilities' -import { getLambdaDetails, listLambdaFunctions } from '../utils' +import { getLambdaEditFromNameRegion, getLambdaDetails, listLambdaFunctions } from '../utils' import { getIdeProperties } from '../../shared/extensionUtilities' import { createQuickPick, DataQuickPickItem } from '../../shared/ui/pickerPrompter' import { createCommonButtons } from '../../shared/ui/buttons' import { StepEstimator, Wizard, WIZARD_BACK } from '../../shared/wizards/wizard' import { createSingleFileDialog } from '../../shared/ui/common/openDialog' import { Prompter, PromptResult } from '../../shared/ui/prompter' +import { SkipPrompter } from '../../shared/ui/common/skipPrompter' import { ToolkitError } from '../../shared/errors' import { FunctionConfiguration } from 'aws-sdk/clients/lambda' import globals from '../../shared/extensionGlobals' @@ -103,6 +104,13 @@ export async function uploadLambdaCommand(lambdaArg?: LambdaFunction, path?: vsc } else if (response.uploadType === 'directory' && response.directoryBuildType) { result = (await runUploadDirectory(lambda, response.directoryBuildType, response.targetUri)) ?? result result = 'Succeeded' + } else if (response.uploadType === 'edit') { + const functionPath = getLambdaEditFromNameRegion(lambda.name, lambda.region)?.location + if (!functionPath) { + throw new ToolkitError('Function had a local copy before, but not anymore') + } else { + await runUploadDirectory(lambda, 'zip', vscode.Uri.file(functionPath)) + } } // TODO(sijaden): potentially allow the wizard to easily support tagged-union states } catch (err) { @@ -131,8 +139,8 @@ export async function uploadLambdaCommand(lambdaArg?: LambdaFunction, path?: vsc /** * Selects the type of file to upload (zip/dir) and proceeds with the rest of the workflow. */ -function createUploadTypePrompter() { - const items: DataQuickPickItem<'zip' | 'directory'>[] = [ +function createUploadTypePrompter(lambda?: LambdaFunction) { + const items: DataQuickPickItem<'edit' | 'zip' | 'directory'>[] = [ { label: addCodiconToString('file-zip', localize('AWS.generic.filetype.zipfile', 'ZIP Archive')), data: 'zip', @@ -143,6 +151,17 @@ function createUploadTypePrompter() { }, ] + if (lambda !== undefined) { + const { region, name: functionName } = lambda + const lambdaEdit = getLambdaEditFromNameRegion(functionName, region) + if (lambdaEdit) { + items.unshift({ + label: addCodiconToString('edit', localize('AWS.generic.filetype.edit', 'Local edit')), + data: 'edit', + }) + } + } + return createQuickPick(items, { title: localize('AWS.lambda.upload.title', 'Select Upload Type'), buttons: createCommonButtons(), @@ -196,7 +215,7 @@ function createConfirmDeploymentPrompter(lambda: LambdaFunction) { } export interface UploadLambdaWizardState { - readonly uploadType: 'zip' | 'directory' + readonly uploadType: 'edit' | 'zip' | 'directory' readonly targetUri: vscode.Uri readonly directoryBuildType: 'zip' | 'sam' readonly confirmedDeploy: boolean @@ -215,23 +234,23 @@ export class UploadLambdaWizard extends Wizard { this.form.targetUri.setDefault(this.invokePath) } } else { - this.form.uploadType.bindPrompter(() => createUploadTypePrompter()) - this.form.targetUri.bindPrompter(({ uploadType }) => { - if (uploadType === 'directory') { - return createSingleFileDialog({ - canSelectFolders: true, - canSelectFiles: false, - }) - } else { - return createSingleFileDialog({ - canSelectFolders: false, - canSelectFiles: true, - filters: { - 'ZIP archive': ['zip'], - }, - }) - } - }) + this.form.uploadType.bindPrompter(() => createUploadTypePrompter(this.lambda)) + this.form.targetUri.bindPrompter( + ({ uploadType }) => { + if (uploadType === 'directory') { + return createSingleFileDialog({ + canSelectFolders: false, + canSelectFiles: true, + filters: { + 'ZIP archive': ['zip'], + }, + }) + } else { + return new SkipPrompter() + } + }, + { showWhen: ({ uploadType }) => uploadType !== 'edit' } + ) } this.form.lambda.name.bindPrompter((state) => { @@ -258,7 +277,12 @@ export class UploadLambdaWizard extends Wizard { this.form.directoryBuildType.setDefault('zip') } - this.form.confirmedDeploy.bindPrompter((state) => createConfirmDeploymentPrompter(state.lambda!)) + this.form.confirmedDeploy.bindPrompter( + (state) => { + return createConfirmDeploymentPrompter(state.lambda!) + }, + { showWhen: ({ uploadType }) => uploadType !== 'edit' } + ) return this } @@ -277,7 +301,7 @@ export class UploadLambdaWizard extends Wizard { * @param type Whether to zip or sam build the directory * @param window Wrapper around vscode.window functionality for testing */ -async function runUploadDirectory(lambda: LambdaFunction, type: 'zip' | 'sam', parentDir: vscode.Uri) { +export async function runUploadDirectory(lambda: LambdaFunction, type: 'zip' | 'sam', parentDir: vscode.Uri) { if (type === 'sam' && lambda.configuration) { return await runUploadLambdaWithSamBuild({ ...lambda, configuration: lambda.configuration }, parentDir) } else { diff --git a/packages/core/src/lambda/explorer/cloudFormationNodes.ts b/packages/core/src/lambda/explorer/cloudFormationNodes.ts index 6fd8c4d0e96..e2d9e9aae27 100644 --- a/packages/core/src/lambda/explorer/cloudFormationNodes.ts +++ b/packages/core/src/lambda/explorer/cloudFormationNodes.ts @@ -153,8 +153,7 @@ function makeCloudFormationLambdaFunctionNode( regionCode: string, configuration: Lambda.FunctionConfiguration ): LambdaFunctionNode { - const node = new LambdaFunctionNode(parent, regionCode, configuration) - node.contextValue = contextValueCloudformationLambdaFunction + const node = new LambdaFunctionNode(parent, regionCode, configuration, contextValueCloudformationLambdaFunction) return node } diff --git a/packages/core/src/lambda/explorer/lambdaFunctionFileNode.ts b/packages/core/src/lambda/explorer/lambdaFunctionFileNode.ts new file mode 100644 index 00000000000..422de99b31f --- /dev/null +++ b/packages/core/src/lambda/explorer/lambdaFunctionFileNode.ts @@ -0,0 +1,38 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AWSResourceNode } from '../../shared/treeview/nodes/awsResourceNode' +import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase' +import { LambdaFunctionNode } from './lambdaFunctionNode' +import { getIcon } from '../../shared/icons' +import { isCloud9 } from '../../shared/extensionUtilities' +import { LambdaFunctionFolderNode } from './lambdaFunctionFolderNode' + +export class LambdaFunctionFileNode extends AWSTreeNodeBase implements AWSResourceNode { + public constructor( + public readonly parent: LambdaFunctionNode | LambdaFunctionFolderNode, + public readonly filename: string, + public readonly path: string + ) { + super(filename) + this.iconPath = getIcon('vscode-file') + this.contextValue = 'lambdaFunctionFileNode' + this.command = !isCloud9() + ? { + command: 'aws.openLambdaFile', + title: 'Open file', + arguments: [path], + } + : undefined + } + + public get arn(): string { + return '' + } + + public get name(): string { + return '' + } +} diff --git a/packages/core/src/lambda/explorer/lambdaFunctionFolderNode.ts b/packages/core/src/lambda/explorer/lambdaFunctionFolderNode.ts new file mode 100644 index 00000000000..7c67d482666 --- /dev/null +++ b/packages/core/src/lambda/explorer/lambdaFunctionFolderNode.ts @@ -0,0 +1,60 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { AWSResourceNode } from '../../shared/treeview/nodes/awsResourceNode' +import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase' +import { LambdaFunctionNode } from './lambdaFunctionNode' +import { fs } from '../../shared/fs/fs' +import { getIcon } from '../../shared/icons' +import { makeChildrenNodes } from '../../shared/treeview/utils' +import { PlaceholderNode } from '../../shared/treeview/nodes/placeholderNode' +import { localize } from 'vscode-nls' +import path from 'path' +import { LambdaFunctionFileNode } from './lambdaFunctionFileNode' + +export class LambdaFunctionFolderNode extends AWSTreeNodeBase implements AWSResourceNode { + public constructor( + public readonly parent: LambdaFunctionNode | LambdaFunctionFolderNode, + public readonly filename: string, + public readonly path: string + ) { + super(filename, vscode.TreeItemCollapsibleState.Collapsed) + this.iconPath = getIcon('vscode-folder') + this.contextValue = 'lambdaFunctionFolderNode' + } + + public get arn(): string { + return '' + } + + public get name(): string { + return '' + } + + public override async getChildren(): Promise { + return await makeChildrenNodes({ + getChildNodes: async () => this.loadFunctionFiles(), + getNoChildrenPlaceholderNode: async () => + new PlaceholderNode(this, localize('AWS.explorerNode.s3.noObjects', '[No Objects found]')), + }) + } + + public async loadFunctionFiles(): Promise { + const nodes: AWSTreeNodeBase[] = [] + const files = await fs.readdir(this.path) + for (const file of files) { + const [fileName, type] = file + const filePath = path.join(this.path, fileName) + if (type === vscode.FileType.Directory) { + nodes.push(new LambdaFunctionFolderNode(this, fileName, filePath)) + } else { + nodes.push(new LambdaFunctionFileNode(this, fileName, filePath)) + } + } + + return nodes + } +} diff --git a/packages/core/src/lambda/explorer/lambdaFunctionNode.ts b/packages/core/src/lambda/explorer/lambdaFunctionNode.ts index 02fa8439d5c..2093a9585d4 100644 --- a/packages/core/src/lambda/explorer/lambdaFunctionNode.ts +++ b/packages/core/src/lambda/explorer/lambdaFunctionNode.ts @@ -5,26 +5,49 @@ import { Lambda } from 'aws-sdk' import * as os from 'os' +import * as vscode from 'vscode' import { getIcon } from '../../shared/icons' import { AWSResourceNode } from '../../shared/treeview/nodes/awsResourceNode' import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase' +import { editLambdaCommand } from '../commands/editLambda' +import { fs } from '../../shared/fs/fs' +import { makeChildrenNodes } from '../../shared/treeview/utils' +import { PlaceholderNode } from '../../shared/treeview/nodes/placeholderNode' +import path from 'path' +import { localize } from 'vscode-nls' +import { LambdaFunctionFolderNode } from './lambdaFunctionFolderNode' +import { LambdaFunctionFileNode } from './lambdaFunctionFileNode' + +export const contextValueLambdaFunction = 'awsRegionFunctionNode' +export const contextValueLambdaFunctionImportable = 'awsRegionFunctionNodeDownloadable' export class LambdaFunctionNode extends AWSTreeNodeBase implements AWSResourceNode { public constructor( public readonly parent: AWSTreeNodeBase, public override readonly regionCode: string, - public configuration: Lambda.FunctionConfiguration + public configuration: Lambda.FunctionConfiguration, + public override readonly contextValue?: string ) { - super('') + super( + `${configuration.FunctionArn}`, + contextValue === contextValueLambdaFunctionImportable + ? vscode.TreeItemCollapsibleState.Collapsed + : vscode.TreeItemCollapsibleState.None + ) this.update(configuration) + this.resourceUri = vscode.Uri.from({ scheme: 'lambda', path: `${regionCode}/${configuration.FunctionName}` }) this.iconPath = getIcon('aws-lambda-function') + this.contextValue = contextValue } public update(configuration: Lambda.FunctionConfiguration): void { this.configuration = configuration this.label = this.configuration.FunctionName || '' this.tooltip = `${this.configuration.FunctionName}${os.EOL}${this.configuration.FunctionArn}` + if (this.contextValue === contextValueLambdaFunction) { + this.tooltip += `${os.EOL} This function is not downloadable` + } } public get functionName(): string { @@ -46,4 +69,35 @@ export class LambdaFunctionNode extends AWSTreeNodeBase implements AWSResourceNo return this.configuration.FunctionName } + + public override async getChildren(): Promise { + if (!(this.contextValue === contextValueLambdaFunctionImportable)) { + return [] + } + + return await makeChildrenNodes({ + getChildNodes: async () => { + const path = await editLambdaCommand(this) + return path ? this.loadFunctionFiles(path) : [] + }, + getNoChildrenPlaceholderNode: async () => + new PlaceholderNode(this, localize('AWS.explorerNode.lambda.noFiles', '[No files found]')), + }) + } + + public async loadFunctionFiles(tmpPath: string): Promise { + const nodes: AWSTreeNodeBase[] = [] + const files = await fs.readdir(tmpPath) + for (const file of files) { + const [fileName, type] = file + const filePath = path.join(tmpPath, fileName) + if (type === vscode.FileType.Directory) { + nodes.push(new LambdaFunctionFolderNode(this, fileName, filePath)) + } else { + nodes.push(new LambdaFunctionFileNode(this, fileName, filePath)) + } + } + + return nodes + } } diff --git a/packages/core/src/lambda/explorer/lambdaFunctionNodeDecorationProvider.ts b/packages/core/src/lambda/explorer/lambdaFunctionNodeDecorationProvider.ts new file mode 100644 index 00000000000..b31de940991 --- /dev/null +++ b/packages/core/src/lambda/explorer/lambdaFunctionNodeDecorationProvider.ts @@ -0,0 +1,107 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as vscode from 'vscode' +import { fs } from '../../shared/fs/fs' +import path from 'path' +import { getFunctionInfo } from '../utils' +import { LambdaFunction } from '../commands/uploadLambda' + +export class LambdaFunctionNodeDecorationProvider implements vscode.FileDecorationProvider { + // Make it a singleton so that it's easier to access + private static instance: LambdaFunctionNodeDecorationProvider + private readonly _onDidChangeFileDecorations = new vscode.EventEmitter() + readonly onDidChangeFileDecorations = this._onDidChangeFileDecorations.event + + private constructor() {} + + public static getInstance(): LambdaFunctionNodeDecorationProvider { + if (!LambdaFunctionNodeDecorationProvider.instance) { + LambdaFunctionNodeDecorationProvider.instance = new LambdaFunctionNodeDecorationProvider() + } + return LambdaFunctionNodeDecorationProvider.instance + } + + async provideFileDecoration(uri: vscode.Uri): Promise { + const badge = { + badge: 'M', + color: new vscode.ThemeColor('gitDecoration.modifiedResourceForeground'), + tooltip: 'This function has undeployed changes', + propagate: true, + } + + if (uri.scheme === 'lambda') { + const [region, name] = uri.path.split('/') + const lambda: LambdaFunction = { region, name } + if (await getFunctionInfo(lambda, 'undeployed')) { + badge.propagate = false + return badge + } + } else { + try { + const lambda = this.getLambdaFromPath(uri) + if (lambda && (await this.isFileModifiedAfterDeployment(uri.fsPath, lambda))) { + return badge + } + } catch { + return undefined + } + } + } + + public async addBadge(fileUri: vscode.Uri, functionUri: vscode.Uri) { + this._onDidChangeFileDecorations.fire(vscode.Uri.file(fileUri.fsPath)) + this._onDidChangeFileDecorations.fire(functionUri) + } + + public async removeBadge(fileUri: vscode.Uri, functionUri: vscode.Uri) { + // We need to propagate the badge removal down to all files in the dir + for (const path of await this.getFilePaths(fileUri.fsPath)) { + const subUri = vscode.Uri.file(path) + this._onDidChangeFileDecorations.fire(subUri) + } + this._onDidChangeFileDecorations.fire(functionUri) + } + + private async getFilePaths(basePath: string) { + const files = await fs.readdir(basePath) + const subFiles: string[] = [basePath] + for (const file of files) { + const [fileName, type] = file + const filePath = path.join(basePath, fileName) + if (type === vscode.FileType.Directory) { + subFiles.push(...(await this.getFilePaths(filePath))) + } else { + subFiles.push(filePath) + } + } + + return subFiles + } + + private getLambdaFromPath(uri: vscode.Uri): LambdaFunction { + const pathParts = uri.fsPath.split(path.sep) + const lambdaIndex = pathParts.indexOf('lambda') + if (lambdaIndex === -1 || lambdaIndex + 2 >= pathParts.length) { + throw new Error('Invalid path') + } + const region = pathParts[lambdaIndex + 1] + const name = pathParts[lambdaIndex + 2] + return { region, name } + } + + private async isFileModifiedAfterDeployment(filePath: string, lambda: LambdaFunction): Promise { + try { + const { lastDeployed, undeployed } = await getFunctionInfo(lambda) + if (!lastDeployed || !undeployed) { + return false + } + + const fileStat = await fs.stat(filePath) + return fileStat.mtime > lastDeployed + } catch { + return false + } + } +} diff --git a/packages/core/src/lambda/explorer/lambdaNodes.ts b/packages/core/src/lambda/explorer/lambdaNodes.ts index 9adff42f04d..077572feda7 100644 --- a/packages/core/src/lambda/explorer/lambdaNodes.ts +++ b/packages/core/src/lambda/explorer/lambdaNodes.ts @@ -15,12 +15,13 @@ import { PlaceholderNode } from '../../shared/treeview/nodes/placeholderNode' import { makeChildrenNodes } from '../../shared/treeview/utils' import { toArrayAsync, toMap, updateInPlace } from '../../shared/utilities/collectionUtils' import { listLambdaFunctions } from '../utils' -import { LambdaFunctionNode } from './lambdaFunctionNode' +import { + contextValueLambdaFunction, + contextValueLambdaFunctionImportable, + LambdaFunctionNode, +} from './lambdaFunctionNode' import { samLambdaImportableRuntimes } from '../models/samLambdaRuntime' -export const contextValueLambdaFunction = 'awsRegionFunctionNode' -export const contextValueLambdaFunctionImportable = 'awsRegionFunctionNodeDownloadable' - /** * An AWS Explorer node representing the Lambda Service. * Contains Lambda Functions for a specific region as child nodes. @@ -70,10 +71,10 @@ function makeLambdaFunctionNode( regionCode: string, configuration: Lambda.FunctionConfiguration ): LambdaFunctionNode { - const node = new LambdaFunctionNode(parent, regionCode, configuration) - node.contextValue = samLambdaImportableRuntimes.contains(node.configuration.Runtime ?? '') + const contextValue = samLambdaImportableRuntimes.contains(configuration.Runtime ?? '') ? contextValueLambdaFunctionImportable : contextValueLambdaFunction + const node = new LambdaFunctionNode(parent, regionCode, configuration, contextValue) return node } diff --git a/packages/core/src/lambda/models/samLambdaRuntime.ts b/packages/core/src/lambda/models/samLambdaRuntime.ts index 58311474d41..d6d8683e28b 100644 --- a/packages/core/src/lambda/models/samLambdaRuntime.ts +++ b/packages/core/src/lambda/models/samLambdaRuntime.ts @@ -68,7 +68,7 @@ export const javaRuntimes: ImmutableSet = ImmutableSet([ 'java21', ]) export const dotNetRuntimes: ImmutableSet = ImmutableSet(['dotnet6', 'dotnet8']) -export const rubyRuntimes: ImmutableSet = ImmutableSet(['ruby3.2', 'ruby3.3']) +export const rubyRuntimes: ImmutableSet = ImmutableSet(['ruby3.2', 'ruby3.3', 'ruby3.4']) /** * Deprecated runtimes can be found at https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html @@ -125,7 +125,11 @@ export const samArmLambdaRuntimes: ImmutableSet = ImmutableSet const cloud9SupportedRuntimes: ImmutableSet = ImmutableSet.union([nodeJsRuntimes, pythonRuntimes]) // only interpreted languages are importable as compiled languages won't provide a useful artifact for editing. -export const samLambdaImportableRuntimes: ImmutableSet = ImmutableSet.union([nodeJsRuntimes, pythonRuntimes]) +export const samLambdaImportableRuntimes: ImmutableSet = ImmutableSet.union([ + nodeJsRuntimes, + pythonRuntimes, + rubyRuntimes, +]) export function samLambdaCreatableRuntimes(cloud9: boolean = isCloud9()): ImmutableSet { return cloud9 ? cloud9SupportedRuntimes : samZipLambdaRuntimes diff --git a/packages/core/src/lambda/uriHandlers.ts b/packages/core/src/lambda/uriHandlers.ts new file mode 100644 index 00000000000..b5d6b4d6661 --- /dev/null +++ b/packages/core/src/lambda/uriHandlers.ts @@ -0,0 +1,62 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as nls from 'vscode-nls' + +import { SearchParams } from '../shared/vscode/uriHandler' +import { openLambdaFolderForEdit } from './commands/editLambda' +import { showConfirmationMessage } from '../shared/utilities/messages' +import globals from '../shared/extensionGlobals' +import { getFunctionWithCredentials } from '../shared/clients/lambdaClient' +import { telemetry } from '../shared/telemetry/telemetry' +import { ToolkitError } from '../shared/errors' + +const localize = nls.loadMessageBundle() + +export function registerLambdaUriHandler() { + async function openFunctionHandler(params: ReturnType) { + await telemetry.lambda_uriHandler.run(async () => { + try { + // We just want to be able to get the function - if it fails we abort and throw the error + await getFunctionWithCredentials(params.region, params.functionName) + + if (params.isCfn === 'true') { + const response = await showConfirmationMessage({ + prompt: localize( + 'AWS.lambda.open.confirmInStack', + 'The function you are attempting to open is in a CloudFormation stack. Editing the function code could lead to stack drift.' + ), + confirm: localize('AWS.lambda.open.confirmStack', 'Confirm'), + cancel: localize('AWS.lambda.open.cancelStack', 'Cancel'), + }) + if (!response) { + return + } + } + await openLambdaFolderForEdit(params.functionName, params.region) + } catch (e) { + throw new ToolkitError(`Unable to get function ${params.functionName} in region ${params.region}: ${e}`) + } + }) + } + + return vscode.Disposable.from( + globals.uriHandler.onPath('/lambda/load-function', openFunctionHandler, parseOpenParams) + ) +} + +// Sample url: +// vscode://AmazonWebServices.aws-toolkit-vscode/lambda/load-function?functionName=fnf-func-1®ion=us-east-1&isCfn=true +export function parseOpenParams(query: SearchParams) { + return { + functionName: query.getOrThrow( + 'functionName', + localize('AWS.lambda.open.missingName', 'A function name must be provided') + ), + region: query.getOrThrow('region', localize('AWS.lambda.open.missingRegion', 'A region must be provided')), + isCfn: query.get('isCfn'), + } +} diff --git a/packages/core/src/lambda/utils.ts b/packages/core/src/lambda/utils.ts index 79054e02cac..4bb9063fedd 100644 --- a/packages/core/src/lambda/utils.ts +++ b/packages/core/src/lambda/utils.ts @@ -6,17 +6,21 @@ import * as nls from 'vscode-nls' const localize = nls.loadMessageBundle() +import path from 'path' import xml2js = require('xml2js') import { Lambda } from 'aws-sdk' import * as vscode from 'vscode' import { CloudFormationClient, StackSummary } from '../shared/clients/cloudFormation' -import { LambdaClient } from '../shared/clients/lambdaClient' +import { DefaultLambdaClient, LambdaClient } from '../shared/clients/lambdaClient' import { getFamily, getNodeMajorVersion, RuntimeFamily } from './models/samLambdaRuntime' import { getLogger } from '../shared/logger/logger' import { HttpResourceFetcher } from '../shared/resourcefetcher/httpResourceFetcher' import { FileResourceFetcher } from '../shared/resourcefetcher/fileResourceFetcher' import { sampleRequestManifestPath } from './constants' import globals from '../shared/extensionGlobals' +import { tempDirPath } from '../shared/filesystemUtilities' +import { LambdaFunction } from './commands/uploadLambda' +import { fs } from '../shared/fs/fs' export async function* listCloudFormationStacks(client: CloudFormationClient): AsyncIterableIterator { // TODO: this 'loading' message needs to go under each regional entry @@ -46,6 +50,23 @@ export async function* listLambdaFunctions(client: LambdaClient): AsyncIterableI } } +export async function* listLayerVersions( + client: LambdaClient, + name: string +): AsyncIterableIterator { + const status = vscode.window.setStatusBarMessage( + localize('AWS.message.statusBar.loading.lambda', 'Loading Lambda Layer Versions...') + ) + + try { + yield* client.listLayerVersions(name) + } finally { + if (status) { + status.dispose() + } + } +} + /** * Returns filename and function name corresponding to a Lambda.FunctionConfiguration * Only works for supported languages (Python/JS) @@ -70,6 +91,9 @@ export function getLambdaDetails(configuration: Lambda.FunctionConfiguration): { } break } + case RuntimeFamily.Ruby: + runtimeExtension = 'rb' + break default: throw new Error(`Toolkit does not currently support imports for runtime: ${configuration.Runtime}`) } @@ -124,3 +148,77 @@ async function getSampleRequestManifest(): Promise { } return httpResp.text() } + +function getInfoLocation(lambda: LambdaFunction): string { + return path.join(getTempRegionLocation(lambda.region), `.${lambda.name}`) +} + +export async function getCodeShaLive(lambda: LambdaFunction): Promise { + const lambdaClient = new DefaultLambdaClient(lambda.region) + const func = await lambdaClient.getFunction(lambda.name) + return func.Configuration?.CodeSha256 +} + +export async function compareCodeSha(lambda: LambdaFunction): Promise { + const local = await getFunctionInfo(lambda, 'sha') + const remote = await getCodeShaLive(lambda) + getLogger().info(`local: ${local}, remote: ${remote}`) + return local === remote +} + +export async function getFunctionInfo(lambda: LambdaFunction, field?: 'lastDeployed' | 'undeployed' | 'sha') { + try { + const data = JSON.parse(await fs.readFileText(getInfoLocation(lambda))) + getLogger().debug('Data returned from getFunctionInfo for %s: %O', lambda.name, data) + return field ? data[field] : data + } catch { + return field ? undefined : {} + } +} + +export async function setFunctionInfo( + lambda: LambdaFunction, + info: { lastDeployed?: number; undeployed?: boolean; sha?: string } +) { + try { + const existing = await getFunctionInfo(lambda) + const updated = { + lastDeployed: info.lastDeployed ?? existing.lastDeployed, + undeployed: info.undeployed ?? true, + sha: info.sha ?? (await getCodeShaLive(lambda)), + } + await fs.writeFile(getInfoLocation(lambda), JSON.stringify(updated)) + } catch (err) { + getLogger().warn(`codesha: unable to save information at key "${lambda.name}: %s"`, err) + } +} + +export const lambdaTempPath = path.join(tempDirPath, 'lambda') + +export function getTempRegionLocation(region: string) { + return path.join(lambdaTempPath, region) +} + +export function getTempLocation(functionName: string, region: string) { + return path.join(getTempRegionLocation(region), functionName) +} + +type LambdaEdit = { + location: string + functionName: string + region: string + configuration?: Lambda.FunctionConfiguration +} + +// Array to keep the list of functions that are being edited. +export const lambdaEdits: LambdaEdit[] = [] + +// Given a particular function and region, it returns the full LambdaEdit object +export function getLambdaEditFromNameRegion(name: string, functionRegion: string) { + return lambdaEdits.find(({ functionName, region }) => functionName === name && region === functionRegion) +} + +// Given a particular localPath, it returns the full LambdaEdit object +export function getLambdaEditFromLocation(functionLocation: string) { + return lambdaEdits.find(({ location }) => location === functionLocation) +} diff --git a/packages/core/src/login/webview/vue/login.vue b/packages/core/src/login/webview/vue/login.vue index 312aa18029b..7973844f4d4 100644 --- a/packages/core/src/login/webview/vue/login.vue +++ b/packages/core/src/login/webview/vue/login.vue @@ -239,6 +239,11 @@
IAM Credentials:
Credentials will be added to the appropriate ~/.aws/ files
+
Learn More
Profile Name
The identifier for these credentials
export class DefaultLambdaClient { @@ -80,6 +85,39 @@ export class DefaultLambdaClient { } } + public async getLayerVersion(name: string, version: number): Promise { + getLogger().debug(`getLayerVersion called for LayerName: ${name}, VersionNumber ${version}`) + const client = await this.createSdkClient() + + try { + const response = await client.getLayerVersion({ LayerName: name, VersionNumber: version }).promise() + // prune `Code` from logs so we don't reveal a signed link to customer resources. + getLogger().debug('getLayerVersion returned response (code section pruned): %O', { + ...response, + Code: 'Pruned', + }) + return response + } catch (e) { + getLogger().error('Failed to get function: %s', e) + throw e + } + } + + public async *listLayerVersions(name: string): AsyncIterableIterator { + const client = await this.createSdkClient() + + const request: Lambda.ListLayerVersionsRequest = { LayerName: name } + do { + const response: Lambda.ListLayerVersionsResponse = await client.listLayerVersions(request).promise() + + if (response.LayerVersions) { + yield* response.LayerVersions + } + + request.Marker = response.NextMarker + } while (request.Marker) + } + public async getFunctionUrlConfigs(name: string): Promise { getLogger().debug(`GetFunctionUrlConfig called for function: ${name}`) const client = await this.createSdkClient() @@ -128,3 +166,21 @@ export class DefaultLambdaClient { ) } } + +export async function getFunctionWithCredentials(region: string, name: string): Promise { + const connection = await getIAMConnection({ + prompt: true, + messageText: 'Opening a Lambda Function requires you to be authenticated.', + }) + + if (!connection) { + throw new CancellationError('user') + } + + const credentials = + connection.type === 'iam' ? await connection.getCredentials() : fromSSO({ profile: connection.id }) + const client = new LambdaSdkClient({ region, credentials }) + + const command = new GetFunctionCommand({ FunctionName: name }) + return client.send(command) +} diff --git a/packages/core/src/shared/cloudformation/cloudformation.ts b/packages/core/src/shared/cloudformation/cloudformation.ts index 5d08bb836dc..690a5aee73c 100644 --- a/packages/core/src/shared/cloudformation/cloudformation.ts +++ b/packages/core/src/shared/cloudformation/cloudformation.ts @@ -16,6 +16,10 @@ import { isUntitledScheme, normalizeVSCodeUri } from '../utilities/vsCodeUtils' export const SERVERLESS_API_TYPE = 'AWS::Serverless::Api' // eslint-disable-line @typescript-eslint/naming-convention export const SERVERLESS_FUNCTION_TYPE = 'AWS::Serverless::Function' // eslint-disable-line @typescript-eslint/naming-convention export const LAMBDA_FUNCTION_TYPE = 'AWS::Lambda::Function' // eslint-disable-line @typescript-eslint/naming-convention +export const LAMBDA_LAYER_TYPE = 'AWS::Lambda::LayerVersion' // eslint-disable-line @typescript-eslint/naming-convention +export const LAMBDA_URL_TYPE = 'AWS::Lambda::Url' // eslint-disable-line @typescript-eslint/naming-convention +export const SERVERLESS_LAYER_TYPE = 'AWS::Serverless::LayerVersion' // eslint-disable-line @typescript-eslint/naming-convention + export const serverlessTableType = 'AWS::Serverless::SimpleTable' export const s3BucketType = 'AWS::S3::Bucket' export const appRunnerType = 'AWS::AppRunner::Service' diff --git a/packages/core/src/shared/telemetry/vscodeTelemetry.json b/packages/core/src/shared/telemetry/vscodeTelemetry.json index d075afa8e81..9b29d1a65a0 100644 --- a/packages/core/src/shared/telemetry/vscodeTelemetry.json +++ b/packages/core/src/shared/telemetry/vscodeTelemetry.json @@ -1137,6 +1137,10 @@ { "name": "docdb_addRegion", "description": "User clicked on add region command" + }, + { + "name": "appbuilder_lambda2sam", + "description": "User click Convert a lambda function to SAM project" } ] } diff --git a/packages/core/src/test/awsService/appBuilder/lambda2sam/lambda2sam.test.ts b/packages/core/src/test/awsService/appBuilder/lambda2sam/lambda2sam.test.ts new file mode 100644 index 00000000000..d26d0131d1e --- /dev/null +++ b/packages/core/src/test/awsService/appBuilder/lambda2sam/lambda2sam.test.ts @@ -0,0 +1,272 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as sinon from 'sinon' +import assert from 'assert' +import { LambdaFunctionNode } from '../../../../lambda/explorer/lambdaFunctionNode' +import { DefaultLambdaClient } from '../../../../shared/clients/lambdaClient' +import { Template } from '../../../../shared/cloudformation/cloudformation' +import * as lambda2sam from '../../../../awsService/appBuilder/lambda2sam/lambda2sam' +import * as authUtils from '../../../../auth/utils' +import * as utils from '../../../../awsService/appBuilder/utils' + +describe('lambda2sam', function () { + let sandbox: sinon.SinonSandbox + + beforeEach(function () { + sandbox = sinon.createSandbox() + }) + + afterEach(function () { + sandbox.restore() + }) + + describe('ifSamTemplate', function () { + it('returns true when transform is a string and starts with AWS::Serverless', function () { + const template: Template = { + Transform: 'AWS::Serverless-2016-10-31', + } + assert.strictEqual(lambda2sam.ifSamTemplate(template), true) + }) + + it('returns false when transform is a string and does not start with AWS::Serverless', function () { + const template: Template = { + Transform: 'AWS::Other-Transform', + } + assert.strictEqual(lambda2sam.ifSamTemplate(template), false) + }) + + it('returns true when transform is an array and at least one starts with AWS::Serverless', function () { + const template: Template = { + Transform: ['AWS::Serverless-2016-10-31', 'AWS::Other-Transform'] as any, + } + assert.strictEqual(lambda2sam.ifSamTemplate(template), true) + }) + + it('returns false when transform is an array and none start with AWS::Serverless', function () { + const template: Template = { + Transform: ['AWS::Other-Transform-1', 'AWS::Other-Transform-2'] as any, + } + assert.strictEqual(lambda2sam.ifSamTemplate(template), false) + }) + + it('returns false when transform is not present', function () { + const template: Template = {} + assert.strictEqual(lambda2sam.ifSamTemplate(template), false) + }) + + it('returns false when transform is an unsupported type', function () { + const template: Template = { + Transform: { some: 'object' } as any, + } + assert.strictEqual(lambda2sam.ifSamTemplate(template), false) + }) + }) + + describe('extractLogicalIdFromIntrinsic', function () { + it('extracts logical ID from Ref intrinsic', function () { + const value = { Ref: 'MyResource' } + assert.strictEqual(lambda2sam.extractLogicalIdFromIntrinsic(value), 'MyResource') + }) + + it('extracts logical ID from GetAtt intrinsic with Arn attribute', function () { + const value = { 'Fn::GetAtt': ['MyResource', 'Arn'] } + assert.strictEqual(lambda2sam.extractLogicalIdFromIntrinsic(value), 'MyResource') + }) + + it('returns undefined for GetAtt intrinsic with non-Arn attribute', function () { + const value = { 'Fn::GetAtt': ['MyResource', 'Name'] } + assert.strictEqual(lambda2sam.extractLogicalIdFromIntrinsic(value), undefined) + }) + + it('returns undefined for non-intrinsic values', function () { + assert.strictEqual(lambda2sam.extractLogicalIdFromIntrinsic('not-an-intrinsic'), undefined) + assert.strictEqual(lambda2sam.extractLogicalIdFromIntrinsic({ NotIntrinsic: 'value' }), undefined) + assert.strictEqual(lambda2sam.extractLogicalIdFromIntrinsic(undefined), undefined) + }) + }) + + describe('callExternalApiForCfnTemplate', function () { + let lambdaClientStub: sinon.SinonStubbedInstance + let cfnClientStub: any + + beforeEach(function () { + lambdaClientStub = sandbox.createStubInstance(DefaultLambdaClient) + // Stub at prototype level to avoid TypeScript errors + sandbox + .stub(DefaultLambdaClient.prototype, 'getFunction') + .callsFake((name) => lambdaClientStub.getFunction(name)) + + // Mock CloudFormation client for the new external API calls - now returns Promises directly + cfnClientStub = { + getGeneratedTemplate: sandbox.stub().resolves({ + Status: 'COMPLETE', + TemplateBody: JSON.stringify({ + AWSTemplateFormatVersion: '2010-09-09', + Resources: { + testFunc: { + DeletionPolicy: 'Retain', + Properties: { + Code: { + S3Bucket: 'aws-sam-cli-managed-default-samclisourcebucket-1n8tvb0jdhsd', + S3Key: '1d1c93ec17af7e2666ee20ea1a215c77', + }, + Environment: { + Variables: { + KEY: 'value', + }, + }, + FunctionName: 'myFunction', + Handler: 'index.handler', + MemorySize: 128, + PackageType: 'Zip', + Role: 'arn:aws:iam::123456789012:role/lambda-role', + Runtime: 'nodejs18.x', + Timeout: 3, + }, + Type: 'AWS::Lambda::Function', + }, + }, + }), + }), + describeGeneratedTemplate: sandbox.stub().resolves({ + Status: 'COMPLETE', + Resources: [ + { + LogicalResourceId: 'testFunc', + ResourceType: 'AWS::Lambda::Function', + ResourceIdentifier: { + FunctionName: 'myFunction', + }, + }, + ], + }), + } + sandbox.stub(utils, 'getCFNClient').resolves(cfnClientStub) + + // Mock IAM connection + const mockConnection = { + type: 'iam' as const, + id: 'test-connection', + label: 'Test Connection', + state: 'valid' as const, + getCredentials: sandbox.stub().resolves({ + accessKeyId: 'test-key', + secretAccessKey: 'test-secret', + }), + } + sandbox.stub(authUtils, 'getIAMConnection').resolves(mockConnection) + + // Mock fetch response + sandbox.stub(global, 'fetch').resolves({ + ok: true, + json: sandbox.stub().resolves({ + cloudFormationTemplateId: 'test-template-id', + }), + } as any) + }) + + it('creates a basic CloudFormation template for the Lambda function', async function () { + const lambdaNode = { + name: 'myFunction', + regionCode: 'us-east-2', + arn: 'arn:aws:lambda:us-east-2:123456789012:function:myFunction', + } as LambdaFunctionNode + + lambdaClientStub.getFunction.resolves({ + Configuration: { + FunctionName: 'myFunction', + Handler: 'index.handler', + Role: 'arn:aws:iam::123456789012:role/lambda-role', + Runtime: 'nodejs18.x', + Timeout: 3, + MemorySize: 128, + Environment: { Variables: { KEY: 'value' } }, + }, + }) + + // Create a simple mock template that matches the Template type + const mockTemplate = { + AWSTemplateFormatVersion: '2010-09-09', + Resources: { + testFunc: { + DeletionPolicy: 'Retain', + Properties: { + Code: { + S3Bucket: 'aws-sam-cli-managed-default-samclisourcebucket-1n8tvb0jdhsd', + S3Key: '1d1c93ec17af7e2666ee20ea1a215c77', + }, + Environment: { + Variables: { + KEY: 'value', + }, + }, + FunctionName: 'myFunction', + Handler: 'index.handler', + MemorySize: 128, + PackageType: 'Zip', + Role: 'arn:aws:iam::123456789012:role/lambda-role', + Runtime: 'nodejs18.x', + Timeout: 3, + }, + Type: 'AWS::Lambda::Function', + }, + }, + } + const mockList = [ + { + LogicalResourceId: 'testFunc', + ResourceIdentifier: { + FunctionName: 'myFunction', + }, + ResourceType: 'AWS::Lambda::Function', + }, + ] + + const result = await lambda2sam.callExternalApiForCfnTemplate(lambdaNode) + // Verify the result structure matches expected format + assert.strictEqual(Array.isArray(result), true) + assert.strictEqual(result.length, 2) + const [template, resourcesToImport] = result + assert.strictEqual(typeof template, 'object') + assert.strictEqual(Array.isArray(resourcesToImport), true) + assert.strictEqual(resourcesToImport.length, 1) + assert.strictEqual(resourcesToImport[0].ResourceType, 'AWS::Lambda::Function') + assert.strictEqual(resourcesToImport[0].LogicalResourceId, 'testFunc') + assert.deepStrictEqual(result, [mockTemplate, mockList]) + }) + }) + + describe('determineStackAssociation', function () { + let lambdaClientStub: sinon.SinonStubbedInstance + + beforeEach(function () { + lambdaClientStub = sandbox.createStubInstance(DefaultLambdaClient) + sandbox + .stub(DefaultLambdaClient.prototype, 'getFunction') + .callsFake((name) => lambdaClientStub.getFunction(name)) + }) + + it('returns undefined when Lambda has no tags', async function () { + const lambdaNode = { + name: 'myFunction', + regionCode: 'us-west-2', + } as LambdaFunctionNode + + lambdaClientStub.getFunction.resolves({}) + + // Skip CloudFormation mocking for now + // This is difficult to mock correctly without errors and would be better tested with integration tests + + const result = await lambda2sam.determineStackAssociation(lambdaNode) + + assert.strictEqual(result, undefined) + assert.strictEqual(lambdaClientStub.getFunction.calledOnceWith(lambdaNode.name), true) + }) + + // For this function, additional testing would require complex mocking of the AWS SDK + // Consider adding more specific test cases in an integration test + }) +}) diff --git a/packages/core/src/test/awsService/appBuilder/lambda2sam/lambda2samCoreLogic.test.ts b/packages/core/src/test/awsService/appBuilder/lambda2sam/lambda2samCoreLogic.test.ts new file mode 100644 index 00000000000..552d0104b7e --- /dev/null +++ b/packages/core/src/test/awsService/appBuilder/lambda2sam/lambda2samCoreLogic.test.ts @@ -0,0 +1,784 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as sinon from 'sinon' +import assert from 'assert' +import * as vscode from 'vscode' +import * as lambda2sam from '../../../../awsService/appBuilder/lambda2sam/lambda2sam' +import * as cloudFormation from '../../../../shared/cloudformation/cloudformation' +import * as utils from '../../../../awsService/appBuilder/utils' +import * as walkthrough from '../../../../awsService/appBuilder/walkthrough' +import * as authUtils from '../../../../auth/utils' +import { getTestWindow } from '../../../shared/vscode/window' +import { fs } from '../../../../shared' +import { DefaultLambdaClient } from '../../../../shared/clients/lambdaClient' +import { LambdaFunctionNode } from '../../../../lambda/explorer/lambdaFunctionNode' +import { ToolkitError } from '../../../../shared/errors' +import os from 'os' +import path from 'path' +import { LAMBDA_FUNCTION_TYPE } from '../../../../shared/cloudformation/cloudformation' +import { ResourcesToImport } from 'aws-sdk/clients/cloudformation' + +describe('lambda2samCoreLogic', function () { + let sandbox: sinon.SinonSandbox + let tempDir: string + let lambdaClientStub: sinon.SinonStubbedInstance + let cfnClientStub: any + let downloadUnzipStub: sinon.SinonStub + + beforeEach(async function () { + sandbox = sinon.createSandbox() + tempDir = path.join(os.tmpdir(), `aws-toolkit-test-${Date.now()}`) + + // Create temp directory for tests - actually create it, don't stub + if (!(await fs.exists(vscode.Uri.file(tempDir)))) { + await fs.mkdir(vscode.Uri.file(tempDir)) + } + + // Create Lambda client stub with necessary properties + lambdaClientStub = sandbox.createStubInstance(DefaultLambdaClient) + Object.defineProperty(lambdaClientStub, 'defaultTimeoutInMs', { + value: 5 * 60 * 1000, // 5 minutes + configurable: true, + }) + Object.defineProperty(lambdaClientStub, 'createSdkClient', { + value: () => Promise.resolve({}), + configurable: true, + }) + + sandbox.stub(utils, 'getLambdaClient').returns(lambdaClientStub as any) + + // Mock CloudFormation client - now returns Promises directly (no .promise() method) + cfnClientStub = { + describeStackResource: sandbox.stub().resolves({ + StackResourceDetail: { + PhysicalResourceId: 'test-physical-id', + }, + }), + describeStackResources: sandbox.stub().resolves({ + StackResources: [ + { LogicalResourceId: 'testResource', PhysicalResourceId: 'test-physical-id' }, + { LogicalResourceId: 'prefixTestResource', PhysicalResourceId: 'prefix-test-physical-id' }, + ], + }), + describeStacks: sandbox.stub().resolves({ + Stacks: [ + { + StackId: 'stack-id', + StackName: 'test-stack', + StackStatus: 'CREATE_COMPLETE', + }, + ], + }), + getTemplate: sandbox.stub().resolves({ + TemplateBody: '{"Resources": {"TestFunc": {"Type": "AWS::Lambda::Function"}}}', + }), + getGeneratedTemplate: sandbox.stub().resolves({ + Status: 'COMPLETE', + TemplateBody: + '{"Resources": {"TestFunc": {"Type": "AWS::Lambda::Function", "Properties": {"FunctionName": "test-function"}}}}', + }), + describeGeneratedTemplate: sandbox.stub().resolves({ + Status: 'COMPLETE', + Resources: [ + { + LogicalResourceId: 'TestFunc', + ResourceType: 'AWS::Lambda::Function', + ResourceIdentifier: { + FunctionName: 'arn:aws:lambda:us-east-2:123456789012:function:test-function', + }, + }, + ], + }), + createChangeSet: sandbox.stub().resolves({ Id: 'change-set-id' }), + waitFor: sandbox.stub().resolves(), + executeChangeSet: sandbox.stub().resolves(), + describeChangeSet: sandbox.stub().resolves({ + StatusReason: 'Test reason', + }), + } + sandbox.stub(utils, 'getCFNClient').resolves(cfnClientStub) + + // Setup test window to return appropriate values + getTestWindow().onDidShowMessage((msg) => { + if (msg.message.includes('Enter Stack Name')) { + msg.selectItem('test-stack') + } + }) + + getTestWindow().onDidShowDialog((dialog) => { + dialog.selectItem(vscode.Uri.file(tempDir)) + }) + + // Stub downloadUnzip function + downloadUnzipStub = sandbox.stub(utils, 'downloadUnzip').callsFake(async (url, outputPath) => { + // Create a mock file structure for testing purposes + if (!(await fs.exists(outputPath))) { + await fs.mkdir(outputPath) + } + + await fs.writeFile( + vscode.Uri.joinPath(outputPath, 'index.js'), + 'exports.handler = async (event) => { return "Hello World" };' + ) + await fs.writeFile( + vscode.Uri.joinPath(outputPath, 'package.json'), + JSON.stringify( + { + name: 'test-lambda', + version: '1.0.0', + description: 'Test Lambda function', + }, + undefined, + 2 + ) + ) + }) + + // Stub workspace functions + sandbox.stub(vscode.workspace, 'openTextDocument').resolves({} as any) + sandbox.stub(vscode.window, 'showTextDocument').resolves() + }) + + afterEach(async function () { + sandbox.restore() + + // Clean up the temp directory + if (await fs.exists(vscode.Uri.file(tempDir))) { + await fs.delete(vscode.Uri.file(tempDir), { recursive: true, force: true }) + } + }) + + describe('processLambdaUrlResources', function () { + it('converts Lambda URL resources to FunctionUrlConfig', async function () { + // Setup resources with Lambda URL - using 'as any' to bypass strict typing for tests + const resources: cloudFormation.TemplateResources = { + TestFunc: { + Type: cloudFormation.SERVERLESS_FUNCTION_TYPE, + Properties: { + FunctionName: 'test-function', + PackageType: 'Zip', + }, + }, + TestFuncUrl: { + Type: cloudFormation.LAMBDA_URL_TYPE, + Properties: { + TargetFunctionArn: { Ref: 'TestFunc' }, + AuthType: 'NONE', + }, + }, + } as any + + // Call the function + await lambda2sam.processLambdaUrlResources(resources) + + // Verify URL resource is removed + assert.strictEqual(resources['TestFuncUrl'], undefined) + + // Verify FunctionUrlConfig added to function resource using non-null assertion + assert.deepStrictEqual(resources['TestFunc']!.Properties!.FunctionUrlConfig, { + AuthType: 'NONE', + Cors: undefined, + InvokeMode: undefined, + }) + }) + + it('skips URL resources with Qualifier property', async function () { + // Setup resources with Lambda URL including Qualifier - using 'as any' to bypass strict typing for tests + const resources: cloudFormation.TemplateResources = { + TestFunc: { + Type: cloudFormation.SERVERLESS_FUNCTION_TYPE, + Properties: { + FunctionName: 'test-function', + PackageType: 'Zip', + }, + }, + TestFuncUrl: { + Type: cloudFormation.LAMBDA_URL_TYPE, + Properties: { + TargetFunctionArn: { Ref: 'TestFunc' }, + AuthType: 'NONE', + Qualifier: 'prod', + }, + }, + } as any + + // Call the function + await lambda2sam.processLambdaUrlResources(resources) + + // Verify URL resource is still there (not transformed) + assert.notStrictEqual(resources['TestFuncUrl'], undefined) + + // Verify function resource doesn't have FunctionUrlConfig using non-null assertion + assert.strictEqual(resources['TestFunc']!.Properties!.FunctionUrlConfig, undefined) + }) + }) + + describe('processLambdaResources', function () { + it('transforms AWS::Lambda::Function to AWS::Serverless::Function', async function () { + // Setup resources with Lambda function - using 'as any' to bypass strict typing for tests + const resources: cloudFormation.TemplateResources = { + TestFunc: { + Type: cloudFormation.LAMBDA_FUNCTION_TYPE, + Properties: { + FunctionName: 'test-function', + Handler: 'index.handler', + Runtime: 'nodejs18.x', + Code: { + S3Bucket: 'test-bucket', + S3Key: 'test-key', + }, + Tags: [ + { Key: 'test-key', Value: 'test-value' }, + { Key: 'lambda:createdBy', Value: 'test' }, + ], + TracingConfig: { + Mode: 'Active', + }, + PackageType: 'Zip', + }, + }, + } as any + + const stackInfo = { + stackId: 'stack-id', + stackName: 'test-stack', + isSamTemplate: false, + template: {}, + } + + const projectDir = vscode.Uri.file(tempDir) + + // Add necessary stub for getFunction + lambdaClientStub.getFunction.resolves({ + Code: { Location: 'https://lambda-function-code.zip' }, + }) + + // Call the function + await lambda2sam.processLambdaResources(resources, projectDir, stackInfo, 'us-west-2') + + // Verify function type was transformed using non-null assertions + assert.strictEqual(resources['TestFunc']!.Type, cloudFormation.SERVERLESS_FUNCTION_TYPE) + + // Verify properties were transformed correctly using non-null assertions + assert.strictEqual(resources['TestFunc']!.Properties!.Code, undefined) + assert.strictEqual(resources['TestFunc']!.Properties!.CodeUri, 'TestFunc') + assert.strictEqual(resources['TestFunc']!.Properties!.Tracing, 'Active') + assert.strictEqual(resources['TestFunc']!.Properties!.TracingConfig, undefined) + assert.deepStrictEqual(resources['TestFunc']!.Properties!.Tags, { + 'test-key': 'test-value', + }) + + // Verify downloadLambdaFunctionCode was called + assert.strictEqual(downloadUnzipStub.calledOnce, true) + }) + + it('updates CodeUri for AWS::Serverless::Function', async function () { + // Setup resources with Serverless function - using 'as any' to bypass strict typing for tests + const resources: cloudFormation.TemplateResources = { + TestFunc: { + Type: cloudFormation.SERVERLESS_FUNCTION_TYPE, + Properties: { + FunctionName: 'test-function', + Handler: 'index.handler', + Runtime: 'nodejs18.x', + CodeUri: 's3://test-bucket/test-key', + PackageType: 'Zip', + }, + }, + } as any + + const stackInfo = { + stackId: 'stack-id', + stackName: 'test-stack', + isSamTemplate: false, + template: {}, + } + + const projectDir = vscode.Uri.file(tempDir) + + // Add necessary stub for getFunction + lambdaClientStub.getFunction.resolves({ + Code: { Location: 'https://lambda-function-code.zip' }, + }) + + // Call the function + await lambda2sam.processLambdaResources(resources, projectDir, stackInfo, 'us-west-2') + + // Verify CodeUri was updated using non-null assertions + assert.strictEqual(resources['TestFunc']!.Properties!.CodeUri, 'TestFunc') + + // Verify downloadLambdaFunctionCode was called + assert.strictEqual(downloadUnzipStub.calledOnce, true) + }) + }) + + describe('processLambdaLayerResources', function () { + it('transforms AWS::Lambda::LayerVersion to AWS::Serverless::LayerVersion', async function () { + // Setup resources with Lambda layer - using 'as any' to bypass strict typing for tests + const resources: cloudFormation.TemplateResources = { + TestLayer: { + Type: cloudFormation.LAMBDA_LAYER_TYPE, + Properties: { + LayerName: 'test-layer', + Content: { + S3Bucket: 'test-bucket', + S3Key: 'test-key', + }, + CompatibleRuntimes: ['nodejs18.x'], + }, + }, + } as any + + const stackInfo = { + stackId: 'stack-id', + stackName: 'test-stack', + isSamTemplate: false, + template: {}, + } + + const projectDir = vscode.Uri.file(tempDir) + + // Setup layer version stub + cfnClientStub.describeStackResource.resolves({ + StackResourceDetail: { + PhysicalResourceId: 'arn:aws:lambda:us-west-2:123456789012:layer:my-layer:1', + }, + }) + + lambdaClientStub.getLayerVersion.resolves({ + Content: { Location: 'https://lambda-layer-code.zip' }, + }) + + // Call the function + await lambda2sam.processLambdaLayerResources(resources, projectDir, stackInfo, 'us-west-2') + + // Verify layer type was transformed using non-null assertions + assert.strictEqual(resources['TestLayer']!.Type, cloudFormation.SERVERLESS_LAYER_TYPE) + + // Verify properties were transformed correctly using non-null assertions + assert.strictEqual(resources['TestLayer']!.Properties!.Content, undefined) + assert.strictEqual(resources['TestLayer']!.Properties!.ContentUri, 'TestLayer') + assert.deepStrictEqual(resources['TestLayer']!.Properties!.CompatibleRuntimes, ['nodejs18.x']) + + // Verify downloadLayerVersionResrouceByName was called (through downloadUnzip) + assert.strictEqual(downloadUnzipStub.calledOnce, true) + }) + + it('preserves AWS::Serverless::LayerVersion properties', async function () { + // Setup resources with Serverless layer - using 'as any' to bypass strict typing for tests + const resources: cloudFormation.TemplateResources = { + TestLayer: { + Type: cloudFormation.SERVERLESS_LAYER_TYPE, + Properties: { + LayerName: 'test-layer', + ContentUri: 's3://test-bucket/test-key', + CompatibleRuntimes: ['nodejs18.x'], + }, + }, + } as any + + const stackInfo = { + stackId: 'stack-id', + stackName: 'test-stack', + isSamTemplate: false, + template: {}, + } + + const projectDir = vscode.Uri.file(tempDir) + + // Setup layer version stub + cfnClientStub.describeStackResource.resolves({ + StackResourceDetail: { + PhysicalResourceId: 'arn:aws:lambda:us-west-2:123456789012:layer:my-layer:1', + }, + }) + + lambdaClientStub.getLayerVersion.resolves({ + Content: { Location: 'https://lambda-layer-code.zip' }, + }) + + // Call the function + await lambda2sam.processLambdaLayerResources(resources, projectDir, stackInfo, 'us-west-2') + + // Verify layer type is still serverless using non-null assertions + assert.strictEqual(resources['TestLayer']!.Type, cloudFormation.SERVERLESS_LAYER_TYPE) + + // Verify ContentUri was updated using non-null assertions + assert.strictEqual(resources['TestLayer']!.Properties!.ContentUri, 'TestLayer') + + // Verify downloadLayerVersionResrouceByName was called (through downloadUnzip) + assert.strictEqual(downloadUnzipStub.calledOnce, true) + }) + }) + + describe('deployCfnTemplate', function () { + it('deploys a CloudFormation template and returns stack info', async function () { + // Setup CloudFormation template - using 'as any' to bypass strict typing for tests + const template: cloudFormation.Template = { + AWSTemplateFormatVersion: '2010-09-09', + Resources: { + TestFunc: { + Type: cloudFormation.LAMBDA_FUNCTION_TYPE, + Properties: { + FunctionName: 'test-function', + PackageType: 'Zip', + }, + }, + }, + } as any + + // Setup Lambda node + const lambdaNode = { + name: 'test-function', + regionCode: 'us-west-2', + } as LambdaFunctionNode + + const resourceToImport: ResourcesToImport = [ + { + ResourceType: LAMBDA_FUNCTION_TYPE, + LogicalResourceId: 'TestFunc', + ResourceIdentifier: { + FunctionName: lambdaNode.name, + }, + }, + ] + + // Call the function + const result = await lambda2sam.deployCfnTemplate( + template, + resourceToImport, + 'test-stack', + lambdaNode.regionCode + ) + + // Verify createChangeSet was called with correct parameters + assert.strictEqual(cfnClientStub.createChangeSet.called, true) + const createChangeSetArgs = cfnClientStub.createChangeSet.firstCall.args[0] + assert.strictEqual(createChangeSetArgs.StackName, 'test-stack') + assert.strictEqual(createChangeSetArgs.ChangeSetType, 'IMPORT') + + // Verify waitFor and executeChangeSet were called + assert.strictEqual(cfnClientStub.waitFor.calledWith('changeSetCreateComplete'), true) + assert.strictEqual(cfnClientStub.executeChangeSet.called, true) + + // Verify describeStacks was called to get stack ID + assert.strictEqual(cfnClientStub.describeStacks.called, true) + + // Verify result structure + assert.strictEqual(result.stackId, 'stack-id') + assert.strictEqual(result.stackName, 'test-stack') + assert.strictEqual(result.isSamTemplate, false) + assert.deepStrictEqual(result.template, template) + }) + + it('throws an error when change set creation fails', async function () { + // Setup CloudFormation template - using 'as any' to bypass strict typing for tests + const template: cloudFormation.Template = { + AWSTemplateFormatVersion: '2010-09-09', + Resources: { + TestFunc: { + Type: cloudFormation.LAMBDA_FUNCTION_TYPE, + Properties: { + FunctionName: 'test-function', + PackageType: 'Zip', + }, + }, + }, + } as any + + // Setup Lambda node + const lambdaNode = { + name: 'test-function', + regionCode: 'us-west-2', + } as LambdaFunctionNode + + // Make createChangeSet fail + cfnClientStub.createChangeSet.resolves({}) // No Id + + const resourceToImport: ResourcesToImport = [ + { + ResourceType: LAMBDA_FUNCTION_TYPE, + LogicalResourceId: 'TestFunc', + ResourceIdentifier: { + FunctionName: lambdaNode.name, + }, + }, + ] + + // Call the function and expect error + await assert.rejects( + lambda2sam.deployCfnTemplate(template, resourceToImport, 'test-stack', lambdaNode.regionCode), + (err: ToolkitError) => { + assert.strictEqual(err.message.includes('Failed to create change set'), true) + return true + } + ) + }) + }) + + describe('callExternalApiForCfnTemplate', function () { + it('extracts function name from ARN in ResourceIdentifier', async function () { + // Setup Lambda node + const lambdaNode = { + name: 'test-function', + regionCode: 'us-east-2', + arn: 'arn:aws:lambda:us-east-2:123456789012:function:test-function', + } as LambdaFunctionNode + + // Mock IAM connection + const mockConnection = { + type: 'iam' as const, + id: 'test-connection', + label: 'Test Connection', + state: 'valid' as const, + getCredentials: sandbox.stub().resolves({ + accessKeyId: 'test-key', + secretAccessKey: 'test-secret', + }), + } + sandbox.stub(authUtils, 'getIAMConnection').resolves(mockConnection) + + // Mock fetch response + const mockFetch = sandbox.stub(global, 'fetch').resolves({ + ok: true, + json: sandbox.stub().resolves({ + cloudFormationTemplateId: 'test-template-id', + }), + } as any) + + // Setup CloudFormation client to return ARN in ResourceIdentifier + cfnClientStub.describeGeneratedTemplate.resolves({ + Status: 'COMPLETE', + Resources: [ + { + LogicalResourceId: 'TestFunc', + ResourceType: 'AWS::Lambda::Function', + ResourceIdentifier: { + FunctionName: 'arn:aws:lambda:us-east-2:123456789012:function:test-function', + }, + }, + ], + }) + + // Call the function + const [_, resourcesToImport] = await lambda2sam.callExternalApiForCfnTemplate(lambdaNode) + + // Verify that the ARN was converted to just the function name + assert.strictEqual(resourcesToImport.length, 1) + assert.strictEqual(resourcesToImport[0].ResourceType, 'AWS::Lambda::Function') + assert.strictEqual(resourcesToImport[0].LogicalResourceId, 'TestFunc') + assert.strictEqual(resourcesToImport[0].ResourceIdentifier!.FunctionName, 'test-function') + + // Verify API calls were made + assert.strictEqual(mockFetch.calledOnce, true) + assert.strictEqual(cfnClientStub.getGeneratedTemplate.calledOnce, true) + assert.strictEqual(cfnClientStub.describeGeneratedTemplate.calledOnce, true) + }) + + it('preserves function name when not an ARN', async function () { + // Setup Lambda node + const lambdaNode = { + name: 'test-function', + regionCode: 'us-east-2', + arn: 'arn:aws:lambda:us-east-2:123456789012:function:test-function', + } as LambdaFunctionNode + + // Mock IAM connection + const mockConnection = { + type: 'iam' as const, + id: 'test-connection', + label: 'Test Connection', + state: 'valid' as const, + getCredentials: sandbox.stub().resolves({ + accessKeyId: 'test-key', + secretAccessKey: 'test-secret', + }), + } + sandbox.stub(authUtils, 'getIAMConnection').resolves(mockConnection) + + // Mock fetch response + sandbox.stub(global, 'fetch').resolves({ + ok: true, + json: sandbox.stub().resolves({ + cloudFormationTemplateId: 'test-template-id', + }), + } as any) + + // Setup CloudFormation client to return plain function name + cfnClientStub.describeGeneratedTemplate.resolves({ + Status: 'COMPLETE', + Resources: [ + { + LogicalResourceId: 'TestFunc', + ResourceType: 'AWS::Lambda::Function', + ResourceIdentifier: { + FunctionName: 'test-function', + }, + }, + ], + }) + + // Call the function + const [_, resourcesToImport] = await lambda2sam.callExternalApiForCfnTemplate(lambdaNode) + + // Verify that the function name was preserved + assert.strictEqual(resourcesToImport.length, 1) + assert.strictEqual(resourcesToImport[0].ResourceIdentifier!.FunctionName, 'test-function') + }) + + it('handles non-Lambda resources without modification', async function () { + // Setup Lambda node + const lambdaNode = { + name: 'test-function', + regionCode: 'us-east-2', + arn: 'arn:aws:lambda:us-east-2:123456789012:function:test-function', + } as LambdaFunctionNode + + // Mock IAM connection + const mockConnection = { + type: 'iam' as const, + id: 'test-connection', + label: 'Test Connection', + state: 'valid' as const, + getCredentials: sandbox.stub().resolves({ + accessKeyId: 'test-key', + secretAccessKey: 'test-secret', + }), + } + sandbox.stub(authUtils, 'getIAMConnection').resolves(mockConnection) + + // Mock fetch response + sandbox.stub(global, 'fetch').resolves({ + ok: true, + json: sandbox.stub().resolves({ + cloudFormationTemplateId: 'test-template-id', + }), + } as any) + + // Setup CloudFormation client to return mixed resource types + cfnClientStub.describeGeneratedTemplate.resolves({ + Status: 'COMPLETE', + Resources: [ + { + LogicalResourceId: 'TestFunc', + ResourceType: 'AWS::Lambda::Function', + ResourceIdentifier: { + FunctionName: 'arn:aws:lambda:us-east-2:123456789012:function:test-function', + }, + }, + { + LogicalResourceId: 'TestRole', + ResourceType: 'AWS::IAM::Role', + ResourceIdentifier: { + RoleName: 'test-role', + }, + }, + ], + }) + + // Call the function + const [_, resourcesToImport] = await lambda2sam.callExternalApiForCfnTemplate(lambdaNode) + + // Verify that Lambda function ARN was converted but IAM role was not + assert.strictEqual(resourcesToImport.length, 2) + + const lambdaResource = resourcesToImport.find((r) => r.ResourceType === 'AWS::Lambda::Function') + const iamResource = resourcesToImport.find((r) => r.ResourceType === 'AWS::IAM::Role') + + assert.strictEqual(lambdaResource!.ResourceIdentifier!.FunctionName, 'test-function') + assert.strictEqual(iamResource!.ResourceIdentifier!.RoleName, 'test-role') + }) + }) + + describe('lambdaToSam', function () { + it('converts a Lambda function to a SAM project', async function () { + // Setup Lambda node + const lambdaNode = { + name: 'test-function', + regionCode: 'us-west-2', + } as LambdaFunctionNode + + // Setup AWS Lambda client responses + lambdaClientStub.getFunction.resolves({ + Tags: { + 'aws:cloudformation:stack-id': 'stack-id', + 'aws:cloudformation:stack-name': 'test-stack', + }, + Configuration: { + FunctionName: 'test-function', + Handler: 'index.handler', + Runtime: 'nodejs18.x', + }, + Code: { + Location: 'https://lambda-function-code.zip', + }, + }) + + // Setup CloudFormation client responses + cfnClientStub.describeStacks.resolves({ + Stacks: [ + { + StackId: 'stack-id', + StackName: 'test-stack', + StackStatus: 'CREATE_COMPLETE', + }, + ], + }) + + cfnClientStub.getTemplate.resolves({ + TemplateBody: JSON.stringify({ + AWSTemplateFormatVersion: '2010-09-09', + Transform: 'AWS::Serverless-2016-10-31', + Resources: { + TestFunc: { + Type: 'AWS::Serverless::Function', + Properties: { + FunctionName: 'test-function', + Handler: 'index.handler', + Runtime: 'nodejs18.x', + CodeUri: 's3://test-bucket/test-key', + PackageType: 'Zip', + }, + }, + }, + }), + }) + + // Setup test window to return a project directory + getTestWindow().onDidShowDialog((dialog) => { + dialog.selectItem(vscode.Uri.file(tempDir)) + }) + // Spy on walkthrough.openProjectInWorkspace + const openProjectStub = sandbox.stub(walkthrough, 'openProjectInWorkspace') + + // Call the function + await lambda2sam.lambdaToSam(lambdaNode) + + assert.strictEqual( + await fs.exists(vscode.Uri.joinPath(vscode.Uri.file(tempDir), 'test-stack', 'template.yaml').fsPath), + true, + 'template.yaml was not written' + ) + assert.strictEqual( + await fs.exists(vscode.Uri.joinPath(vscode.Uri.file(tempDir), 'test-stack', 'README.md').fsPath), + true, + 'README.md was not written' + ) + assert.strictEqual( + await fs.exists(vscode.Uri.joinPath(vscode.Uri.file(tempDir), 'test-stack', 'samconfig.toml').fsPath), + true, + 'samconfig.toml was not written' + ) + + // Verify that project was opened in workspace + assert.strictEqual(openProjectStub.calledOnce, true) + assert.strictEqual( + openProjectStub.firstCall.args[0].fsPath, + vscode.Uri.joinPath(vscode.Uri.file(tempDir), 'test-stack').fsPath + ) + }) + }) +}) diff --git a/packages/core/src/test/awsService/appBuilder/lambda2sam/lambda2samDownload.test.ts b/packages/core/src/test/awsService/appBuilder/lambda2sam/lambda2samDownload.test.ts new file mode 100644 index 00000000000..9c4d3122918 --- /dev/null +++ b/packages/core/src/test/awsService/appBuilder/lambda2sam/lambda2samDownload.test.ts @@ -0,0 +1,312 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as sinon from 'sinon' +import assert from 'assert' +import * as vscode from 'vscode' +import * as lambda2sam from '../../../../awsService/appBuilder/lambda2sam/lambda2sam' +import * as utils from '../../../../awsService/appBuilder/utils' +import { fs } from '../../../../shared' +import { DefaultLambdaClient } from '../../../../shared/clients/lambdaClient' +import os from 'os' +import path from 'path' +import { LAMBDA_FUNCTION_TYPE, LAMBDA_LAYER_TYPE } from '../../../../shared/cloudformation/cloudformation' + +describe('lambda2samDownload', function () { + let sandbox: sinon.SinonSandbox + let tempDir: string + let lambdaClientStub: sinon.SinonStubbedInstance + let cfnClientStub: any + let downloadUnzipStub: sinon.SinonStub + + beforeEach(async function () { + sandbox = sinon.createSandbox() + tempDir = path.join(os.tmpdir(), `aws-toolkit-test-${Date.now()}`) + + // Create temp directory for tests - actually create it, don't stub + if (!(await fs.exists(vscode.Uri.file(tempDir)))) { + await fs.mkdir(vscode.Uri.file(tempDir)) + } + + // Create Lambda client stub with necessary properties + lambdaClientStub = sandbox.createStubInstance(DefaultLambdaClient) + // Add required properties that aren't stubbed automatically + Object.defineProperty(lambdaClientStub, 'defaultTimeoutInMs', { + value: 5 * 60 * 1000, // 5 minutes + configurable: true, + }) + Object.defineProperty(lambdaClientStub, 'createSdkClient', { + value: () => Promise.resolve({}), + configurable: true, + }) + + sandbox.stub(utils, 'getLambdaClient').returns(lambdaClientStub as any) + + // Stub CloudFormation client - now returns Promises directly (no .promise() method) + cfnClientStub = { + describeStackResource: sandbox.stub().resolves({ + StackResourceDetail: { + PhysicalResourceId: 'test-physical-id', + }, + }), + describeStackResources: sandbox.stub().resolves({ + StackResources: [ + { LogicalResourceId: 'testResource', PhysicalResourceId: 'test-physical-id' }, + { LogicalResourceId: 'prefixTestResource', PhysicalResourceId: 'prefix-test-physical-id' }, + ], + }), + } + sandbox.stub(utils, 'getCFNClient').resolves(cfnClientStub) + + // Stub downloadUnzip function to create actual files in the temp directory + downloadUnzipStub = sandbox.stub(utils, 'downloadUnzip').callsFake(async (url, outputPath) => { + // Create a mock file structure for testing purposes + + // Create the output directory if it doesn't exist + if (!(await fs.exists(outputPath))) { + await fs.mkdir(outputPath) + } + + // Create a simple file to simulate extracted content + await fs.writeFile( + vscode.Uri.joinPath(outputPath, 'index.js'), + 'exports.handler = async (event) => { return "Hello World" };' + ) + + // Create a package.json file + await fs.writeFile( + vscode.Uri.joinPath(outputPath, 'package.json'), + JSON.stringify( + { + name: 'test-lambda', + version: '1.0.0', + description: 'Test Lambda function', + }, + undefined, + 2 + ) + ) + }) + }) + + afterEach(async function () { + sandbox.restore() + + // Clean up the temp directory after each test + if (await fs.exists(vscode.Uri.file(tempDir))) { + await fs.delete(vscode.Uri.file(tempDir), { recursive: true, force: true }) + } + }) + + describe('getPhysicalIdfromCFNResourceName', function () { + it('returns the physical ID when an exact match is found', async function () { + const result = await lambda2sam.getPhysicalIdfromCFNResourceName( + 'testResource', + 'us-west-2', + 'stack-id', + LAMBDA_FUNCTION_TYPE + ) + + assert.strictEqual(cfnClientStub.describeStackResource.calledOnce, true) + assert.strictEqual(cfnClientStub.describeStackResource.firstCall.args[0].StackName, 'stack-id') + assert.strictEqual(cfnClientStub.describeStackResource.firstCall.args[0].LogicalResourceId, 'testResource') + assert.strictEqual(result, 'test-physical-id') + }) + + it('returns a prefix match when exact match fails', async function () { + // Make exact match fail + cfnClientStub.describeStackResource.rejects(new Error('Resource not found')) + + const result = await lambda2sam.getPhysicalIdfromCFNResourceName( + 'prefix', + 'us-west-2', + 'stack-id', + LAMBDA_LAYER_TYPE + ) + + assert.strictEqual(cfnClientStub.describeStackResources.calledOnce, true) + assert.strictEqual(cfnClientStub.describeStackResources.firstCall.args[0].StackName, 'stack-id') + assert.strictEqual(result, 'prefix-test-physical-id') + }) + + it('returns undefined when no match is found', async function () { + // Make exact match fail + cfnClientStub.describeStackResource.rejects(new Error('Resource not found')) + + // Return empty resources + cfnClientStub.describeStackResources.resolves({ StackResources: [] }) + + const result = await lambda2sam.getPhysicalIdfromCFNResourceName( + 'nonexistent', + 'us-west-2', + 'stack-id', + LAMBDA_LAYER_TYPE + ) + assert.strictEqual(result, undefined) + }) + }) + + describe('downloadLambdaFunctionCode', function () { + it('uses physical ID from CloudFormation when not provided', async function () { + const targetDir = vscode.Uri.file(tempDir) + const resourceName = 'testResource' + const stackInfo = { stackId: 'stack-id', stackName: 'test-stack', isSamTemplate: false, template: {} } + + lambdaClientStub.getFunction.resolves({ + Code: { Location: 'https://lambda-function-code.zip' }, + }) + + await lambda2sam.downloadLambdaFunctionCode(resourceName, stackInfo, targetDir, 'us-west-2') + + // Verify CloudFormation was called to get physical ID + assert.strictEqual(cfnClientStub.describeStackResource.calledOnce, true) + + // Verify Lambda client was called with correct physical ID + assert.strictEqual(lambdaClientStub.getFunction.calledOnce, true) + assert.strictEqual(lambdaClientStub.getFunction.firstCall.args[0], 'test-physical-id') + + // Verify downloadUnzip was called with correct parameters + assert.strictEqual(downloadUnzipStub.calledOnce, true) + assert.strictEqual(downloadUnzipStub.firstCall.args[0], 'https://lambda-function-code.zip') + assert.strictEqual( + downloadUnzipStub.firstCall.args[1].fsPath, + vscode.Uri.joinPath(targetDir, resourceName).fsPath + ) + + // Verify files were actually created in the temp directory + const outputDir = vscode.Uri.joinPath(targetDir, resourceName) + assert.strictEqual(await fs.exists(outputDir), true) + assert.strictEqual(await fs.exists(vscode.Uri.joinPath(outputDir, 'index.js')), true) + assert.strictEqual(await fs.exists(vscode.Uri.joinPath(outputDir, 'package.json')), true) + }) + + it('uses provided physical ID when available', async function () { + const targetDir = vscode.Uri.file(tempDir) + const resourceName = 'testResource' + const physicalResourceId = 'provided-physical-id' + const stackInfo = { stackId: 'stack-id', stackName: 'test-stack', isSamTemplate: false, template: {} } + + lambdaClientStub.getFunction.resolves({ + Code: { Location: 'https://lambda-function-code.zip' }, + }) + + await lambda2sam.downloadLambdaFunctionCode( + resourceName, + stackInfo, + targetDir, + 'us-west-2', + physicalResourceId + ) + + // Verify CloudFormation was NOT called to get physical ID + assert.strictEqual(cfnClientStub.describeStackResource.called, false) + + // Verify Lambda client was called with provided physical ID + assert.strictEqual(lambdaClientStub.getFunction.calledOnce, true) + assert.strictEqual(lambdaClientStub.getFunction.firstCall.args[0], physicalResourceId) + + // Verify files were actually created in the temp directory + const outputDir = vscode.Uri.joinPath(targetDir, resourceName) + assert.strictEqual(await fs.exists(outputDir), true) + assert.strictEqual(await fs.exists(vscode.Uri.joinPath(outputDir, 'index.js')), true) + assert.strictEqual(await fs.exists(vscode.Uri.joinPath(outputDir, 'package.json')), true) + }) + + it('throws an error when code location is missing', async function () { + const targetDir = vscode.Uri.file(tempDir) + const resourceName = 'testResource' + const stackInfo = { stackId: 'stack-id', stackName: 'test-stack', isSamTemplate: false, template: {} } + + lambdaClientStub.getFunction.resolves({ + Code: {}, // No Location + }) + + await assert.rejects( + lambda2sam.downloadLambdaFunctionCode(resourceName, stackInfo, targetDir, 'us-west-2'), + /Could not determine code location/ + ) + }) + }) + + describe('downloadLayerVersionResourceByName', function () { + it('extracts layer name and version from ARN and downloads content', async function () { + const targetDir = vscode.Uri.file(tempDir) + const resourceName = 'testLayer' + const stackInfo = { stackId: 'stack-id', stackName: 'test-stack', isSamTemplate: false, template: {} } + + // Return an ARN for a layer version + cfnClientStub.describeStackResource.resolves({ + StackResourceDetail: { + PhysicalResourceId: 'arn:aws:lambda:us-west-2:123456789012:layer:my-layer:1', + }, + }) + + lambdaClientStub.getLayerVersion.resolves({ + Content: { Location: 'https://lambda-layer-code.zip' }, + }) + + await lambda2sam.downloadLayerVersionResourceByName(resourceName, stackInfo, targetDir, 'us-west-2') + + // Verify Lambda client was called with correct layer name and version + assert.strictEqual(lambdaClientStub.getLayerVersion.calledOnce, true) + assert.strictEqual(lambdaClientStub.getLayerVersion.firstCall.args[0], 'my-layer') + assert.strictEqual(lambdaClientStub.getLayerVersion.firstCall.args[1], 1) + + // Verify downloadUnzip was called with correct parameters + assert.strictEqual(downloadUnzipStub.calledOnce, true) + assert.strictEqual(downloadUnzipStub.firstCall.args[0], 'https://lambda-layer-code.zip') + assert.strictEqual( + downloadUnzipStub.firstCall.args[1].fsPath, + vscode.Uri.joinPath(targetDir, resourceName).fsPath + ) + + // Verify files were actually created in the temp directory + const outputDir = vscode.Uri.joinPath(targetDir, resourceName) + assert.strictEqual(await fs.exists(outputDir), true) + assert.strictEqual(await fs.exists(vscode.Uri.joinPath(outputDir, 'index.js')), true) + assert.strictEqual(await fs.exists(vscode.Uri.joinPath(outputDir, 'package.json')), true) + }) + + it('throws an error when ARN format is invalid', async function () { + const targetDir = vscode.Uri.file(tempDir) + const resourceName = 'testLayer' + const stackInfo = { stackId: 'stack-id', stackName: 'test-stack', isSamTemplate: false, template: {} } + + // Return an invalid ARN + cfnClientStub.describeStackResource.resolves({ + StackResourceDetail: { + PhysicalResourceId: 'arn:aws:lambda:us-west-2:123456789012:layer:my-layer', // Missing version + }, + }) + + await assert.rejects( + lambda2sam.downloadLayerVersionResourceByName(resourceName, stackInfo, targetDir, 'us-west-2'), + /Invalid layer ARN format/ + ) + }) + + it('throws an error when layer content location is missing', async function () { + const targetDir = vscode.Uri.file(tempDir) + const resourceName = 'testLayer' + const stackInfo = { stackId: 'stack-id', stackName: 'test-stack', isSamTemplate: false, template: {} } + + // Return an ARN for a layer version + cfnClientStub.describeStackResource.resolves({ + StackResourceDetail: { + PhysicalResourceId: 'arn:aws:lambda:us-west-2:123456789012:layer:my-layer:1', + }, + }) + + lambdaClientStub.getLayerVersion.resolves({ + Content: {}, // No Location + }) + + await assert.rejects( + lambda2sam.downloadLayerVersionResourceByName(resourceName, stackInfo, targetDir, 'us-west-2'), + /Could not determine code location for layer/ + ) + }) + }) +}) diff --git a/packages/core/src/test/awsService/appBuilder/utils.test.ts b/packages/core/src/test/awsService/appBuilder/utils.test.ts index d74cfc77802..eaaa69254d7 100644 --- a/packages/core/src/test/awsService/appBuilder/utils.test.ts +++ b/packages/core/src/test/awsService/appBuilder/utils.test.ts @@ -12,9 +12,20 @@ import fs from '../../../shared/fs/fs' import { ResourceNode } from '../../../awsService/appBuilder/explorer/nodes/resourceNode' import path from 'path' import { SERVERLESS_FUNCTION_TYPE } from '../../../shared/cloudformation/cloudformation' -import { runOpenHandler, runOpenTemplate } from '../../../awsService/appBuilder/utils' +import { + runOpenHandler, + runOpenTemplate, + isPermissionError, + EnhancedLambdaClient, + EnhancedCloudFormationClient, + getLambdaClient, + getCFNClient, +} from '../../../awsService/appBuilder/utils' import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider' import { assertTextEditorContains } from '../../testUtil' +import { DefaultLambdaClient } from '../../../shared/clients/lambdaClient' +import { ToolkitError } from '../../../shared/errors' +import globals from '../../../shared/extensionGlobals' interface TestScenario { runtime: string @@ -303,4 +314,553 @@ describe('AppBuilder Utils', function () { assert(showCommand.notCalled) }) }) + + describe('Permission Error Handling', function () { + let sandbox: sinon.SinonSandbox + + beforeEach(function () { + sandbox = sinon.createSandbox() + }) + + afterEach(function () { + sandbox.restore() + }) + + describe('isPermissionError', function () { + it('should return true for AccessDeniedException', function () { + const error = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + assert.strictEqual(isPermissionError(error), true) + }) + + it('should return true for UnauthorizedOperation', function () { + const error = Object.assign(new Error('Unauthorized'), { + code: 'UnauthorizedOperation', + time: new Date(), + statusCode: 403, + }) + assert.strictEqual(isPermissionError(error), true) + }) + + it('should return true for Forbidden', function () { + const error = Object.assign(new Error('Forbidden'), { + code: 'Forbidden', + time: new Date(), + statusCode: 403, + }) + assert.strictEqual(isPermissionError(error), true) + }) + + it('should return true for AccessDenied', function () { + const error = Object.assign(new Error('Access denied'), { + code: 'AccessDenied', + time: new Date(), + statusCode: 403, + }) + assert.strictEqual(isPermissionError(error), true) + }) + + it('should return true for 403 status code', function () { + const error = Object.assign(new Error('Forbidden'), { + code: 'SomeError', + statusCode: 403, + time: new Date(), + }) + assert.strictEqual(isPermissionError(error), true) + }) + + it('should return false for non-permission errors', function () { + const error = Object.assign(new Error('Resource not found'), { + code: 'ResourceNotFoundException', + time: new Date(), + statusCode: 404, + }) + assert.strictEqual(isPermissionError(error), false) + }) + + it('should return false for non-AWS errors', function () { + const error = new Error('Regular error') + assert.strictEqual(isPermissionError(error), false) + }) + + it('should return false for undefined', function () { + assert.strictEqual(isPermissionError(undefined), false) + }) + }) + + describe('EnhancedLambdaClient', function () { + let mockLambdaClient: sinon.SinonStubbedInstance + let enhancedClient: EnhancedLambdaClient + + beforeEach(function () { + mockLambdaClient = sandbox.createStubInstance(DefaultLambdaClient) + // Add missing properties that EnhancedLambdaClient expects + Object.defineProperty(mockLambdaClient, 'defaultTimeoutInMs', { + value: 5 * 60 * 1000, + configurable: true, + }) + Object.defineProperty(mockLambdaClient, 'createSdkClient', { + value: sandbox.stub().resolves({}), + configurable: true, + }) + enhancedClient = new EnhancedLambdaClient(mockLambdaClient as any, 'us-east-1') + }) + + it('should enhance permission errors for getFunction', async function () { + const permissionError = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + mockLambdaClient.getFunction.rejects(permissionError) + + try { + await enhancedClient.getFunction('test-function') + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + assert( + error.message.includes('Permission denied: Missing required permissions for lambda:getFunction') + ) + assert(error.message.includes('lambda:GetFunction')) + assert(error.message.includes('arn:aws:lambda:us-east-1:*:function:test-function')) + assert(error.message.includes('To fix this issue:')) + assert(error.message.includes('Documentation:')) + } + }) + + it('should pass through non-permission errors for getFunction', async function () { + const nonPermissionError = new Error('Function not found') + mockLambdaClient.getFunction.rejects(nonPermissionError) + + try { + await enhancedClient.getFunction('test-function') + assert.fail('Expected error to be thrown') + } catch (error) { + assert.strictEqual(error, nonPermissionError) + } + }) + + it('should enhance permission errors for listFunctions', async function () { + const permissionError = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + + // Create a mock async generator that throws the error + const mockAsyncGenerator = async function* (): AsyncIterableIterator { + throw permissionError + yield // This line will never be reached but satisfies ESLint require-yield rule + } + mockLambdaClient.listFunctions.returns(mockAsyncGenerator()) + + try { + const iterator = enhancedClient.listFunctions() + await iterator.next() + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + assert( + error.message.includes( + 'Permission denied: Missing required permissions for lambda:listFunctions' + ) + ) + assert(error.message.includes('lambda:ListFunctions')) + } + }) + + it('should enhance permission errors for deleteFunction', async function () { + const permissionError = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + mockLambdaClient.deleteFunction.rejects(permissionError) + + try { + await enhancedClient.deleteFunction('test-function') + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + assert( + error.message.includes( + 'Permission denied: Missing required permissions for lambda:deleteFunction' + ) + ) + assert(error.message.includes('lambda:DeleteFunction')) + assert(error.message.includes('arn:aws:lambda:us-east-1:*:function:test-function')) + } + }) + + it('should enhance permission errors for invoke', async function () { + const permissionError = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + mockLambdaClient.invoke.rejects(permissionError) + + try { + await enhancedClient.invoke('test-function', '{}') + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + assert(error.message.includes('Permission denied: Missing required permissions for lambda:invoke')) + assert(error.message.includes('lambda:InvokeFunction')) + assert(error.message.includes('arn:aws:lambda:us-east-1:*:function:test-function')) + } + }) + + it('should enhance permission errors for getLayerVersion', async function () { + const permissionError = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + mockLambdaClient.getLayerVersion.rejects(permissionError) + + try { + await enhancedClient.getLayerVersion('test-layer', 1) + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + assert( + error.message.includes( + 'Permission denied: Missing required permissions for lambda:getLayerVersion' + ) + ) + assert(error.message.includes('lambda:GetLayerVersion')) + assert(error.message.includes('arn:aws:lambda:us-east-1:*:layer:test-layer:1')) + } + }) + + it('should enhance permission errors for updateFunctionCode', async function () { + const permissionError = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + mockLambdaClient.updateFunctionCode.rejects(permissionError) + + try { + await enhancedClient.updateFunctionCode('test-function', new Uint8Array()) + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + assert( + error.message.includes( + 'Permission denied: Missing required permissions for lambda:updateFunctionCode' + ) + ) + assert(error.message.includes('lambda:UpdateFunctionCode')) + assert(error.message.includes('arn:aws:lambda:us-east-1:*:function:test-function')) + } + }) + + it('should return successful results when no errors occur', async function () { + const mockResponse = { Configuration: { FunctionName: 'test-function' } } + mockLambdaClient.getFunction.resolves(mockResponse) + + const result = await enhancedClient.getFunction('test-function') + assert.strictEqual(result, mockResponse) + }) + }) + + describe('EnhancedCloudFormationClient', function () { + let mockCfnClient: any + let enhancedClient: EnhancedCloudFormationClient + + beforeEach(function () { + // Create a mock CloudFormation client with all required methods + mockCfnClient = { + describeStacks: sandbox.stub(), + getTemplate: sandbox.stub(), + createChangeSet: sandbox.stub(), + describeStackResource: sandbox.stub(), + describeStackResources: sandbox.stub(), + } + enhancedClient = new EnhancedCloudFormationClient(mockCfnClient, 'us-east-1') + }) + + it('should enhance permission errors for describeStacks', async function () { + const permissionError = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + mockCfnClient.describeStacks.returns({ + promise: sandbox.stub().rejects(permissionError), + } as any) + + try { + await enhancedClient.describeStacks({ StackName: 'test-stack' }) + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + assert( + error.message.includes( + 'Permission denied: Missing required permissions for cloudformation:describeStacks' + ) + ) + assert(error.message.includes('cloudformation:DescribeStacks')) + assert(error.message.includes('arn:aws:cloudformation:us-east-1:*:stack/test-stack/*')) + assert(error.message.includes('To fix this issue:')) + assert(error.message.includes('Documentation:')) + } + }) + + it('should enhance permission errors for getTemplate', async function () { + const permissionError = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + mockCfnClient.getTemplate.returns({ + promise: sandbox.stub().rejects(permissionError), + } as any) + + try { + await enhancedClient.getTemplate({ StackName: 'test-stack' }) + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + assert( + error.message.includes( + 'Permission denied: Missing required permissions for cloudformation:getTemplate' + ) + ) + assert(error.message.includes('cloudformation:GetTemplate')) + assert(error.message.includes('arn:aws:cloudformation:us-east-1:*:stack/test-stack/*')) + } + }) + + it('should enhance permission errors for createChangeSet', async function () { + const permissionError = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + mockCfnClient.createChangeSet.returns({ + promise: sandbox.stub().rejects(permissionError), + } as any) + + try { + await enhancedClient.createChangeSet({ + StackName: 'test-stack', + ChangeSetName: 'test-changeset', + TemplateBody: '{}', + }) + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + assert( + error.message.includes( + 'Permission denied: Missing required permissions for cloudformation:createChangeSet' + ) + ) + assert(error.message.includes('cloudformation:CreateChangeSet')) + assert(error.message.includes('arn:aws:cloudformation:us-east-1:*:stack/test-stack/*')) + } + }) + + it('should enhance permission errors for describeStackResource', async function () { + const permissionError = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + mockCfnClient.describeStackResource.returns({ + promise: sandbox.stub().rejects(permissionError), + } as any) + + try { + await enhancedClient.describeStackResource({ + StackName: 'test-stack', + LogicalResourceId: 'TestResource', + }) + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + assert( + error.message.includes( + 'Permission denied: Missing required permissions for cloudformation:describeStackResource' + ) + ) + assert(error.message.includes('cloudformation:DescribeStackResource')) + assert(error.message.includes('arn:aws:cloudformation:us-east-1:*:stack/test-stack/*')) + } + }) + + it('should enhance permission errors for describeStackResources', async function () { + const permissionError = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + mockCfnClient.describeStackResources.returns({ + promise: sandbox.stub().rejects(permissionError), + } as any) + + try { + await enhancedClient.describeStackResources({ StackName: 'test-stack' }) + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + assert( + error.message.includes( + 'Permission denied: Missing required permissions for cloudformation:describeStackResources' + ) + ) + assert(error.message.includes('cloudformation:DescribeStackResources')) + assert(error.message.includes('arn:aws:cloudformation:us-east-1:*:stack/test-stack/*')) + } + }) + + it('should pass through non-permission errors', async function () { + const nonPermissionError = new Error('Stack not found') + mockCfnClient.describeStacks.returns({ + promise: sandbox.stub().rejects(nonPermissionError), + } as any) + + try { + await enhancedClient.describeStacks({ StackName: 'test-stack' }) + assert.fail('Expected error to be thrown') + } catch (error) { + assert.strictEqual(error, nonPermissionError) + } + }) + + it('should return successful results when no errors occur', async function () { + const mockResponse = { Stacks: [{ StackName: 'test-stack' }] } + mockCfnClient.describeStacks.returns({ + promise: sandbox.stub().resolves(mockResponse), + } as any) + + const result = await enhancedClient.describeStacks({ StackName: 'test-stack' }) + assert.strictEqual(result, mockResponse) + }) + }) + + describe('Client Factory Functions', function () { + beforeEach(function () { + // Stub the global SDK client builder + sandbox.stub(globals.sdkClientBuilder, 'createAwsService').resolves({} as any) + }) + + it('should return EnhancedLambdaClient from getLambdaClient', function () { + const client = getLambdaClient('us-east-1') + assert(client instanceof EnhancedLambdaClient) + }) + + it('should return EnhancedCloudFormationClient from getCFNClient', async function () { + const client = await getCFNClient('us-east-1') + assert(client instanceof EnhancedCloudFormationClient) + }) + }) + + describe('Error Message Content', function () { + let mockLambdaClient: sinon.SinonStubbedInstance + let enhancedClient: EnhancedLambdaClient + + beforeEach(function () { + mockLambdaClient = sandbox.createStubInstance(DefaultLambdaClient) + // Add missing properties that EnhancedLambdaClient expects + Object.defineProperty(mockLambdaClient, 'defaultTimeoutInMs', { + value: 5 * 60 * 1000, + configurable: true, + }) + Object.defineProperty(mockLambdaClient, 'createSdkClient', { + value: sandbox.stub().resolves({}), + configurable: true, + }) + enhancedClient = new EnhancedLambdaClient(mockLambdaClient as any, 'us-west-2') + }) + + it('should include all required elements in enhanced error message', async function () { + const permissionError = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + mockLambdaClient.getFunction.rejects(permissionError) + + try { + await enhancedClient.getFunction('my-test-function') + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + + // Check that the error message contains all expected elements + const message = error.message + + // Main error description + assert(message.includes('Permission denied: Missing required permissions for lambda:getFunction')) + + // Required permissions section + assert(message.includes('Required permissions:')) + assert(message.includes('- lambda:GetFunction')) + + // Resource ARN + assert(message.includes('Resource: arn:aws:lambda:us-west-2:*:function:my-test-function')) + + // Instructions + assert(message.includes('To fix this issue:')) + assert(message.includes('1. Contact your AWS administrator')) + assert(message.includes('2. Add these permissions to your IAM user/role policy')) + assert(message.includes('3. If using IAM roles, ensure the role has these permissions attached')) + + // Documentation link + assert( + message.includes( + 'Documentation: https://docs.aws.amazon.com/lambda/latest/api/API_GetFunction.html' + ) + ) + + // Check error details + assert.strictEqual(error.code, 'InsufficientPermissions') + assert(error.details) + assert.strictEqual(error.details.service, 'lambda') + assert.strictEqual(error.details.action, 'getFunction') + assert.deepStrictEqual(error.details.requiredPermissions, ['lambda:GetFunction']) + assert.strictEqual( + error.details.resourceArn, + 'arn:aws:lambda:us-west-2:*:function:my-test-function' + ) + } + }) + + it('should handle errors without resource ARN', async function () { + const permissionError = Object.assign(new Error('Access denied'), { + code: 'AccessDeniedException', + time: new Date(), + statusCode: 403, + }) + + // Create a mock async generator that throws the error + const mockAsyncGenerator = async function* (): AsyncIterableIterator { + throw permissionError + yield // This line will never be reached but satisfies ESLint require-yield rule + } + mockLambdaClient.listFunctions.returns(mockAsyncGenerator()) + + try { + const iterator = enhancedClient.listFunctions() + await iterator.next() + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + + const message = error.message + assert(message.includes('Permission denied: Missing required permissions for lambda:listFunctions')) + assert(message.includes('- lambda:ListFunctions')) + // Should not include Resource line for operations without specific resources + assert(!message.includes('Resource: arn:')) + } + }) + }) + }) }) diff --git a/packages/core/src/test/lambda/commands/editLambda.test.ts b/packages/core/src/test/lambda/commands/editLambda.test.ts new file mode 100644 index 00000000000..9c38f767885 --- /dev/null +++ b/packages/core/src/test/lambda/commands/editLambda.test.ts @@ -0,0 +1,251 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import assert from 'assert' +import * as sinon from 'sinon' +import { + editLambda, + watchForUpdates, + promptForSync, + deployFromTemp, + openLambdaFolderForEdit, +} from '../../../lambda/commands/editLambda' +import { LambdaFunction } from '../../../lambda/commands/uploadLambda' +import * as downloadLambda from '../../../lambda/commands/downloadLambda' +import * as uploadLambda from '../../../lambda/commands/uploadLambda' +import * as utils from '../../../lambda/utils' +import * as messages from '../../../shared/utilities/messages' +import fs from '../../../shared/fs/fs' +import { LambdaFunctionNodeDecorationProvider } from '../../../lambda/explorer/lambdaFunctionNodeDecorationProvider' +import path from 'path' + +describe('editLambda', function () { + let mockLambda: LambdaFunction + let mockTemp: string + let mockUri: vscode.Uri + + // Stub variables + let getFunctionInfoStub: sinon.SinonStub + let setFunctionInfoStub: sinon.SinonStub + let compareCodeShaStub: sinon.SinonStub + let downloadLambdaStub: sinon.SinonStub + let openLambdaFileStub: sinon.SinonStub + let runUploadDirectoryStub: sinon.SinonStub + let showConfirmationMessageStub: sinon.SinonStub + let createFileSystemWatcherStub: sinon.SinonStub + let executeCommandStub: sinon.SinonStub + let existsDirStub: sinon.SinonStub + let mkdirStub: sinon.SinonStub + let promptDeployStub: sinon.SinonStub + + beforeEach(function () { + mockLambda = { + name: 'test-function', + region: 'us-east-1', + configuration: { + FunctionName: 'test-function', + CodeSha256: 'test-sha', + Runtime: 'nodejs18.x', + }, + } + mockTemp = utils.getTempLocation(mockLambda.name, mockLambda.region) + mockUri = vscode.Uri.file(mockTemp) + + // Create stubs + getFunctionInfoStub = sinon.stub(utils, 'getFunctionInfo').resolves(undefined) + setFunctionInfoStub = sinon.stub(utils, 'setFunctionInfo').resolves() + compareCodeShaStub = sinon.stub(utils, 'compareCodeSha').resolves(true) + downloadLambdaStub = sinon.stub(downloadLambda, 'downloadLambdaInLocation').resolves() + openLambdaFileStub = sinon.stub(downloadLambda, 'openLambdaFile').resolves() + runUploadDirectoryStub = sinon.stub(uploadLambda, 'runUploadDirectory').resolves() + showConfirmationMessageStub = sinon.stub(messages, 'showConfirmationMessage').resolves(true) + createFileSystemWatcherStub = sinon.stub(vscode.workspace, 'createFileSystemWatcher').returns({ + onDidChange: sinon.stub(), + onDidCreate: sinon.stub(), + onDidDelete: sinon.stub(), + dispose: sinon.stub(), + } as any) + executeCommandStub = sinon.stub(vscode.commands, 'executeCommand').resolves() + existsDirStub = sinon.stub(fs, 'existsDir').resolves(true) + mkdirStub = sinon.stub(fs, 'mkdir').resolves() + promptDeployStub = sinon.stub().resolves(true) + sinon.replace(require('../../../lambda/commands/editLambda'), 'promptDeploy', promptDeployStub) + + // Other stubs + sinon.stub(utils, 'lambdaEdits').value([]) + sinon.stub(utils, 'getLambdaDetails').returns({ fileName: 'index.js', functionName: 'test-function' }) + sinon.stub(fs, 'readdir').resolves([]) + sinon.stub(fs, 'delete').resolves() + sinon.stub(fs, 'stat').resolves({ ctime: Date.now() } as any) + sinon.stub(vscode.workspace, 'saveAll').resolves(true) + sinon.stub(LambdaFunctionNodeDecorationProvider.prototype, 'addBadge').resolves() + sinon.stub(LambdaFunctionNodeDecorationProvider.prototype, 'removeBadge').resolves() + sinon.stub(LambdaFunctionNodeDecorationProvider, 'getInstance').returns({ + addBadge: sinon.stub().resolves(), + removeBadge: sinon.stub().resolves(), + } as any) + }) + + afterEach(function () { + sinon.restore() + }) + + describe('editLambda', function () { + it('returns early if folder already exists in workspace', async function () { + sinon.stub(vscode.workspace, 'workspaceFolders').value([{ uri: vscode.Uri.file(mockTemp) }]) + + const result = await editLambda(mockLambda) + + assert.strictEqual(result, mockTemp) + }) + + it('downloads lambda when no local code exists', async function () { + await editLambda(mockLambda) + + assert(downloadLambdaStub.calledOnce) + }) + + it('prompts for overwrite when local code differs from remote', async function () { + getFunctionInfoStub.resolves('old-sha') + compareCodeShaStub.resolves(false) + + await editLambda(mockLambda) + + assert(showConfirmationMessageStub.calledOnce) + }) + + it('opens existing file when user declines overwrite', async function () { + getFunctionInfoStub.resolves('old-sha') + compareCodeShaStub.resolves(false) + showConfirmationMessageStub.resolves(false) + + await editLambda(mockLambda) + + assert(openLambdaFileStub.calledOnce) + }) + + it('sets up file watcher after download', async function () { + const watcherStub = { + onDidChange: sinon.stub(), + onDidCreate: sinon.stub(), + onDidDelete: sinon.stub(), + } + createFileSystemWatcherStub.returns(watcherStub) + + await editLambda(mockLambda) + + assert(watcherStub.onDidChange.calledOnce) + assert(watcherStub.onDidCreate.calledOnce) + assert(watcherStub.onDidDelete.calledOnce) + }) + }) + + describe('watchForUpdates', function () { + it('creates file system watcher with correct pattern', function () { + const watcher = { + onDidChange: sinon.stub(), + onDidCreate: sinon.stub(), + onDidDelete: sinon.stub(), + } + createFileSystemWatcherStub.returns(watcher) + + watchForUpdates(mockLambda, mockUri) + + assert(createFileSystemWatcherStub.calledOnce) + const pattern = createFileSystemWatcherStub.firstCall.args[0] + assert(pattern instanceof vscode.RelativePattern) + }) + + it('sets up change, create, and delete handlers', function () { + const watcher = { + onDidChange: sinon.stub(), + onDidCreate: sinon.stub(), + onDidDelete: sinon.stub(), + } + createFileSystemWatcherStub.returns(watcher) + + watchForUpdates(mockLambda, mockUri) + + assert(watcher.onDidChange.calledOnce) + assert(watcher.onDidCreate.calledOnce) + assert(watcher.onDidDelete.calledOnce) + }) + }) + + describe('promptForSync', function () { + it('returns early if directory does not exist', async function () { + existsDirStub.resolves(false) + + await promptForSync(mockLambda, mockUri, vscode.Uri.file('/test/file.js')) + + assert(setFunctionInfoStub.notCalled) + }) + }) + + describe('deployFromTemp', function () { + it('uploads without confirmation when code is up to date', async function () { + await deployFromTemp(mockLambda, mockUri) + + assert(showConfirmationMessageStub.notCalled) + assert(runUploadDirectoryStub.calledOnce) + }) + + it('prompts for confirmation when code is outdated', async function () { + compareCodeShaStub.resolves(false) + + await deployFromTemp(mockLambda, mockUri) + + assert(showConfirmationMessageStub.calledOnce) + }) + + it('does not upload when user declines overwrite', async function () { + compareCodeShaStub.resolves(false) + showConfirmationMessageStub.resolves(false) + + await deployFromTemp(mockLambda, mockUri) + + assert(runUploadDirectoryStub.notCalled) + }) + + it('updates function info after successful upload', async function () { + await deployFromTemp(mockLambda, mockUri) + + assert(runUploadDirectoryStub.calledOnce) + assert( + setFunctionInfoStub.calledWith(mockLambda, { + lastDeployed: sinon.match.number, + undeployed: false, + }) + ) + }) + }) + + describe('openLambdaFolderForEdit', function () { + it('focuses existing workspace folder if already open', async function () { + const subfolderPath = path.normalize(path.join(mockTemp, 'subfolder')) + sinon.stub(vscode.workspace, 'workspaceFolders').value([{ uri: vscode.Uri.file(subfolderPath) }]) + + await openLambdaFolderForEdit('test-function', 'us-east-1') + + assert(executeCommandStub.calledWith('workbench.action.focusSideBar')) + assert(executeCommandStub.calledWith('workbench.view.explorer')) + }) + + it('opens new folder when not in workspace', async function () { + sinon.stub(vscode.workspace, 'workspaceFolders').value([]) + + await openLambdaFolderForEdit('test-function', 'us-east-1') + + assert(mkdirStub.calledOnce) + assert( + executeCommandStub.calledWith('vscode.openFolder', sinon.match.any, { + newWindow: true, + noRecentEntry: true, + }) + ) + }) + }) +}) diff --git a/packages/core/src/test/lambda/explorer/lambdaFunctionFileNode.test.ts b/packages/core/src/test/lambda/explorer/lambdaFunctionFileNode.test.ts new file mode 100644 index 00000000000..59235bf7558 --- /dev/null +++ b/packages/core/src/test/lambda/explorer/lambdaFunctionFileNode.test.ts @@ -0,0 +1,58 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { LambdaFunctionNode } from '../../../lambda/explorer/lambdaFunctionNode' +import { TestAWSTreeNode } from '../../shared/treeview/nodes/testAWSTreeNode' +import { LambdaFunctionFileNode } from '../../../lambda/explorer/lambdaFunctionFileNode' +import path from 'path' + +describe('LambdaFunctionFileNode', function () { + const fakeFunctionConfig = { + FunctionName: 'testFunctionName', + FunctionArn: 'testFunctionARN', + } + const fakeFilename = 'fakeFile' + const fakeRegion = 'fakeRegion' + const functionNode = new LambdaFunctionNode(new TestAWSTreeNode('test node'), fakeRegion, fakeFunctionConfig) + const filePath = path.join( + '/tmp/aws-toolkit-vscode/lambda', + fakeRegion, + fakeFunctionConfig.FunctionName, + fakeFilename + ) + + let testNode: LambdaFunctionFileNode + + before(async function () { + testNode = new LambdaFunctionFileNode(functionNode, fakeFilename, filePath) + }) + + it('instantiates without issue', function () { + assert.ok(testNode) + }) + + it('initializes the parent node', function () { + assert.equal(testNode.parent, functionNode, 'unexpected parent node') + }) + + it('initializes the label', function () { + assert.equal(testNode.label, fakeFilename) + }) + + it('has no children', async function () { + const childNodes = await testNode.getChildren() + assert.ok(childNodes) + assert.strictEqual(childNodes.length, 0, 'Expected zero children') + }) + + it('has correct command', function () { + assert.deepStrictEqual(testNode.command, { + command: 'aws.openLambdaFile', + title: 'Open file', + arguments: [filePath], + }) + }) +}) diff --git a/packages/core/src/test/lambda/explorer/lambdaFunctionFolderNode.test.ts b/packages/core/src/test/lambda/explorer/lambdaFunctionFolderNode.test.ts new file mode 100644 index 00000000000..67471bc4e24 --- /dev/null +++ b/packages/core/src/test/lambda/explorer/lambdaFunctionFolderNode.test.ts @@ -0,0 +1,57 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { LambdaFunctionNode } from '../../../lambda/explorer/lambdaFunctionNode' +import { TestAWSTreeNode } from '../../shared/treeview/nodes/testAWSTreeNode' +import path from 'path' +import { LambdaFunctionFolderNode } from '../../../lambda/explorer/lambdaFunctionFolderNode' +import { fs } from '../../../shared/fs/fs' + +describe('LambdaFunctionFileNode', function () { + const fakeFunctionConfig = { + FunctionName: 'testFunctionName', + FunctionArn: 'testFunctionARN', + } + const fakeRegion = 'fakeRegion' + const fakeSubFolder = 'fakeSubFolder' + const fakeFile = 'fakeFilename' + const functionNode = new LambdaFunctionNode(new TestAWSTreeNode('test node'), fakeRegion, fakeFunctionConfig) + + const regionPath = path.join('/tmp/aws-toolkit-vscode/lambda', fakeRegion) + const functionPath = path.join(regionPath, fakeFunctionConfig.FunctionName) + const subFolderPath = path.join(functionPath, fakeSubFolder) + + let testNode: LambdaFunctionFolderNode + + before(async function () { + await fs.mkdir(subFolderPath) + await fs.writeFile(path.join(subFolderPath, fakeFile), 'fakefilecontent') + + testNode = new LambdaFunctionFolderNode(functionNode, fakeSubFolder, subFolderPath) + }) + + after(async function () { + await fs.delete(regionPath, { recursive: true }) + }) + + it('instantiates without issue', function () { + assert.ok(testNode) + }) + + it('initializes the parent node', function () { + assert.equal(testNode.parent, functionNode, 'unexpected parent node') + }) + + it('initializes the label', function () { + assert.equal(testNode.label, fakeSubFolder) + }) + + it('loads function files', async function () { + const functionFiles = await testNode.loadFunctionFiles() + assert.equal(functionFiles.length, 1) + assert.equal(functionFiles[0].label, fakeFile) + }) +}) diff --git a/packages/core/src/test/lambda/explorer/lambdaFunctionNode.test.ts b/packages/core/src/test/lambda/explorer/lambdaFunctionNode.test.ts index cb9ffc9b5f2..184bdd915b8 100644 --- a/packages/core/src/test/lambda/explorer/lambdaFunctionNode.test.ts +++ b/packages/core/src/test/lambda/explorer/lambdaFunctionNode.test.ts @@ -4,23 +4,54 @@ */ import assert from 'assert' -import { Lambda } from 'aws-sdk' import * as os from 'os' import { LambdaFunctionNode } from '../../../lambda/explorer/lambdaFunctionNode' import { TestAWSTreeNode } from '../../shared/treeview/nodes/testAWSTreeNode' +import path from 'path' +import { fs } from '../../../shared/fs/fs' +import { + contextValueLambdaFunction, + contextValueLambdaFunctionImportable, +} from '../../../lambda/explorer/lambdaFunctionNode' +import sinon from 'sinon' +import * as editLambdaModule from '../../../lambda/commands/editLambda' describe('LambdaFunctionNode', function () { const parentNode = new TestAWSTreeNode('test node') + const fakeRegion = 'fakeRegion' + const fakeFilename = 'fakeFilename' + + const fakeFunctionConfig = { + FunctionName: 'testFunctionName', + FunctionArn: 'testFunctionARN', + } + + const regionPath = path.join('/tmp/aws-toolkit-vscode/lambda', fakeRegion) + const functionPath = path.join(regionPath, fakeFunctionConfig.FunctionName) + const filePath = path.join(functionPath, fakeFilename) + let testNode: LambdaFunctionNode - let fakeFunctionConfig: Lambda.FunctionConfiguration - before(function () { - fakeFunctionConfig = { - FunctionName: 'testFunctionName', - FunctionArn: 'testFunctionARN', - } + let editLambdaStub: sinon.SinonStub + + before(async function () { + await fs.mkdir(functionPath) + await fs.writeFile(filePath, 'fakefilecontent') + + // Stub the editLambdaCommand to return the function path + editLambdaStub = sinon.stub(editLambdaModule, 'editLambdaCommand').resolves(functionPath) + + testNode = new LambdaFunctionNode( + parentNode, + 'someregioncode', + fakeFunctionConfig, + contextValueLambdaFunctionImportable + ) + }) - testNode = new LambdaFunctionNode(parentNode, 'someregioncode', fakeFunctionConfig) + after(async function () { + await fs.delete(regionPath, { recursive: true }) + editLambdaStub.restore() }) it('instantiates without issue', async function () { @@ -43,6 +74,11 @@ describe('LambdaFunctionNode', function () { assert.strictEqual(testNode.functionName, fakeFunctionConfig.FunctionName) }) + it('initializes resourceUri', async function () { + assert.strictEqual(testNode.resourceUri?.scheme, 'lambda') + assert.strictEqual(testNode.resourceUri?.path, `someregioncode/${fakeFunctionConfig.FunctionName}`) + }) + it('initializes the tooltip', async function () { assert.strictEqual( testNode.tooltip, @@ -50,9 +86,27 @@ describe('LambdaFunctionNode', function () { ) }) - it('has no children', async function () { + it('loads function files', async function () { + const functionFiles = await testNode.loadFunctionFiles(functionPath) + assert.equal(functionFiles.length, 1) + assert.equal(functionFiles[0].label, fakeFilename) + }) + + it('has child if importable', async function () { const childNodes = await testNode.getChildren() assert.ok(childNodes) - assert.strictEqual(childNodes.length, 0, 'Expected node to have no children') + assert.equal(childNodes.length, 1, 'Expected node to have one child, should be "failed to load resources"') + }) + + it('is not collapsible if not importable', async function () { + const nonImportableNode = new LambdaFunctionNode( + parentNode, + fakeRegion, + fakeFunctionConfig, + contextValueLambdaFunction + ) + const childNodes = await nonImportableNode.getChildren() + assert.ok(childNodes) + assert.equal(childNodes.length, 0, 'Expected node to have no children') }) }) diff --git a/packages/core/src/test/lambda/explorer/lambdaFunctionNodeDecorationProvider.test.ts b/packages/core/src/test/lambda/explorer/lambdaFunctionNodeDecorationProvider.test.ts new file mode 100644 index 00000000000..19a2662815f --- /dev/null +++ b/packages/core/src/test/lambda/explorer/lambdaFunctionNodeDecorationProvider.test.ts @@ -0,0 +1,147 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import * as vscode from 'vscode' +import * as sinon from 'sinon' +import * as path from 'path' +import { LambdaFunctionNodeDecorationProvider } from '../../../lambda/explorer/lambdaFunctionNodeDecorationProvider' +import * as utils from '../../../lambda/utils' +import { fs } from '../../../shared/fs/fs' + +describe('LambdaFunctionNodeDecorationProvider', function () { + let provider: LambdaFunctionNodeDecorationProvider + let getFunctionInfoStub: sinon.SinonStub + let fsStatStub: sinon.SinonStub + let fsReaddirStub: sinon.SinonStub + + const filepath = path.join(utils.getTempLocation('test-function', 'us-east-1'), 'index.js') + const functionUri = vscode.Uri.parse('lambda:us-east-1/test-function') + const fileUri = vscode.Uri.file(filepath) + + beforeEach(function () { + provider = LambdaFunctionNodeDecorationProvider.getInstance() + getFunctionInfoStub = sinon.stub(utils, 'getFunctionInfo') + fsStatStub = sinon.stub(fs, 'stat') + fsReaddirStub = sinon.stub(fs, 'readdir') + }) + + afterEach(function () { + sinon.restore() + }) + + describe('provideFileDecoration', function () { + it('returns decoration for lambda URI with undeployed changes', async function () { + getFunctionInfoStub.resolves(true) + + const decoration = await provider.provideFileDecoration(functionUri) + + assert.ok(decoration) + assert.strictEqual(decoration.badge, 'M') + assert.strictEqual(decoration.tooltip, 'This function has undeployed changes') + assert.strictEqual(decoration.propagate, false) + }) + + it('returns undefined for lambda URI without undeployed changes', async function () { + getFunctionInfoStub.resolves(false) + + const decoration = await provider.provideFileDecoration(functionUri) + + assert.strictEqual(decoration, undefined) + }) + + it('returns decoration for file URI with modifications after deployment', async function () { + const lastDeployed = 1 + const fileModified = 2 + + getFunctionInfoStub.resolves({ lastDeployed, undeployed: true }) + fsStatStub.resolves({ mtime: fileModified }) + + const decoration = await provider.provideFileDecoration(fileUri) + + assert.ok(decoration) + assert.strictEqual(decoration.badge, 'M') + assert.strictEqual(decoration.tooltip, 'This function has undeployed changes') + assert.strictEqual(decoration.propagate, true) + }) + + it('returns undefined for file URI without modifications after deployment', async function () { + const lastDeployed = 2 + const fileModified = 1 + + getFunctionInfoStub.resolves({ lastDeployed, undeployed: true }) + fsStatStub.resolves({ mtime: fileModified }) + + const decoration = await provider.provideFileDecoration(fileUri) + + assert.strictEqual(decoration, undefined) + }) + + it('returns undefined for file URI when no deployment info exists', async function () { + getFunctionInfoStub.resolves(undefined) + + const decoration = await provider.provideFileDecoration(fileUri) + + assert.strictEqual(decoration, undefined) + }) + + it('returns undefined for file URI that does not match lambda pattern', async function () { + const uri = vscode.Uri.file(path.join('not', 'in', 'tmp')) + + const decoration = await provider.provideFileDecoration(uri) + + assert.strictEqual(decoration, undefined) + }) + + it('handles errors gracefully when checking file modification', async function () { + getFunctionInfoStub.resolves(0) + fsStatStub.rejects(new Error('File not found')) + + const decoration = await provider.provideFileDecoration(fileUri) + + assert.strictEqual(decoration, undefined) + }) + }) + + describe('addBadge', function () { + it('fires decoration change events for both URIs', async function () { + const fileUri = vscode.Uri.file(path.join('test', 'file.js')) + const functionUri = vscode.Uri.parse('lambda:us-east-1/test-function') + + let eventCount = 0 + const disposable = provider.onDidChangeFileDecorations(() => { + eventCount++ + }) + + await provider.addBadge(fileUri, functionUri) + + assert.strictEqual(eventCount, 2) + disposable.dispose() + }) + }) + + describe('getFilePaths', function () { + it('returns all file paths recursively', async function () { + const basePath = path.join('test', 'dir') + + // Mock first readdir call + fsReaddirStub.onFirstCall().resolves([ + ['file1.js', vscode.FileType.File], + ['subdir', vscode.FileType.Directory], + ]) + + // Mock second readdir call for subdirectory + fsReaddirStub.onSecondCall().resolves([['file2.js', vscode.FileType.File]]) + + // Access private method through any cast for testing + const paths = await (provider as any).getFilePaths(basePath) + + assert.ok(paths.includes(basePath)) + assert.ok(paths.includes(path.join('test', 'dir', 'file1.js'))) + assert.ok(paths.includes(path.join('test', 'dir', 'subdir'))) + assert.ok(paths.includes(path.join('test', 'dir', 'subdir', 'file2.js'))) + }) + }) +}) diff --git a/packages/core/src/test/lambda/explorer/lambdaNodes.test.ts b/packages/core/src/test/lambda/explorer/lambdaNodes.test.ts index ba494680911..2c94d28ba9b 100644 --- a/packages/core/src/test/lambda/explorer/lambdaNodes.test.ts +++ b/packages/core/src/test/lambda/explorer/lambdaNodes.test.ts @@ -4,8 +4,8 @@ */ import assert from 'assert' -import { LambdaFunctionNode } from '../../../lambda/explorer/lambdaFunctionNode' -import { contextValueLambdaFunction, LambdaNode } from '../../../lambda/explorer/lambdaNodes' +import { contextValueLambdaFunction, LambdaFunctionNode } from '../../../lambda/explorer/lambdaFunctionNode' +import { LambdaNode } from '../../../lambda/explorer/lambdaNodes' import { asyncGenerator } from '../../../shared/utilities/collectionUtils' import { assertNodeListOnlyHasErrorNode, diff --git a/packages/core/src/test/lambda/uriHandlers.test.ts b/packages/core/src/test/lambda/uriHandlers.test.ts new file mode 100644 index 00000000000..f3e8ae7c368 --- /dev/null +++ b/packages/core/src/test/lambda/uriHandlers.test.ts @@ -0,0 +1,36 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { SearchParams } from '../../shared/vscode/uriHandler' +import { parseOpenParams } from '../../lambda/uriHandlers' +import { globals } from '../../shared' + +describe('Lambda URI Handler', function () { + describe('load-function', function () { + it('registers for "/lambda/load-function"', function () { + assert.throws(() => globals.uriHandler.onPath('/lambda/load-function', () => {})) + }) + + it('parses parameters', function () { + let query = new SearchParams({ + functionName: 'example', + }) + assert.throws(() => parseOpenParams(query), /A region must be provided/) + query = new SearchParams({ + region: 'example', + }) + assert.throws(() => parseOpenParams(query), /A function name must be provided/) + + const valid = { + functionName: 'example', + region: 'example', + isCfn: 'false', + } + query = new SearchParams(valid) + assert.deepEqual(parseOpenParams(query), valid) + }) + }) +}) diff --git a/packages/core/src/test/lambda/utils.test.ts b/packages/core/src/test/lambda/utils.test.ts index b7c1edb0aa4..72f5d3e63e4 100644 --- a/packages/core/src/test/lambda/utils.test.ts +++ b/packages/core/src/test/lambda/utils.test.ts @@ -4,9 +4,30 @@ */ import assert from 'assert' -import { getLambdaDetails } from '../../lambda/utils' +import * as sinon from 'sinon' +import { + getLambdaDetails, + getTempLocation, + getTempRegionLocation, + getFunctionInfo, + setFunctionInfo, + compareCodeSha, + lambdaEdits, + getLambdaEditFromNameRegion, + getLambdaEditFromLocation, +} from '../../lambda/utils' +import { LambdaFunction } from '../../lambda/commands/uploadLambda' +import { DefaultLambdaClient } from '../../shared/clients/lambdaClient' +import { fs } from '../../shared/fs/fs' +import { tempDirPath } from '../../shared/filesystemUtilities' +import path from 'path' -describe('lambda utils', async function () { +describe('lambda utils', function () { + const mockLambda = { + name: 'test-function', + region: 'us-east-1', + configuration: { FunctionName: 'test-function' }, + } describe('getLambdaDetails', function () { it('returns valid filenames and function names', function () { const jsNonNestedParsedName = getLambdaDetails({ @@ -52,4 +73,162 @@ describe('lambda utils', async function () { assert.throws(() => getLambdaDetails({ Runtime: 'COBOL-60', Handler: 'asdf.asdf' })) }) }) + + describe('getTempLocation', function () { + it('returns correct temp location path', function () { + const result = getTempLocation('test-function', 'us-east-1') + const expected = path.join(tempDirPath, 'lambda', 'us-east-1', 'test-function') + assert.strictEqual(result, expected) + }) + }) + + describe('getTempRegionLocation', function () { + it('returns correct temp region path', function () { + const result = getTempRegionLocation('us-west-2') + const expected = path.join(tempDirPath, 'lambda', 'us-west-2') + assert.strictEqual(result, expected) + }) + }) + + describe('getFunctionInfo', function () { + afterEach(function () { + sinon.restore() + }) + + it('returns parsed data when file exists', async function () { + const mockData = { lastDeployed: 123456, undeployed: false, sha: 'test-sha' } + sinon.stub(fs, 'readFileText').resolves(JSON.stringify(mockData)) + + const result = await getFunctionInfo(mockLambda) + assert.deepStrictEqual(result, mockData) + }) + + it('returns specific field when requested', async function () { + const mockData = { lastDeployed: 123456, undeployed: false, sha: 'test-sha' } + sinon.stub(fs, 'readFileText').resolves(JSON.stringify(mockData)) + + const result = await getFunctionInfo(mockLambda, 'sha') + assert.strictEqual(result, 'test-sha') + }) + + it('returns empty object when file does not exist', async function () { + sinon.stub(fs, 'readFileText').rejects(new Error('File not found')) + + const result = await getFunctionInfo(mockLambda) + assert.deepStrictEqual(result, {}) + }) + }) + + describe('setFunctionInfo', function () { + let mockLambda: LambdaFunction + + beforeEach(function () { + mockLambda = { + name: 'test-function', + region: 'us-east-1', + configuration: { FunctionName: 'test-function' }, + } + }) + + afterEach(function () { + sinon.restore() + }) + + it('merges with existing data', async function () { + const existingData = { lastDeployed: 123456, undeployed: true, sha: 'old-sha' } + sinon.stub(fs, 'readFileText').resolves(JSON.stringify(existingData)) + const writeStub = sinon.stub(fs, 'writeFile').resolves() + sinon.stub(DefaultLambdaClient.prototype, 'getFunction').resolves({ + Configuration: { CodeSha256: 'new-sha' }, + } as any) + + await setFunctionInfo(mockLambda, { undeployed: false }) + + assert(writeStub.calledOnce) + const writtenData = JSON.parse(writeStub.firstCall.args[1] as string) + assert.strictEqual(writtenData.lastDeployed, 123456) + assert.strictEqual(writtenData.undeployed, false) + assert.strictEqual(writtenData.sha, 'new-sha') + }) + }) + + describe('compareCodeSha', function () { + let mockLambda: LambdaFunction + + beforeEach(function () { + mockLambda = { + name: 'test-function', + region: 'us-east-1', + configuration: { FunctionName: 'test-function' }, + } + }) + + afterEach(function () { + sinon.restore() + }) + + it('returns true when local and remote SHA match', async function () { + sinon.stub(fs, 'readFileText').resolves(JSON.stringify({ sha: 'same-sha' })) + sinon.stub(DefaultLambdaClient.prototype, 'getFunction').resolves({ + Configuration: { CodeSha256: 'same-sha' }, + } as any) + + const result = await compareCodeSha(mockLambda) + assert.strictEqual(result, true) + }) + + it('returns false when local and remote SHA differ', async function () { + sinon.stub(fs, 'readFileText').resolves(JSON.stringify({ sha: 'local-sha' })) + sinon.stub(DefaultLambdaClient.prototype, 'getFunction').resolves({ + Configuration: { CodeSha256: 'remote-sha' }, + } as any) + + const result = await compareCodeSha(mockLambda) + assert.strictEqual(result, false) + }) + }) + + describe('lambdaEdits array functions', function () { + beforeEach(function () { + lambdaEdits.length = 0 + lambdaEdits.push( + { + location: '/tmp/func1', + functionName: 'func1', + region: 'us-east-1', + }, + { + location: '/tmp/func2', + functionName: 'func2', + region: 'us-west-2', + } + ) + }) + + describe('getLambdaEditFromNameRegion', function () { + it('finds edit by name and region', function () { + const result = getLambdaEditFromNameRegion('func1', 'us-east-1') + assert.strictEqual(result?.functionName, 'func1') + assert.strictEqual(result?.region, 'us-east-1') + }) + + it('returns undefined when not found', function () { + const result = getLambdaEditFromNameRegion('nonexistent', 'us-east-1') + assert.strictEqual(result, undefined) + }) + }) + + describe('getLambdaEditFromLocation', function () { + it('finds edit by location', function () { + const result = getLambdaEditFromLocation('/tmp/func2') + assert.strictEqual(result?.functionName, 'func2') + assert.strictEqual(result?.location, '/tmp/func2') + }) + + it('returns undefined when not found', function () { + const result = getLambdaEditFromLocation('/tmp/nonexistent') + assert.strictEqual(result, undefined) + }) + }) + }) }) diff --git a/packages/toolkit/.changes/next-release/Feature-b2f13a50-0474-464c-b503-611edce7c356.json b/packages/toolkit/.changes/next-release/Feature-b2f13a50-0474-464c-b503-611edce7c356.json new file mode 100644 index 00000000000..d9d220cf51f --- /dev/null +++ b/packages/toolkit/.changes/next-release/Feature-b2f13a50-0474-464c-b503-611edce7c356.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "Lambda to SAM Transformation: AWS Toolkit Explorer now can convert existing Lambda functions into SAM (Serverless Application Model) projects. This conversion creates a project structure that's ready for local development and can be managed using Application Builder" +} diff --git a/packages/toolkit/.changes/next-release/Feature-fc398d68-b7e1-4f37-8746-867381f402c6.json b/packages/toolkit/.changes/next-release/Feature-fc398d68-b7e1-4f37-8746-867381f402c6.json new file mode 100644 index 00000000000..0e73c5e6c84 --- /dev/null +++ b/packages/toolkit/.changes/next-release/Feature-fc398d68-b7e1-4f37-8746-867381f402c6.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "Lambda Quick Edit: AWS Toolkit Explorer now offers a streamlined editing experience for Lambda functions. Download a function's code with double-click, make local modifications, and easily synchronize changes back to the cloud." +} diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index 09cf1440405..72386694921 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -2,7 +2,7 @@ "name": "aws-toolkit-vscode", "displayName": "AWS Toolkit", "description": "Including CodeCatalyst, Infrastructure Composer, and support for Lambda, S3, CloudWatch Logs, CloudFormation, and many other services.", - "version": "3.68.0-SNAPSHOT", + "version": "3.68.0-g67e4600", "extensionKind": [ "workspace" ], @@ -831,6 +831,10 @@ "command": "aws.downloadStateMachineDefinition", "when": "false" }, + { + "command": "aws.toolkit.lambda.convertToSam", + "when": "false" + }, { "command": "aws.ecr.createRepository", "when": "false" @@ -1689,6 +1693,16 @@ "when": "view =~ /^(aws.explorer|aws.appBuilder|aws.appBuilderForFileExplorer)$/ && viewItem =~ /^(awsRegionFunctionNode|awsRegionFunctionNodeDownloadable)$/ || viewItem == awsAppBuilderDeployedNode", "group": "0@2" }, + { + "command": "aws.lambda.openWorkspace", + "when": "view == aws.explorer && viewItem == awsRegionFunctionNodeDownloadable", + "group": "0@6" + }, + { + "command": "aws.toolkit.lambda.convertToSam", + "when": "view == aws.explorer && viewItem == awsRegionFunctionNodeDownloadable", + "group": "0@3" + }, { "command": "aws.uploadLambda", "when": "view =~ /^(aws.explorer|aws.appBuilder|aws.appBuilderForFileExplorer)$/ && viewItem =~ /^(awsRegionFunctionNode|awsRegionFunctionNodeDownloadable)$/ || viewItem == awsAppBuilderDeployedNode", @@ -2219,6 +2233,16 @@ "when": "view =~ /^(aws.explorer|aws.appBuilder|aws.appBuilderForFileExplorer)$/ && viewItem =~ /^(awsRegionFunctionNode|awsRegionFunctionNodeDownloadable|awsCloudFormationFunctionNode)$/", "group": "inline@2" }, + { + "command": "aws.quickDeployLambda", + "when": "view == aws.explorer && viewItem == awsRegionFunctionNodeDownloadable", + "group": "inline@3" + }, + { + "command": "aws.toolkit.lambda.convertToSam", + "when": "view == aws.explorer && viewItem == awsRegionFunctionNodeDownloadable", + "group": "inline@4" + }, { "command": "aws.docdb.createCluster", "when": "view == aws.explorer && viewItem == awsDocDBNode", @@ -2305,7 +2329,7 @@ }, { "command": "aws.appBuilder.tailLogs", - "when": "view =~ /^(aws.explorer|aws.appBuilder|aws.appBuilderForFileExplorer)$/ && viewItem =~ /^(awsRegionFunctionNode|awsRegionFunctionNodeDownloadable|awsCloudFormationFunctionNode)$/", + "when": "view =~ /^(aws.appBuilder|aws.appBuilderForFileExplorer)$/ && viewItem =~ /^(awsRegionFunctionNode|awsRegionFunctionNodeDownloadable|awsCloudFormationFunctionNode)$/", "group": "inline@3" } ], @@ -3082,6 +3106,21 @@ } } }, + { + "command": "aws.toolkit.lambda.convertToSam", + "title": "%AWS.command.lambda.convertToSam%", + "category": "%AWS.title%", + "enablement": "viewItem == awsRegionFunctionNodeDownloadable", + "cloud9": { + "cn": { + "category": "%AWS.title.cn%" + } + }, + "icon": { + "light": "resources/icons/aws/lambda/create-stack.svg", + "dark": "resources/icons/aws/lambda/create-stack-light.svg" + } + }, { "command": "aws.downloadLambda", "title": "%AWS.command.downloadLambda%", @@ -3093,6 +3132,17 @@ } } }, + { + "command": "aws.lambda.openWorkspace", + "title": "%AWS.command.openLambdaWorkspace%", + "category": "%AWS.title%", + "enablement": "viewItem == awsRegionFunctionNodeDownloadable", + "cloud9": { + "cn": { + "category": "%AWS.title.cn%" + } + } + }, { "command": "aws.uploadLambda", "title": "%AWS.command.uploadLambda%", @@ -3102,7 +3152,27 @@ "cn": { "category": "%AWS.title.cn%" } - } + }, + "icon": "$(cloud-upload)" + }, + { + "command": "aws.openLambdaFile", + "title": "%AWS.command.openLambdaFile%", + "category": "%AWS.title%", + "enablement": "isCloud9 || !aws.isWebExtHost", + "icon": "$(preview)" + }, + { + "command": "aws.quickDeployLambda", + "title": "%AWS.command.quickDeployLambda%", + "category": "%AWS.title%", + "enablement": "viewItem == awsRegionFunctionNodeDownloadable", + "cloud9": { + "cn": { + "category": "%AWS.title.cn%" + } + }, + "icon": "$(cloud-upload)" }, { "command": "aws.deleteLambda", @@ -4630,124 +4700,138 @@ "fontCharacter": "\\f1d0" } }, - "aws-lambda-function": { + "aws-lambda-create-stack": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d1" } }, - "aws-mynah-MynahIconBlack": { + "aws-lambda-create-stack-light": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d2" } }, - "aws-mynah-MynahIconWhite": { + "aws-lambda-function": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d3" } }, - "aws-mynah-logo": { + "aws-mynah-MynahIconBlack": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d4" } }, - "aws-redshift-cluster": { + "aws-mynah-MynahIconWhite": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d5" } }, - "aws-redshift-cluster-connected": { + "aws-mynah-logo": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d6" } }, - "aws-redshift-database": { + "aws-redshift-cluster": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d7" } }, - "aws-redshift-redshift-cluster-connected": { + "aws-redshift-cluster-connected": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d8" } }, - "aws-redshift-schema": { + "aws-redshift-database": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1d9" } }, - "aws-redshift-table": { + "aws-redshift-redshift-cluster-connected": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1da" } }, - "aws-s3-bucket": { + "aws-redshift-schema": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1db" } }, - "aws-s3-create-bucket": { + "aws-redshift-table": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1dc" } }, - "aws-sagemaker-code-editor": { + "aws-s3-bucket": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1dd" } }, - "aws-sagemaker-jupyter-lab": { + "aws-s3-create-bucket": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1de" } }, - "aws-schemas-registry": { + "aws-sagemaker-code-editor": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1df" } }, - "aws-schemas-schema": { + "aws-sagemaker-jupyter-lab": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1e0" } }, - "aws-stepfunctions-preview": { + "aws-schemas-registry": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1e1" } + }, + "aws-schemas-schema": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e2" + } + }, + "aws-stepfunctions-preview": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e3" + } } }, "notebooks": [ From 857d78156fcfe514fab413e7c9f9e4712cddd74a Mon Sep 17 00:00:00 2001 From: aws-toolkit-automation <> Date: Wed, 2 Jul 2025 19:27:38 +0000 Subject: [PATCH 10/12] Release 1.81.0 --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 4f3095262b6..56df83b177f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "prettier": "^3.3.3", "prettier-plugin-sh": "^0.14.0", "pretty-quick": "^4.0.0", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "typescript": "^5.0.4", "webpack": "^5.95.0", "webpack-cli": "^5.1.4", From c5d531e01c7392bba728c28859f1d985c84b385d Mon Sep 17 00:00:00 2001 From: aws-toolkit-automation <> Date: Thu, 3 Jul 2025 00:17:27 +0000 Subject: [PATCH 11/12] Update version to snapshot version: 1.82.0-SNAPSHOT --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 56df83b177f..4f3095262b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "prettier": "^3.3.3", "prettier-plugin-sh": "^0.14.0", "pretty-quick": "^4.0.0", - "ts-node": "^10.9.2", + "ts-node": "^10.9.1", "typescript": "^5.0.4", "webpack": "^5.95.0", "webpack-cli": "^5.1.4", From 2dd177e10431010e93b4c29cf5a9fcbbda166bc2 Mon Sep 17 00:00:00 2001 From: Laxman Reddy <141967714+laileni-aws@users.noreply.github.com> Date: Thu, 3 Jul 2025 09:24:49 -0700 Subject: [PATCH 12/12] Merging Master to feature/toolkit (#33) * Release 1.81.0 * Update version to snapshot version: 1.82.0-SNAPSHOT * feat(lambda): Merging Feature/lambda console2 ide to staging branch. (#7601) ## Notes: - Releasing https://github.com/aws/aws-toolkit-vscode-staging/pull/2144 --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. Co-authored-by: aws-toolkit-automation <43144436+aws-toolkit-automation@users.noreply.github.com> Co-authored-by: Roger Zhang Co-authored-by: Reed Hamilton <49345456+rhamilt@users.noreply.github.com> Co-authored-by: Jacob Chung --------- Co-authored-by: aws-toolkit-automation <> Co-authored-by: aws-toolkit-automation <43144436+aws-toolkit-automation@users.noreply.github.com> Co-authored-by: Roger Zhang Co-authored-by: Reed Hamilton <49345456+rhamilt@users.noreply.github.com> Co-authored-by: Jacob Chung